/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
2376.3.8 by Robert Collins
Overhaul the SmartTCPServer connect-thread logic to synchronise on startup and shutdown and notify the server if it is in accept.
1
# Copyright (C) 2006, 2007 Canonical Ltd
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
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.smart import (
2018.5.26 by Andrew Bennetts
Extract a simple SmartClient class from RemoteTransport, and a hack to avoid VFS operations when probing for a bzrdir over a smart transport.
34
        client,
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
35
        medium,
36
        protocol,
2018.5.16 by Andrew Bennetts
Move SmartServerResponse to smart/request.py, untangling more import dependencies.
37
        request,
2432.4.3 by Robert Collins
Refactor the HPSS Response code to take SmartServerResponse rather than args and body.
38
        request as _mod_request,
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
39
        server,
40
        vfs,
41
)
2004.1.28 by v.ladeuil+lp at free
Merge bzr.dev. Including http modifications by "smart" related code
42
from bzrlib.tests.HTTPTestUtil import (
43
        HTTPServerWithSmarts,
44
        SmartRequestHandler,
45
        )
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
46
from bzrlib.tests.test_smart import TestCaseWithSmartMedium
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
47
from bzrlib.transport import (
48
        get_transport,
49
        local,
50
        memory,
2400.1.1 by Andrew Bennetts
Rename bzrlib/transport/smart.py to bzrlib/transport/remote.py.
51
        remote,
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
52
        )
2004.1.28 by v.ladeuil+lp at free
Merge bzr.dev. Including http modifications by "smart" related code
53
from bzrlib.transport.http import SmartClientHTTPMediumRequest
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
54
55
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
56
class StringIOSSHVendor(object):
57
    """A SSH vendor that uses StringIO to buffer writes and answer reads."""
58
59
    def __init__(self, read_from, write_to):
60
        self.read_from = read_from
61
        self.write_to = write_to
62
        self.calls = []
63
64
    def connect_ssh(self, username, password, host, port, command):
65
        self.calls.append(('connect_ssh', username, password, host, port,
66
            command))
67
        return StringIOSSHConnection(self)
68
69
70
class StringIOSSHConnection(object):
71
    """A SSH connection that uses StringIO to buffer writes and answer reads."""
72
73
    def __init__(self, vendor):
74
        self.vendor = vendor
75
    
76
    def close(self):
77
        self.vendor.calls.append(('close', ))
78
        
79
    def get_filelike_channels(self):
80
        return self.vendor.read_from, self.vendor.write_to
81
82
83
84
class SmartClientMediumTests(tests.TestCase):
85
    """Tests for SmartClientMedium.
86
87
    We should create a test scenario for this: we need a server module that
88
    construct the test-servers (like make_loopsocket_and_medium), and the list
89
    of SmartClientMedium classes to test.
90
    """
91
92
    def make_loopsocket_and_medium(self):
93
        """Create a loopback socket for testing, and a medium aimed at it."""
94
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
95
        sock.bind(('127.0.0.1', 0))
96
        sock.listen(1)
97
        port = sock.getsockname()[1]
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
98
        client_medium = medium.SmartTCPClientMedium('127.0.0.1', port)
99
        return sock, client_medium
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
100
101
    def receive_bytes_on_server(self, sock, bytes):
102
        """Accept a connection on sock and read 3 bytes.
103
104
        The bytes are appended to the list bytes.
105
106
        :return: a Thread which is running to do the accept and recv.
107
        """
108
        def _receive_bytes_on_server():
109
            connection, address = sock.accept()
2091.1.1 by Martin Pool
Avoid MSG_WAITALL as it doesn't work on Windows
110
            bytes.append(osutils.recv_all(connection, 3))
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
111
            connection.close()
112
        t = threading.Thread(target=_receive_bytes_on_server)
113
        t.start()
114
        return t
115
    
116
    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.
117
        # make a new instance of the common base for Stream-like Mediums.
118
        # this just ensures that the constructor stays parameter-free which
119
        # is important for reuse : some subclasses will dynamically connect,
120
        # others are always on, etc.
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
121
        client_medium = medium.SmartClientStreamMedium()
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
122
123
    def test_construct_smart_client_medium(self):
124
        # the base client medium takes no parameters
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
125
        client_medium = medium.SmartClientMedium()
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
126
    
127
    def test_construct_smart_simple_pipes_client_medium(self):
128
        # the SimplePipes client medium takes two pipes:
129
        # readable pipe, writeable pipe.
130
        # Constructing one should just save these and do nothing.
131
        # We test this by passing in None.
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
132
        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.
133
        
134
    def test_simple_pipes_client_request_type(self):
135
        # SimplePipesClient should use SmartClientStreamMediumRequest's.
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
136
        client_medium = medium.SmartSimplePipesClientMedium(None, None)
137
        request = client_medium.get_request()
138
        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.
139
140
    def test_simple_pipes_client_get_concurrent_requests(self):
141
        # the simple_pipes client does not support pipelined requests:
142
        # but it does support serial requests: we construct one after 
143
        # another is finished. This is a smoke test testing the integration
144
        # of the SmartClientStreamMediumRequest and the SmartClientStreamMedium
145
        # classes - as the sibling classes share this logic, they do not have
146
        # explicit tests for this.
147
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
148
        client_medium = medium.SmartSimplePipesClientMedium(None, output)
149
        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.
150
        request.finished_writing()
151
        request.finished_reading()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
152
        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.
153
        request2.finished_writing()
154
        request2.finished_reading()
155
156
    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.
157
        # accept_bytes writes to the writeable pipe.
158
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
159
        client_medium = medium.SmartSimplePipesClientMedium(None, output)
160
        client_medium._accept_bytes('abc')
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
161
        self.assertEqual('abc', output.getvalue())
162
    
163
    def test_simple_pipes_client_disconnect_does_nothing(self):
164
        # calling disconnect does nothing.
165
        input = StringIO()
166
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
167
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
168
        # send some bytes to ensure disconnecting after activity still does not
169
        # close.
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
170
        client_medium._accept_bytes('abc')
171
        client_medium.disconnect()
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
172
        self.assertFalse(input.closed)
173
        self.assertFalse(output.closed)
174
175
    def test_simple_pipes_client_accept_bytes_after_disconnect(self):
176
        # calling disconnect on the client does not alter the pipe that
177
        # accept_bytes writes to.
178
        input = StringIO()
179
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
180
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
181
        client_medium._accept_bytes('abc')
182
        client_medium.disconnect()
183
        client_medium._accept_bytes('abc')
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
184
        self.assertFalse(input.closed)
185
        self.assertFalse(output.closed)
186
        self.assertEqual('abcabc', output.getvalue())
187
    
188
    def test_simple_pipes_client_ignores_disconnect_when_not_connected(self):
189
        # Doing a disconnect on a new (and thus unconnected) SimplePipes medium
190
        # does nothing.
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
191
        client_medium = medium.SmartSimplePipesClientMedium(None, None)
192
        client_medium.disconnect()
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
193
194
    def test_simple_pipes_client_can_always_read(self):
195
        # SmartSimplePipesClientMedium is never disconnected, so read_bytes
196
        # always tries to read from the underlying pipe.
197
        input = StringIO('abcdef')
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
198
        client_medium = medium.SmartSimplePipesClientMedium(input, None)
199
        self.assertEqual('abc', client_medium.read_bytes(3))
200
        client_medium.disconnect()
201
        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.
202
        
203
    def test_simple_pipes_client_supports__flush(self):
204
        # invoking _flush on a SimplePipesClient should flush the output 
205
        # pipe. We test this by creating an output pipe that records
206
        # flush calls made to it.
207
        from StringIO import StringIO # get regular StringIO
208
        input = StringIO()
209
        output = StringIO()
210
        flush_calls = []
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
211
        def logging_flush(): flush_calls.append('flush')
212
        output.flush = logging_flush
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
213
        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.
214
        # this call is here to ensure we only flush once, not on every
215
        # _accept_bytes call.
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
216
        client_medium._accept_bytes('abc')
217
        client_medium._flush()
218
        client_medium.disconnect()
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
219
        self.assertEqual(['flush'], flush_calls)
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
220
221
    def test_construct_smart_ssh_client_medium(self):
222
        # the SSH client medium takes:
223
        # host, port, username, password, vendor
224
        # Constructing one should just save these and do nothing.
225
        # we test this by creating a empty bound socket and constructing
226
        # a medium.
227
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
228
        sock.bind(('127.0.0.1', 0))
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
229
        unopened_port = sock.getsockname()[1]
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
230
        # having vendor be invalid means that if it tries to connect via the
231
        # vendor it will blow up.
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
232
        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.
233
            username=None, password=None, vendor="not a vendor")
234
        sock.close()
235
236
    def test_ssh_client_connects_on_first_use(self):
237
        # The only thing that initiates a connection from the medium is giving
238
        # it bytes.
239
        output = StringIO()
240
        vendor = StringIOSSHVendor(StringIO(), output)
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
241
        client_medium = medium.SmartSSHClientMedium(
242
            'a hostname', 'a port', 'a username', 'a password', vendor)
243
        client_medium._accept_bytes('abc')
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
244
        self.assertEqual('abc', output.getvalue())
245
        self.assertEqual([('connect_ssh', 'a username', 'a password',
246
            'a hostname', 'a port',
247
            ['bzr', 'serve', '--inet', '--directory=/', '--allow-writes'])],
248
            vendor.calls)
249
    
250
    def test_ssh_client_changes_command_when_BZR_REMOTE_PATH_is_set(self):
251
        # The only thing that initiates a connection from the medium is giving
252
        # it bytes.
253
        output = StringIO()
254
        vendor = StringIOSSHVendor(StringIO(), output)
255
        orig_bzr_remote_path = os.environ.get('BZR_REMOTE_PATH')
256
        def cleanup_environ():
257
            osutils.set_or_unset_env('BZR_REMOTE_PATH', orig_bzr_remote_path)
258
        self.addCleanup(cleanup_environ)
259
        os.environ['BZR_REMOTE_PATH'] = 'fugly'
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
260
        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.
261
            'a password', vendor)
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
262
        client_medium._accept_bytes('abc')
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
263
        self.assertEqual('abc', output.getvalue())
264
        self.assertEqual([('connect_ssh', 'a username', 'a password',
265
            'a hostname', 'a port',
266
            ['fugly', 'serve', '--inet', '--directory=/', '--allow-writes'])],
267
            vendor.calls)
268
    
269
    def test_ssh_client_disconnect_does_so(self):
270
        # calling disconnect should disconnect both the read_from and write_to
271
        # file-like object it from the ssh connection.
272
        input = StringIO()
273
        output = StringIO()
274
        vendor = StringIOSSHVendor(input, output)
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
275
        client_medium = medium.SmartSSHClientMedium('a hostname', vendor=vendor)
276
        client_medium._accept_bytes('abc')
277
        client_medium.disconnect()
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
278
        self.assertTrue(input.closed)
279
        self.assertTrue(output.closed)
280
        self.assertEqual([
281
            ('connect_ssh', None, None, 'a hostname', None,
282
            ['bzr', 'serve', '--inet', '--directory=/', '--allow-writes']),
283
            ('close', ),
284
            ],
285
            vendor.calls)
286
287
    def test_ssh_client_disconnect_allows_reconnection(self):
288
        # calling disconnect on the client terminates the connection, but should
289
        # not prevent additional connections occuring.
290
        # we test this by initiating a second connection after doing a
291
        # disconnect.
292
        input = StringIO()
293
        output = StringIO()
294
        vendor = StringIOSSHVendor(input, output)
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
295
        client_medium = medium.SmartSSHClientMedium('a hostname', vendor=vendor)
296
        client_medium._accept_bytes('abc')
297
        client_medium.disconnect()
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
298
        # the disconnect has closed output, so we need a new output for the
299
        # new connection to write to.
300
        input2 = StringIO()
301
        output2 = StringIO()
302
        vendor.read_from = input2
303
        vendor.write_to = output2
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
304
        client_medium._accept_bytes('abc')
305
        client_medium.disconnect()
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
306
        self.assertTrue(input.closed)
307
        self.assertTrue(output.closed)
308
        self.assertTrue(input2.closed)
309
        self.assertTrue(output2.closed)
310
        self.assertEqual([
311
            ('connect_ssh', None, None, 'a hostname', None,
312
            ['bzr', 'serve', '--inet', '--directory=/', '--allow-writes']),
313
            ('close', ),
314
            ('connect_ssh', None, None, 'a hostname', None,
315
            ['bzr', 'serve', '--inet', '--directory=/', '--allow-writes']),
316
            ('close', ),
317
            ],
318
            vendor.calls)
319
    
320
    def test_ssh_client_ignores_disconnect_when_not_connected(self):
321
        # 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.
322
        # 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.
323
        client_medium = medium.SmartSSHClientMedium(None)
324
        client_medium.disconnect()
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
325
326
    def test_ssh_client_raises_on_read_when_not_connected(self):
327
        # 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.
328
        # MediumNotConnected.
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
329
        client_medium = medium.SmartSSHClientMedium(None)
330
        self.assertRaises(errors.MediumNotConnected, client_medium.read_bytes, 0)
331
        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.
332
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
333
    def test_ssh_client_supports__flush(self):
334
        # invoking _flush on a SSHClientMedium should flush the output 
335
        # pipe. We test this by creating an output pipe that records
336
        # flush calls made to it.
337
        from StringIO import StringIO # get regular StringIO
338
        input = StringIO()
339
        output = StringIO()
340
        flush_calls = []
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
341
        def logging_flush(): flush_calls.append('flush')
342
        output.flush = logging_flush
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
343
        vendor = StringIOSSHVendor(input, output)
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
344
        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.
345
        # this call is here to ensure we only flush once, not on every
346
        # _accept_bytes call.
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
347
        client_medium._accept_bytes('abc')
348
        client_medium._flush()
349
        client_medium.disconnect()
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
350
        self.assertEqual(['flush'], flush_calls)
351
        
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
352
    def test_construct_smart_tcp_client_medium(self):
353
        # the TCP client medium takes a host and a port.  Constructing it won't
354
        # connect to anything.
355
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
356
        sock.bind(('127.0.0.1', 0))
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
357
        unopened_port = sock.getsockname()[1]
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
358
        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.
359
        sock.close()
360
361
    def test_tcp_client_connects_on_first_use(self):
362
        # The only thing that initiates a connection from the medium is giving
363
        # it bytes.
364
        sock, medium = self.make_loopsocket_and_medium()
365
        bytes = []
366
        t = self.receive_bytes_on_server(sock, bytes)
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
367
        medium.accept_bytes('abc')
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
368
        t.join()
369
        sock.close()
370
        self.assertEqual(['abc'], bytes)
371
    
372
    def test_tcp_client_disconnect_does_so(self):
373
        # calling disconnect on the client terminates the connection.
374
        # 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.
375
        # 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.
376
        sock, medium = self.make_loopsocket_and_medium()
377
        bytes = []
378
        t = self.receive_bytes_on_server(sock, bytes)
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
379
        medium.accept_bytes('ab')
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
380
        medium.disconnect()
381
        t.join()
382
        sock.close()
383
        self.assertEqual(['ab'], bytes)
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
384
        # 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.
385
        # really did disconnect.
386
        medium.disconnect()
387
    
388
    def test_tcp_client_ignores_disconnect_when_not_connected(self):
389
        # 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.
390
        # 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.
391
        client_medium = medium.SmartTCPClientMedium(None, None)
392
        client_medium.disconnect()
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
393
394
    def test_tcp_client_raises_on_read_when_not_connected(self):
395
        # 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.
396
        # MediumNotConnected.
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
397
        client_medium = medium.SmartTCPClientMedium(None, None)
398
        self.assertRaises(errors.MediumNotConnected, client_medium.read_bytes, 0)
399
        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.
400
401
    def test_tcp_client_supports__flush(self):
402
        # invoking _flush on a TCPClientMedium should do something useful.
403
        # RBC 20060922 not sure how to test/tell in this case.
404
        sock, medium = self.make_loopsocket_and_medium()
405
        bytes = []
406
        t = self.receive_bytes_on_server(sock, bytes)
407
        # try with nothing buffered
408
        medium._flush()
409
        medium._accept_bytes('ab')
410
        # and with something sent.
411
        medium._flush()
412
        medium.disconnect()
413
        t.join()
414
        sock.close()
415
        self.assertEqual(['ab'], bytes)
416
        # now disconnect again : this should not do anything, if disconnection
417
        # really did disconnect.
418
        medium.disconnect()
419
420
421
class TestSmartClientStreamMediumRequest(tests.TestCase):
422
    """Tests the for SmartClientStreamMediumRequest.
423
    
424
    SmartClientStreamMediumRequest is a helper for the three stream based 
425
    mediums: TCP, SSH, SimplePipes, so we only test it once, and then test that
426
    those three mediums implement the interface it expects.
427
    """
428
429
    def test_accept_bytes_after_finished_writing_errors(self):
430
        # calling accept_bytes after calling finished_writing raises 
431
        # WritingCompleted to prevent bad assumptions on stream environments
432
        # breaking the needs of message-based environments.
433
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
434
        client_medium = medium.SmartSimplePipesClientMedium(None, output)
435
        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.
436
        request.finished_writing()
437
        self.assertRaises(errors.WritingCompleted, request.accept_bytes, None)
438
439
    def test_accept_bytes(self):
440
        # accept bytes should invoke _accept_bytes on the stream medium.
441
        # we test this by using the SimplePipes medium - the most trivial one
442
        # and checking that the pipes get the data.
443
        input = StringIO()
444
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
445
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
446
        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.
447
        request.accept_bytes('123')
448
        request.finished_writing()
449
        request.finished_reading()
450
        self.assertEqual('', input.getvalue())
451
        self.assertEqual('123', output.getvalue())
452
453
    def test_construct_sets_stream_request(self):
454
        # constructing a SmartClientStreamMediumRequest on a StreamMedium sets
455
        # the current request to the new SmartClientStreamMediumRequest
456
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
457
        client_medium = medium.SmartSimplePipesClientMedium(None, output)
458
        request = medium.SmartClientStreamMediumRequest(client_medium)
459
        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.
460
461
    def test_construct_while_another_request_active_throws(self):
462
        # constructing a SmartClientStreamMediumRequest on a StreamMedium with
463
        # a non-None _current_request raises TooManyConcurrentRequests.
464
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
465
        client_medium = medium.SmartSimplePipesClientMedium(None, output)
466
        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.
467
        self.assertRaises(errors.TooManyConcurrentRequests,
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
468
            medium.SmartClientStreamMediumRequest, client_medium)
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
469
470
    def test_finished_read_clears_current_request(self):
471
        # calling finished_reading clears the current request from the requests
472
        # medium
473
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
474
        client_medium = medium.SmartSimplePipesClientMedium(None, output)
475
        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.
476
        request.finished_writing()
477
        request.finished_reading()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
478
        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.
479
480
    def test_finished_read_before_finished_write_errors(self):
481
        # calling finished_reading before calling finished_writing triggers a
482
        # WritingNotComplete error.
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
483
        client_medium = medium.SmartSimplePipesClientMedium(None, None)
484
        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.
485
        self.assertRaises(errors.WritingNotComplete, request.finished_reading)
486
        
487
    def test_read_bytes(self):
488
        # read bytes should invoke _read_bytes on the stream medium.
489
        # we test this by using the SimplePipes medium - the most trivial one
490
        # and checking that the data is supplied. Its possible that a 
491
        # faulty implementation could poke at the pipe variables them selves,
492
        # but we trust that this will be caught as it will break the integration
493
        # smoke tests.
494
        input = StringIO('321')
495
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
496
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
497
        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.
498
        request.finished_writing()
499
        self.assertEqual('321', request.read_bytes(3))
500
        request.finished_reading()
501
        self.assertEqual('', input.read())
502
        self.assertEqual('', output.getvalue())
503
504
    def test_read_bytes_before_finished_write_errors(self):
505
        # calling read_bytes before calling finished_writing triggers a
506
        # WritingNotComplete error because the Smart protocol is designed to be
507
        # compatible with strict message based protocols like HTTP where the
508
        # request cannot be submitted until the writing has completed.
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
509
        client_medium = medium.SmartSimplePipesClientMedium(None, None)
510
        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.
511
        self.assertRaises(errors.WritingNotComplete, request.read_bytes, None)
512
513
    def test_read_bytes_after_finished_reading_errors(self):
514
        # calling read_bytes after calling finished_reading raises 
515
        # ReadingCompleted to prevent bad assumptions on stream environments
516
        # breaking the needs of message-based environments.
517
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
518
        client_medium = medium.SmartSimplePipesClientMedium(None, output)
519
        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.
520
        request.finished_writing()
521
        request.finished_reading()
522
        self.assertRaises(errors.ReadingCompleted, request.read_bytes, None)
523
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
524
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
525
class RemoteTransportTests(TestCaseWithSmartMedium):
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
526
527
    def test_plausible_url(self):
528
        self.assert_(self.get_url().startswith('bzr://'))
529
530
    def test_probe_transport(self):
531
        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)
532
        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
533
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
534
    def test_get_medium_from_transport(self):
535
        """Remote transport has a medium always, which it can return."""
536
        t = self.get_transport()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
537
        client_medium = t.get_smart_medium()
538
        self.assertIsInstance(client_medium, medium.SmartClientMedium)
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
539
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
540
2018.2.18 by Andrew Bennetts
Just close the pipe/socket if the medium catches an error, and add tests for medium exception handling.
541
class ErrorRaisingProtocol(object):
542
543
    def __init__(self, exception):
544
        self.exception = exception
545
546
    def next_read_size(self):
547
        raise self.exception
548
549
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
550
class SampleRequest(object):
551
    
552
    def __init__(self, expected_bytes):
553
        self.accepted_bytes = ''
2018.2.15 by Andrew Bennetts
Remove SmartServerRequestProtocolOne.finished_reading attribute, replace with next_read_size method.
554
        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.
555
        self.expected_bytes = expected_bytes
556
        self.excess_buffer = ''
557
558
    def accept_bytes(self, bytes):
559
        self.accepted_bytes += bytes
560
        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.
561
            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.
562
            self.excess_buffer = self.accepted_bytes[len(self.expected_bytes):]
563
2018.2.15 by Andrew Bennetts
Remove SmartServerRequestProtocolOne.finished_reading attribute, replace with next_read_size method.
564
    def next_read_size(self):
565
        if self._finished_reading:
566
            return 0
567
        else:
568
            return 1
569
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
570
571
class TestSmartServerStreamMedium(tests.TestCase):
572
2018.5.24 by Andrew Bennetts
Setting NO_SMART_VFS in environment will disable VFS methods in the smart server. (Robert Collins, John Arbash Meinel, Andrew Bennetts)
573
    def setUp(self):
574
        super(TestSmartServerStreamMedium, self).setUp()
2402.1.2 by Andrew Bennetts
Deal with review comments.
575
        self._captureVar('BZR_NO_SMART_VFS', None)
2018.5.24 by Andrew Bennetts
Setting NO_SMART_VFS in environment will disable VFS methods in the smart server. (Robert Collins, John Arbash Meinel, Andrew Bennetts)
576
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
577
    def portable_socket_pair(self):
578
        """Return a pair of TCP sockets connected to each other.
579
        
580
        Unlike socket.socketpair, this should work on Windows.
581
        """
582
        listen_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
583
        listen_sock.bind(('127.0.0.1', 0))
584
        listen_sock.listen(1)
585
        client_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
586
        client_sock.connect(listen_sock.getsockname())
587
        server_sock, addr = listen_sock.accept()
588
        listen_sock.close()
589
        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
590
    
591
    def test_smart_query_version(self):
592
        """Feed a canned query version to a server"""
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
593
        # 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
594
        to_server = StringIO('hello\n')
595
        from_server = StringIO()
2018.2.27 by Andrew Bennetts
Merge from bzr.dev
596
        transport = local.LocalTransport(urlutils.local_path_to_url('/'))
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
597
        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.
598
            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
599
        smart_protocol = protocol.SmartServerRequestProtocolOne(transport,
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
600
                from_server.write)
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
601
        server._serve_one_request(smart_protocol)
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
602
        self.assertEqual('ok\0012\n',
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
603
                         from_server.getvalue())
604
2018.2.31 by Andrew Bennetts
Fix dispatching of smart server requests involving unicode paths.
605
    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
606
        transport = memory.MemoryTransport('memory:///')
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
607
        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
608
        to_server = StringIO('get\001./testfile\n')
609
        from_server = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
610
        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.
611
            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
612
        smart_protocol = protocol.SmartServerRequestProtocolOne(transport,
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
613
                from_server.write)
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
614
        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
615
        self.assertEqual('ok\n'
616
                         '17\n'
617
                         'contents\nof\nfile\n'
618
                         'done\n',
619
                         from_server.getvalue())
620
2018.2.31 by Andrew Bennetts
Fix dispatching of smart server requests involving unicode paths.
621
    def test_response_to_canned_get_of_utf8(self):
622
        # wire-to-wire, using the whole stack, with a UTF-8 filename.
623
        transport = memory.MemoryTransport('memory:///')
624
        utf8_filename = u'testfile\N{INTERROBANG}'.encode('utf-8')
625
        transport.put_bytes(utf8_filename, 'contents\nof\nfile\n')
626
        to_server = StringIO('get\001' + utf8_filename + '\n')
627
        from_server = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
628
        server = medium.SmartServerPipeStreamMedium(
2018.2.31 by Andrew Bennetts
Fix dispatching of smart server requests involving unicode paths.
629
            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
630
        smart_protocol = protocol.SmartServerRequestProtocolOne(transport,
2018.2.31 by Andrew Bennetts
Fix dispatching of smart server requests involving unicode paths.
631
                from_server.write)
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
632
        server._serve_one_request(smart_protocol)
2018.2.31 by Andrew Bennetts
Fix dispatching of smart server requests involving unicode paths.
633
        self.assertEqual('ok\n'
634
                         '17\n'
635
                         'contents\nof\nfile\n'
636
                         'done\n',
637
                         from_server.getvalue())
638
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
639
    def test_pipe_like_stream_with_bulk_data(self):
640
        sample_request_bytes = 'command\n9\nbulk datadone\n'
641
        to_server = StringIO(sample_request_bytes)
642
        from_server = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
643
        server = medium.SmartServerPipeStreamMedium(
644
            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.
645
        sample_protocol = SampleRequest(expected_bytes=sample_request_bytes)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
646
        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.
647
        self.assertEqual('', from_server.getvalue())
648
        self.assertEqual(sample_request_bytes, sample_protocol.accepted_bytes)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
649
        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.
650
651
    def test_socket_stream_with_bulk_data(self):
652
        sample_request_bytes = 'command\n9\nbulk datadone\n'
653
        server_sock, client_sock = self.portable_socket_pair()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
654
        server = medium.SmartServerSocketStreamMedium(
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
655
            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.
656
        sample_protocol = SampleRequest(expected_bytes=sample_request_bytes)
657
        client_sock.sendall(sample_request_bytes)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
658
        server._serve_one_request(sample_protocol)
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
659
        server_sock.close()
660
        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.
661
        self.assertEqual(sample_request_bytes, sample_protocol.accepted_bytes)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
662
        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.
663
664
    def test_pipe_like_stream_shutdown_detection(self):
665
        to_server = StringIO('')
666
        from_server = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
667
        server = medium.SmartServerPipeStreamMedium(to_server, from_server, None)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
668
        server._serve_one_request(SampleRequest('x'))
669
        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.
670
        
671
    def test_socket_stream_shutdown_detection(self):
672
        server_sock, client_sock = self.portable_socket_pair()
673
        client_sock.close()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
674
        server = medium.SmartServerSocketStreamMedium(
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
675
            server_sock, None)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
676
        server._serve_one_request(SampleRequest('x'))
677
        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.
678
        
679
    def test_pipe_like_stream_with_two_requests(self):
680
        # If two requests are read in one go, then two calls to
681
        # _serve_one_request should still process both of them as if they had
682
        # been received seperately.
683
        sample_request_bytes = 'command\n'
684
        to_server = StringIO(sample_request_bytes * 2)
685
        from_server = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
686
        server = medium.SmartServerPipeStreamMedium(
687
            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.
688
        first_protocol = SampleRequest(expected_bytes=sample_request_bytes)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
689
        server._serve_one_request(first_protocol)
2018.2.15 by Andrew Bennetts
Remove SmartServerRequestProtocolOne.finished_reading attribute, replace with next_read_size method.
690
        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.
691
        self.assertEqual('', from_server.getvalue())
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
692
        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.
693
        # Make a new protocol, call _serve_one_request with it to collect the
694
        # second request.
695
        second_protocol = SampleRequest(expected_bytes=sample_request_bytes)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
696
        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.
697
        self.assertEqual('', from_server.getvalue())
698
        self.assertEqual(sample_request_bytes, second_protocol.accepted_bytes)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
699
        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.
700
        
701
    def test_socket_stream_with_two_requests(self):
702
        # If two requests are read in one go, then two calls to
703
        # _serve_one_request should still process both of them as if they had
704
        # been received seperately.
705
        sample_request_bytes = 'command\n'
706
        server_sock, client_sock = self.portable_socket_pair()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
707
        server = medium.SmartServerSocketStreamMedium(
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
708
            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.
709
        first_protocol = SampleRequest(expected_bytes=sample_request_bytes)
710
        # Put two whole requests on the wire.
711
        client_sock.sendall(sample_request_bytes * 2)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
712
        server._serve_one_request(first_protocol)
2018.2.15 by Andrew Bennetts
Remove SmartServerRequestProtocolOne.finished_reading attribute, replace with next_read_size method.
713
        self.assertEqual(0, first_protocol.next_read_size())
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
714
        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.
715
        # Make a new protocol, call _serve_one_request with it to collect the
716
        # second request.
717
        second_protocol = SampleRequest(expected_bytes=sample_request_bytes)
718
        stream_still_open = server._serve_one_request(second_protocol)
719
        self.assertEqual(sample_request_bytes, second_protocol.accepted_bytes)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
720
        self.assertFalse(server.finished)
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
721
        server_sock.close()
722
        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.
723
724
    def test_pipe_like_stream_error_handling(self):
725
        # Use plain python StringIO so we can monkey-patch the close method to
726
        # not discard the contents.
727
        from StringIO import StringIO
728
        to_server = StringIO('')
729
        from_server = StringIO()
730
        self.closed = False
731
        def close():
732
            self.closed = True
733
        from_server.close = close
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
734
        server = medium.SmartServerPipeStreamMedium(
735
            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.
736
        fake_protocol = ErrorRaisingProtocol(Exception('boom'))
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
737
        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.
738
        self.assertEqual('', from_server.getvalue())
739
        self.assertTrue(self.closed)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
740
        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.
741
        
742
    def test_socket_stream_error_handling(self):
743
        server_sock, client_sock = self.portable_socket_pair()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
744
        server = medium.SmartServerSocketStreamMedium(
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
745
            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.
746
        fake_protocol = ErrorRaisingProtocol(Exception('boom'))
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
747
        server._serve_one_request(fake_protocol)
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
748
        # recv should not block, because the other end of the socket has been
749
        # closed.
750
        self.assertEqual('', client_sock.recv(1))
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
751
        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.
752
        
753
    def test_pipe_like_stream_keyboard_interrupt_handling(self):
754
        to_server = StringIO('')
755
        from_server = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
756
        server = medium.SmartServerPipeStreamMedium(
757
            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.
758
        fake_protocol = ErrorRaisingProtocol(KeyboardInterrupt('boom'))
759
        self.assertRaises(
760
            KeyboardInterrupt, server._serve_one_request, fake_protocol)
761
        self.assertEqual('', from_server.getvalue())
762
763
    def test_socket_stream_keyboard_interrupt_handling(self):
764
        server_sock, client_sock = self.portable_socket_pair()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
765
        server = medium.SmartServerSocketStreamMedium(
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
766
            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.
767
        fake_protocol = ErrorRaisingProtocol(KeyboardInterrupt('boom'))
768
        self.assertRaises(
769
            KeyboardInterrupt, server._serve_one_request, fake_protocol)
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
770
        server_sock.close()
771
        self.assertEqual('', client_sock.recv(1))
2432.2.2 by Andrew Bennetts
Smart server mediums now detect which protocol version a request is and dispatch accordingly.
772
773
    def build_protocol_pipe_like(self, bytes):
774
        to_server = StringIO(bytes)
775
        from_server = StringIO()
776
        server = medium.SmartServerPipeStreamMedium(
777
            to_server, from_server, None)
778
        return server._build_protocol()
779
780
    def build_protocol_socket(self, bytes):
781
        server_sock, client_sock = self.portable_socket_pair()
782
        server = medium.SmartServerSocketStreamMedium(
783
            server_sock, None)
784
        client_sock.sendall(bytes)
785
        client_sock.close()
786
        return server._build_protocol()
787
788
    def assertProtocolOne(self, server_protocol):
789
        # Use assertIs because assertIsInstance will wrongly pass
790
        # SmartServerRequestProtocolTwo (because it subclasses
791
        # SmartServerRequestProtocolOne).
792
        self.assertIs(
793
            type(server_protocol), protocol.SmartServerRequestProtocolOne)
794
795
    def assertProtocolTwo(self, server_protocol):
796
        self.assertIsInstance(
797
            server_protocol, protocol.SmartServerRequestProtocolTwo)
798
799
    def test_pipe_like_build_protocol_empty_bytes(self):
800
        # Any empty request (i.e. no bytes) is detected as protocol version one.
801
        server_protocol = self.build_protocol_pipe_like('')
802
        self.assertProtocolOne(server_protocol)
803
        
804
    def test_socket_like_build_protocol_empty_bytes(self):
805
        # Any empty request (i.e. no bytes) is detected as protocol version one.
806
        server_protocol = self.build_protocol_socket('')
807
        self.assertProtocolOne(server_protocol)
808
809
    def test_pipe_like_build_protocol_non_two(self):
2432.2.7 by Andrew Bennetts
Use less confusing version strings, and define REQUEST_VERSION_TWO/RESPONSE_VERSION_TWO constants for them.
810
        # A request that doesn't start with "bzr request 2\n" is version one.
811
        server_protocol = self.build_protocol_pipe_like('abc\n')
2432.2.2 by Andrew Bennetts
Smart server mediums now detect which protocol version a request is and dispatch accordingly.
812
        self.assertProtocolOne(server_protocol)
813
814
    def test_socket_build_protocol_non_two(self):
2432.2.7 by Andrew Bennetts
Use less confusing version strings, and define REQUEST_VERSION_TWO/RESPONSE_VERSION_TWO constants for them.
815
        # A request that doesn't start with "bzr request 2\n" is version one.
816
        server_protocol = self.build_protocol_socket('abc\n')
2432.2.2 by Andrew Bennetts
Smart server mediums now detect which protocol version a request is and dispatch accordingly.
817
        self.assertProtocolOne(server_protocol)
818
819
    def test_pipe_like_build_protocol_two(self):
2432.2.7 by Andrew Bennetts
Use less confusing version strings, and define REQUEST_VERSION_TWO/RESPONSE_VERSION_TWO constants for them.
820
        # A request that starts with "bzr request 2\n" is version two.
821
        server_protocol = self.build_protocol_pipe_like('bzr request 2\n')
2432.2.2 by Andrew Bennetts
Smart server mediums now detect which protocol version a request is and dispatch accordingly.
822
        self.assertProtocolTwo(server_protocol)
823
824
    def test_socket_build_protocol_two(self):
2432.2.7 by Andrew Bennetts
Use less confusing version strings, and define REQUEST_VERSION_TWO/RESPONSE_VERSION_TWO constants for them.
825
        # A request that starts with "bzr request 2\n" is version two.
826
        server_protocol = self.build_protocol_socket('bzr request 2\n')
2432.2.2 by Andrew Bennetts
Smart server mediums now detect which protocol version a request is and dispatch accordingly.
827
        self.assertProtocolTwo(server_protocol)
2018.2.15 by Andrew Bennetts
Remove SmartServerRequestProtocolOne.finished_reading attribute, replace with next_read_size method.
828
        
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
829
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
830
class TestSmartTCPServer(tests.TestCase):
831
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
832
    def test_get_error_unexpected(self):
833
        """Error reported by server with no specific representation"""
2402.1.2 by Andrew Bennetts
Deal with review comments.
834
        self._captureVar('BZR_NO_SMART_VFS', None)
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
835
        class FlakyTransport(object):
2376.3.3 by Robert Collins
Fix all smart_transport tests.
836
            base = 'a_url'
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.
837
            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
838
                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.
839
        smart_server = server.SmartTCPServer(backing_transport=FlakyTransport())
840
        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
841
        try:
2413.2.1 by Andrew Bennetts
Rename Smart.*Transport classes to RemoteTransport, RemoteTCPTransport, etc.
842
            transport = remote.RemoteTCPTransport(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
843
            try:
844
                transport.get('something')
845
            except errors.TransportError, e:
846
                self.assertContainsRe(str(e), 'some random exception')
847
            else:
848
                self.fail("get did not raise expected error")
2018.5.171 by Andrew Bennetts
Disconnect RemoteTransports in some tests to avoid tripping up test_strace with leftover threads from previous tests.
849
            transport.disconnect()
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
850
        finally:
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
851
            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
852
853
854
class SmartTCPTests(tests.TestCase):
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
855
    """Tests for connection/end to end behaviour using the TCP server.
856
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
857
    All of these tests are run with a server running on another thread serving
858
    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`.
859
860
    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
861
    """
862
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
863
    def setUpServer(self, readonly=False):
864
        """Setup the server.
865
866
        :param readonly: Create a readonly server.
867
        """
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
868
        self.backing_transport = memory.MemoryTransport()
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
869
        if readonly:
870
            self.real_backing_transport = self.backing_transport
871
            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.
872
        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
873
        self.server.start_background_thread()
2413.2.1 by Andrew Bennetts
Rename Smart.*Transport classes to RemoteTransport, RemoteTCPTransport, etc.
874
        self.transport = remote.RemoteTCPTransport(self.server.get_url())
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
875
        self.addCleanup(self.tearDownServer)
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
876
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
877
    def tearDownServer(self):
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
878
        if getattr(self, 'transport', None):
879
            self.transport.disconnect()
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
880
            del self.transport
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
881
        if getattr(self, 'server', None):
882
            self.server.stop_background_thread()
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
883
            del self.server
884
885
886
class TestServerSocketUsage(SmartTCPTests):
887
2376.3.8 by Robert Collins
Overhaul the SmartTCPServer connect-thread logic to synchronise on startup and shutdown and notify the server if it is in accept.
888
    def test_server_setup_teardown(self):
889
        """It should be safe to teardown the server with no requests."""
890
        self.setUpServer()
891
        server = self.server
2413.2.1 by Andrew Bennetts
Rename Smart.*Transport classes to RemoteTransport, RemoteTCPTransport, etc.
892
        transport = remote.RemoteTCPTransport(self.server.get_url())
2376.3.8 by Robert Collins
Overhaul the SmartTCPServer connect-thread logic to synchronise on startup and shutdown and notify the server if it is in accept.
893
        self.tearDownServer()
894
        self.assertRaises(errors.ConnectionError, transport.has, '.')
895
896
    def test_server_closes_listening_sock_on_shutdown_after_request(self):
2370.4.2 by Robert Collins
Review feedback.
897
        """The server should close its listening socket when it's stopped."""
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
898
        self.setUpServer()
2370.4.2 by Robert Collins
Review feedback.
899
        server = self.server
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
900
        self.transport.has('.')
901
        self.tearDownServer()
902
        # if the listening socket has closed, we should get a BADFD error
903
        # when connecting, rather than a hang.
2413.2.1 by Andrew Bennetts
Rename Smart.*Transport classes to RemoteTransport, RemoteTCPTransport, etc.
904
        transport = remote.RemoteTCPTransport(server.get_url())
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
905
        self.assertRaises(errors.ConnectionError, transport.has, '.')
906
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
907
908
class WritableEndToEndTests(SmartTCPTests):
909
    """Client to server tests that require a writable transport."""
910
911
    def setUp(self):
912
        super(WritableEndToEndTests, self).setUp()
913
        self.setUpServer()
914
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
915
    def test_start_tcp_server(self):
916
        url = self.server.get_url()
917
        self.assertContainsRe(url, r'^bzr://127\.0\.0\.1:[0-9]{2,}/')
918
919
    def test_smart_transport_has(self):
920
        """Checking for file existence over smart."""
2402.1.2 by Andrew Bennetts
Deal with review comments.
921
        self._captureVar('BZR_NO_SMART_VFS', None)
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
922
        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
923
        self.assertTrue(self.transport.has("foo"))
924
        self.assertFalse(self.transport.has("non-foo"))
925
926
    def test_smart_transport_get(self):
927
        """Read back a file over smart."""
2402.1.2 by Andrew Bennetts
Deal with review comments.
928
        self._captureVar('BZR_NO_SMART_VFS', None)
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
929
        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
930
        fp = self.transport.get("foo")
931
        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`.
932
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
933
    def test_get_error_enoent(self):
934
        """Error reported from server getting nonexistent file."""
1752.2.81 by Andrew Bennetts
Merge cleaned up underlying dependencies for remote bzrdir.
935
        # The path in a raised NoSuchFile exception should be the precise path
936
        # asked for by the client. This gives meaningful and unsurprising errors
937
        # for users.
2402.1.2 by Andrew Bennetts
Deal with review comments.
938
        self._captureVar('BZR_NO_SMART_VFS', None)
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
939
        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.
940
            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
941
        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.
942
            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
943
        else:
944
            self.fail("get did not raise expected error")
945
946
    def test_simple_clone_conn(self):
947
        """Test that cloning reuses the same connection."""
948
        # we create a real connection not a loopback one, but it will use the
949
        # 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__.
950
        conn2 = self.transport.clone('.')
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
951
        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
952
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)
953
    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
954
        self.assertEquals('/foo/bar',
955
                          self.transport._remote_path('foo/bar'))
956
957
    def test_clone_changes_base(self):
958
        """Cloning transport produces one with a new base location"""
959
        conn2 = self.transport.clone('subdir')
960
        self.assertEquals(self.transport.base + 'subdir/',
961
                          conn2.base)
962
963
    def test_open_dir(self):
964
        """Test changing directory"""
2402.1.2 by Andrew Bennetts
Deal with review comments.
965
        self._captureVar('BZR_NO_SMART_VFS', None)
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
966
        transport = self.transport
967
        self.backing_transport.mkdir('toffee')
968
        self.backing_transport.mkdir('toffee/apple')
969
        self.assertEquals('/toffee', transport._remote_path('toffee'))
1910.19.13 by Andrew Bennetts
Address various review comments.
970
        toffee_trans = transport.clone('toffee')
971
        # Check that each transport has only the contents of its directory
972
        # directly visible. If state was being held in the wrong object, it's
973
        # conceivable that cloning a transport would alter the state of the
974
        # cloned-from transport.
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
975
        self.assertTrue(transport.has('toffee'))
1910.19.13 by Andrew Bennetts
Address various review comments.
976
        self.assertFalse(toffee_trans.has('toffee'))
977
        self.assertFalse(transport.has('apple'))
978
        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
979
980
    def test_open_bzrdir(self):
981
        """Open an existing bzrdir over smart transport"""
982
        transport = self.transport
983
        t = self.backing_transport
984
        bzrdir.BzrDirFormat.get_default_format().initialize_on_transport(t)
985
        result_dir = bzrdir.BzrDir.open_containing_from_transport(transport)
986
987
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
988
class ReadOnlyEndToEndTests(SmartTCPTests):
989
    """Tests from the client to the server using a readonly backing transport."""
990
991
    def test_mkdir_error_readonly(self):
992
        """TransportNotPossible should be preserved from the backing transport."""
2402.1.2 by Andrew Bennetts
Deal with review comments.
993
        self._captureVar('BZR_NO_SMART_VFS', None)
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
994
        self.setUpServer(readonly=True)
995
        self.assertRaises(errors.TransportNotPossible, self.transport.mkdir,
996
            'foo')
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
997
998
999
class TestServerHooks(SmartTCPTests):
1000
1001
    def capture_server_call(self, backing_url, public_url):
1002
        """Record a server_started|stopped hook firing."""
1003
        self.hook_calls.append((backing_url, public_url))
1004
1005
    def test_server_started_hook(self):
1006
        """The server_started hook fires when the server is started."""
1007
        self.hook_calls = []
2400.1.7 by Andrew Bennetts
Merge from bzr.dev.
1008
        server.SmartTCPServer.hooks.install_hook('server_started',
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
1009
            self.capture_server_call)
1010
        self.setUpServer()
1011
        # at this point, the server will be starting a thread up.
1012
        # there is no indicator at the moment, so bodge it by doing a request.
1013
        self.transport.has('.')
1014
        self.assertEqual([(self.backing_transport.base, self.transport.base)],
1015
            self.hook_calls)
1016
1017
    def test_server_stopped_hook_simple(self):
1018
        """The server_stopped hook fires when the server is stopped."""
1019
        self.hook_calls = []
2400.1.7 by Andrew Bennetts
Merge from bzr.dev.
1020
        server.SmartTCPServer.hooks.install_hook('server_stopped',
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
1021
            self.capture_server_call)
1022
        self.setUpServer()
1023
        result = [(self.backing_transport.base, self.transport.base)]
2376.3.8 by Robert Collins
Overhaul the SmartTCPServer connect-thread logic to synchronise on startup and shutdown and notify the server if it is in accept.
1024
        # check the stopping message isn't emitted up front.
1025
        self.assertEqual([], self.hook_calls)
1026
        # nor after a single message
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
1027
        self.transport.has('.')
1028
        self.assertEqual([], self.hook_calls)
1029
        # clean up the server
1030
        self.tearDownServer()
2376.3.8 by Robert Collins
Overhaul the SmartTCPServer connect-thread logic to synchronise on startup and shutdown and notify the server if it is in accept.
1031
        # now it should have fired.
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
1032
        self.assertEqual(result, self.hook_calls)
1033
1034
# TODO: test that when the server suffers an exception that it calls the
1035
# server-stopped hook.
1036
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
1037
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
1038
class SmartServerCommandTests(tests.TestCaseWithTransport):
1039
    """Tests that call directly into the command objects, bypassing the network
1040
    and the request dispatching.
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1041
1042
    Note: these tests are rudimentary versions of the command object tests in
1043
    test_remote.py.
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
1044
    """
1045
        
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
1046
    def test_hello(self):
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
1047
        cmd = request.HelloRequest(None)
2018.5.24 by Andrew Bennetts
Setting NO_SMART_VFS in environment will disable VFS methods in the smart server. (Robert Collins, John Arbash Meinel, Andrew Bennetts)
1048
        response = cmd.execute()
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1049
        self.assertEqual(('ok', '2'), response.args)
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
1050
        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
1051
        
1052
    def test_get_bundle(self):
1053
        from bzrlib.bundle import serializer
1054
        wt = self.make_branch_and_tree('.')
1910.19.13 by Andrew Bennetts
Address various review comments.
1055
        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
1056
        wt.add('hello')
1910.19.13 by Andrew Bennetts
Address various review comments.
1057
        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
1058
        
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
1059
        cmd = request.GetBundleRequest(self.get_transport())
2018.5.24 by Andrew Bennetts
Setting NO_SMART_VFS in environment will disable VFS methods in the smart server. (Robert Collins, John Arbash Meinel, Andrew Bennetts)
1060
        response = cmd.execute('.', rev_id)
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
1061
        bundle = serializer.read_bundle(StringIO(response.body))
1062
        self.assertEqual((), response.args)
1063
1064
1065
class SmartServerRequestHandlerTests(tests.TestCaseWithTransport):
1066
    """Test that call directly into the handler logic, bypassing the network."""
1067
2018.5.24 by Andrew Bennetts
Setting NO_SMART_VFS in environment will disable VFS methods in the smart server. (Robert Collins, John Arbash Meinel, Andrew Bennetts)
1068
    def setUp(self):
1069
        super(SmartServerRequestHandlerTests, self).setUp()
2402.1.2 by Andrew Bennetts
Deal with review comments.
1070
        self._captureVar('BZR_NO_SMART_VFS', None)
2018.5.24 by Andrew Bennetts
Setting NO_SMART_VFS in environment will disable VFS methods in the smart server. (Robert Collins, John Arbash Meinel, Andrew Bennetts)
1071
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
1072
    def build_handler(self, transport):
1073
        """Returns a handler for the commands in protocol version one."""
2018.5.160 by Andrew Bennetts
Merge from bzr.dev.
1074
        return request.SmartServerRequestHandler(transport,
1075
                                                 request.request_handlers)
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
1076
1077
    def test_construct_request_handler(self):
1078
        """Constructing a request handler should be easy and set defaults."""
2402.1.1 by Andrew Bennetts
Use the Command pattern for handling smart server commands.
1079
        handler = request.SmartServerRequestHandler(None, None)
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
1080
        self.assertFalse(handler.finished_reading)
1081
1082
    def test_hello(self):
1083
        handler = self.build_handler(None)
1084
        handler.dispatch_command('hello', ())
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1085
        self.assertEqual(('ok', '2'), handler.response.args)
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
1086
        self.assertEqual(None, handler.response.body)
1087
        
2018.5.24 by Andrew Bennetts
Setting NO_SMART_VFS in environment will disable VFS methods in the smart server. (Robert Collins, John Arbash Meinel, Andrew Bennetts)
1088
    def test_disable_vfs_handler_classes_via_environment(self):
2018.5.140 by Andrew Bennetts
Merge from bzr.dev
1089
        # VFS handler classes will raise an error from "execute" if
1090
        # BZR_NO_SMART_VFS is set.
2018.5.24 by Andrew Bennetts
Setting NO_SMART_VFS in environment will disable VFS methods in the smart server. (Robert Collins, John Arbash Meinel, Andrew Bennetts)
1091
        handler = vfs.HasRequest(None)
1092
        # set environment variable after construction to make sure it's
1093
        # examined.
2018.5.140 by Andrew Bennetts
Merge from bzr.dev
1094
        # Note that we can safely clobber BZR_NO_SMART_VFS here, because setUp
1095
        # has called _captureVar, so it will be restored to the right state
2018.5.24 by Andrew Bennetts
Setting NO_SMART_VFS in environment will disable VFS methods in the smart server. (Robert Collins, John Arbash Meinel, Andrew Bennetts)
1096
        # afterwards.
2402.1.2 by Andrew Bennetts
Deal with review comments.
1097
        os.environ['BZR_NO_SMART_VFS'] = ''
2018.5.24 by Andrew Bennetts
Setting NO_SMART_VFS in environment will disable VFS methods in the smart server. (Robert Collins, John Arbash Meinel, Andrew Bennetts)
1098
        self.assertRaises(errors.DisabledMethod, handler.execute)
1099
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
1100
    def test_readonly_exception_becomes_transport_not_possible(self):
1101
        """The response for a read-only error is ('ReadOnlyError')."""
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
1102
        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`.
1103
        # send a mkdir for foo, with no explicit mode - should fail.
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1104
        handler.dispatch_command('mkdir', ('foo', ''))
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
1105
        # and the failure should be an explicit ReadOnlyError
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1106
        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`.
1107
        # XXX: TODO: test that other TransportNotPossible errors are
1108
        # presented as TransportNotPossible - not possible to do that
1109
        # until I figure out how to trigger that relatively cleanly via
1110
        # the api. RBC 20060918
1111
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1112
    def test_hello_has_finished_body_on_dispatch(self):
1113
        """The 'hello' command should set finished_reading."""
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
1114
        handler = self.build_handler(None)
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1115
        handler.dispatch_command('hello', ())
1116
        self.assertTrue(handler.finished_reading)
1117
        self.assertNotEqual(None, handler.response)
1118
1119
    def test_put_bytes_non_atomic(self):
1120
        """'put_...' should set finished_reading after reading the bytes."""
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
1121
        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
1122
        handler.dispatch_command('put_non_atomic', ('a-file', '', 'F', ''))
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1123
        self.assertFalse(handler.finished_reading)
1124
        handler.accept_body('1234')
1125
        self.assertFalse(handler.finished_reading)
1126
        handler.accept_body('5678')
1127
        handler.end_of_body()
1128
        self.assertTrue(handler.finished_reading)
1129
        self.assertEqual(('ok', ), handler.response.args)
1130
        self.assertEqual(None, handler.response.body)
1131
        
1132
    def test_readv_accept_body(self):
1133
        """'readv' should set finished_reading after reading offsets."""
1134
        self.build_tree(['a-file'])
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
1135
        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
1136
        handler.dispatch_command('readv', ('a-file', ))
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1137
        self.assertFalse(handler.finished_reading)
1138
        handler.accept_body('2,')
1139
        self.assertFalse(handler.finished_reading)
1140
        handler.accept_body('3')
1141
        handler.end_of_body()
1142
        self.assertTrue(handler.finished_reading)
1143
        self.assertEqual(('readv', ), handler.response.args)
1144
        # co - nte - nt of a-file is the file contents we are extracting from.
1145
        self.assertEqual('nte', handler.response.body)
1146
1147
    def test_readv_short_read_response_contents(self):
1148
        """'readv' when a short read occurs sets the response appropriately."""
1149
        self.build_tree(['a-file'])
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
1150
        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
1151
        handler.dispatch_command('readv', ('a-file', ))
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1152
        # read beyond the end of the file.
1153
        handler.accept_body('100,1')
1154
        handler.end_of_body()
1155
        self.assertTrue(handler.finished_reading)
1156
        self.assertEqual(('ShortReadvError', 'a-file', '100', '1', '0'),
1157
            handler.response.args)
1158
        self.assertEqual(None, handler.response.body)
1159
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.
1160
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)
1161
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.
1162
1163
    def test_registration(self):
1164
        t = get_transport('bzr+ssh://example.com/path')
2413.2.1 by Andrew Bennetts
Rename Smart.*Transport classes to RemoteTransport, RemoteTCPTransport, etc.
1165
        self.assertIsInstance(t, remote.RemoteSSHTransport)
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.
1166
        self.assertEqual('example.com', t._host)
1167
1168
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)
1169
class TestRemoteTransport(tests.TestCase):
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
1170
        
1171
    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)
1172
        # 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.
1173
        input = StringIO("ok\n3\nbardone\n")
1174
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
1175
        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)
1176
        transport = remote.RemoteTransport(
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
1177
            'bzr://localhost/', medium=client_medium)
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
1178
1179
        # 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.
1180
        # method is called.  No data should have been sent, or read.
1181
        self.assertEqual(0, input.tell())
1182
        self.assertEqual('', output.getvalue())
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
1183
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1184
        # Now call a method that should result in a single request : as the
1185
        # transport makes its own protocol instances, we check on the wire.
1186
        # XXX: TODO: give the transport a protocol factory, which can make
1187
        # an instrumented protocol for us.
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
1188
        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.
1189
        # only the needed data should have been sent/received.
1190
        self.assertEqual(13, input.tell())
1191
        self.assertEqual('get\x01/foo\n', output.getvalue())
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
1192
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
1193
    def test__translate_error_readonly(self):
1194
        """Sending a ReadOnlyError to _translate_error raises TransportNotPossible."""
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
1195
        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)
1196
        transport = remote.RemoteTransport(
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
1197
            'bzr://localhost/', medium=client_medium)
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
1198
        self.assertRaises(errors.TransportNotPossible,
1199
            transport._translate_error, ("ReadOnlyError", ))
1200
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
1201
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
1202
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.
1203
    """A smart server which is backed by memory and saves its write requests."""
1204
1205
    def __init__(self, write_output_list):
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
1206
        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.
1207
        self._write_output_list = write_output_list
1208
1209
2432.2.5 by Andrew Bennetts
Reduce duplication in test_smart_transport.
1210
class TestSmartProtocol(tests.TestCase):
1211
    """Base class for smart protocol tests.
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.
1212
1213
    Each test case gets a smart_server and smart_client created during setUp().
1214
1215
    It is planned that the client can be called with self.call_client() giving
1216
    it an expected server response, which will be fed into it when it tries to
1217
    read. Likewise, self.call_server will call a servers method with a canned
1218
    serialised client request. Output done by the client or server for these
1219
    calls will be captured to self.to_server and self.to_client. Each element
1220
    in the list is a write call from the client or server respectively.
2432.2.5 by Andrew Bennetts
Reduce duplication in test_smart_transport.
1221
1222
    Subclasses can override client_protocol_class and server_protocol_class.
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.
1223
    """
1224
2432.2.5 by Andrew Bennetts
Reduce duplication in test_smart_transport.
1225
    client_protocol_class = None
1226
    server_protocol_class = None
1227
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.
1228
    def setUp(self):
2432.2.5 by Andrew Bennetts
Reduce duplication in test_smart_transport.
1229
        super(TestSmartProtocol, self).setUp()
2018.5.26 by Andrew Bennetts
Extract a simple SmartClient class from RemoteTransport, and a hack to avoid VFS operations when probing for a bzrdir over a smart transport.
1230
        # XXX: self.server_to_client doesn't seem to be used.  If so,
1231
        # InstrumentedServerProtocol is redundant too.
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1232
        self.server_to_client = []
1233
        self.to_server = StringIO()
1234
        self.to_client = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
1235
        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.
1236
            self.to_server)
2432.2.5 by Andrew Bennetts
Reduce duplication in test_smart_transport.
1237
        self.client_protocol = self.client_protocol_class(self.client_medium)
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1238
        self.smart_server = InstrumentedServerProtocol(self.server_to_client)
2402.1.1 by Andrew Bennetts
Use the Command pattern for handling smart server commands.
1239
        self.smart_server_request = request.SmartServerRequestHandler(
2018.5.23 by Andrew Bennetts
Use a Registry for smart server command handlers.
1240
            None, request.request_handlers)
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.
1241
1242
    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.
1243
        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.
1244
        """Check that smart (de)serialises offsets as expected.
1245
        
1246
        We check both serialisation and deserialisation at the same time
1247
        to ensure that the round tripping cannot skew: both directions should
1248
        be as expected.
1249
        
1250
        :param expected_offsets: a readv offset list.
1251
        :param expected_seralised: an expected serial form of the offsets.
1252
        """
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.
1253
        # XXX: '_deserialise_offsets' should be a method of the
1254
        # SmartServerRequestProtocol in future.
2018.5.37 by Andrew Bennetts
Make sure all the request handlers in bzrlib/smart/vfs.py have consistent names.
1255
        readv_cmd = vfs.ReadvRequest(None)
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.
1256
        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.
1257
        self.assertEqual(expected_offsets, offsets)
1258
        serialised = client._serialise_offsets(offsets)
1259
        self.assertEqual(expected_serialised, serialised)
1260
2018.2.10 by Andrew Bennetts
Tidy up TODOs, further testing and fixes for SmartServerRequestProtocolOne, and remove a read_bytes(1) call.
1261
    def build_protocol_waiting_for_body(self):
2400.1.4 by Andrew Bennetts
Tidy up accidental changes.
1262
        out_stream = StringIO()
2432.2.5 by Andrew Bennetts
Reduce duplication in test_smart_transport.
1263
        smart_protocol = self.server_protocol_class(None, out_stream.write)
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1264
        smart_protocol.has_dispatched = True
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
1265
        smart_protocol.request = self.smart_server_request
2018.5.7 by Andrew Bennetts
Simplify dispatch_command.
1266
        class FakeCommand(object):
1267
            def do_body(cmd, body_bytes):
1268
                self.end_received = True
1269
                self.assertEqual('abcdefg', body_bytes)
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
1270
                return request.SuccessfulSmartServerResponse(('ok', ))
2018.5.12 by Andrew Bennetts
Rename SmartServerRequestHandler's command attribute to _command; it's private.
1271
        smart_protocol.request._command = FakeCommand()
2400.1.4 by Andrew Bennetts
Tidy up accidental changes.
1272
        # Call accept_bytes to make sure that internal state like _body_decoder
1273
        # is initialised.  This test should probably be given a clearer
1274
        # interface to work with that will not cause this inconsistency.
1275
        #   -- 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
1276
        smart_protocol.accept_bytes('')
1277
        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.
1278
2432.2.5 by Andrew Bennetts
Reduce duplication in test_smart_transport.
1279
    def assertServerToClientEncoding(self, expected_bytes, expected_tuple,
1280
            input_tuples):
1281
        """Assert that each input_tuple serialises as expected_bytes, and the
1282
        bytes deserialise as expected_tuple.
1283
        """
1284
        # check the encoding of the server for all input_tuples matches
1285
        # expected bytes
1286
        for input_tuple in input_tuples:
1287
            server_output = StringIO()
1288
            server_protocol = self.server_protocol_class(
1289
                None, server_output.write)
2432.4.4 by Robert Collins
Merge hpss-protocol2.
1290
            server_protocol._send_response(
1291
                _mod_request.SuccessfulSmartServerResponse(input_tuple))
2432.2.5 by Andrew Bennetts
Reduce duplication in test_smart_transport.
1292
            self.assertEqual(expected_bytes, server_output.getvalue())
1293
        # check the decoding of the client smart_protocol from expected_bytes:
1294
        input = StringIO(expected_bytes)
1295
        output = StringIO()
1296
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
1297
        request = client_medium.get_request()
1298
        smart_protocol = self.client_protocol_class(request)
1299
        smart_protocol.call('foo')
1300
        self.assertEqual(expected_tuple, smart_protocol.read_response_tuple())
1301
1302
1303
class TestSmartProtocolOne(TestSmartProtocol):
1304
    """Tests for the smart protocol version one."""
1305
1306
    client_protocol_class = protocol.SmartClientRequestProtocolOne
1307
    server_protocol_class = protocol.SmartServerRequestProtocolOne
1308
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1309
    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
1310
        smart_protocol = protocol.SmartServerRequestProtocolOne(None, None)
1311
        self.assertEqual('', smart_protocol.excess_buffer)
1312
        self.assertEqual('', smart_protocol.in_buffer)
1313
        self.assertFalse(smart_protocol.has_dispatched)
1314
        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.
1315
1316
    def test_construct_version_one_client_protocol(self):
1317
        # we can construct a client protocol from a client medium request
1318
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
1319
        client_medium = medium.SmartSimplePipesClientMedium(None, output)
1320
        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
1321
        client_protocol = protocol.SmartClientRequestProtocolOne(request)
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1322
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.
1323
    def test_server_offset_serialisation(self):
1324
        """The Smart protocol serialises offsets as a comma and \n string.
1325
1326
        We check a number of boundary cases are as expected: empty, one offset,
1327
        one with the order of reads not increasing (an out of order read), and
1328
        one that should coalesce.
1329
        """
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.
1330
        self.assertOffsetSerialisation([], '', self.client_protocol)
1331
        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.
1332
        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.
1333
            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.
1334
        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.
1335
            '1,2\n3,4\n100,200', self.client_protocol)
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1336
2018.3.2 by Andrew Bennetts
Ensure that a request's next_read_size() is 0 once an error response is sent.
1337
    def test_accept_bytes_of_bad_request_to_protocol(self):
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1338
        out_stream = StringIO()
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1339
        smart_protocol = protocol.SmartServerRequestProtocolOne(
1340
            None, out_stream.write)
1341
        smart_protocol.accept_bytes('abc')
1342
        self.assertEqual('abc', smart_protocol.in_buffer)
1343
        smart_protocol.accept_bytes('\n')
1344
        self.assertEqual(
1345
            "error\x01Generic bzr smart protocol error: bad request 'abc'\n",
1346
            out_stream.getvalue())
1347
        self.assertTrue(smart_protocol.has_dispatched)
1348
        self.assertEqual(0, smart_protocol.next_read_size())
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
1349
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1350
    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.
1351
        protocol = self.build_protocol_waiting_for_body()
2018.2.15 by Andrew Bennetts
Remove SmartServerRequestProtocolOne.finished_reading attribute, replace with next_read_size method.
1352
        self.assertEqual(6, protocol.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1353
        protocol.accept_bytes('7\nabc')
2018.2.15 by Andrew Bennetts
Remove SmartServerRequestProtocolOne.finished_reading attribute, replace with next_read_size method.
1354
        self.assertEqual(9, protocol.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1355
        protocol.accept_bytes('defgd')
1356
        protocol.accept_bytes('one\n')
2018.2.15 by Andrew Bennetts
Remove SmartServerRequestProtocolOne.finished_reading attribute, replace with next_read_size method.
1357
        self.assertEqual(0, protocol.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1358
        self.assertTrue(self.end_received)
1359
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
1360
    def test_accept_request_and_body_all_at_once(self):
2402.1.2 by Andrew Bennetts
Deal with review comments.
1361
        self._captureVar('BZR_NO_SMART_VFS', None)
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
1362
        mem_transport = memory.MemoryTransport()
1363
        mem_transport.put_bytes('foo', 'abcdefghij')
1364
        out_stream = StringIO()
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1365
        smart_protocol = protocol.SmartServerRequestProtocolOne(mem_transport,
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
1366
                out_stream.write)
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1367
        smart_protocol.accept_bytes('readv\x01foo\n3\n3,3done\n')
1368
        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.
1369
        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
1370
        self.assertEqual('', smart_protocol.excess_buffer)
1371
        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.
1372
2018.2.10 by Andrew Bennetts
Tidy up TODOs, further testing and fixes for SmartServerRequestProtocolOne, and remove a read_bytes(1) call.
1373
    def test_accept_excess_bytes_are_preserved(self):
1374
        out_stream = StringIO()
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1375
        smart_protocol = protocol.SmartServerRequestProtocolOne(
1376
            None, out_stream.write)
1377
        smart_protocol.accept_bytes('hello\nhello\n')
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1378
        self.assertEqual("ok\x012\n", out_stream.getvalue())
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1379
        self.assertEqual("hello\n", smart_protocol.excess_buffer)
1380
        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.
1381
1382
    def test_accept_excess_bytes_after_body(self):
1383
        protocol = self.build_protocol_waiting_for_body()
1384
        protocol.accept_bytes('7\nabcdefgdone\nX')
1385
        self.assertTrue(self.end_received)
1386
        self.assertEqual("X", protocol.excess_buffer)
1387
        self.assertEqual("", protocol.in_buffer)
1388
        protocol.accept_bytes('Y')
1389
        self.assertEqual("XY", protocol.excess_buffer)
1390
        self.assertEqual("", protocol.in_buffer)
1391
1392
    def test_accept_excess_bytes_after_dispatch(self):
1393
        out_stream = StringIO()
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1394
        smart_protocol = protocol.SmartServerRequestProtocolOne(
1395
            None, out_stream.write)
1396
        smart_protocol.accept_bytes('hello\n')
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1397
        self.assertEqual("ok\x012\n", out_stream.getvalue())
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1398
        smart_protocol.accept_bytes('hel')
1399
        self.assertEqual("hel", smart_protocol.excess_buffer)
1400
        smart_protocol.accept_bytes('lo\n')
1401
        self.assertEqual("hello\n", smart_protocol.excess_buffer)
1402
        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.
1403
2018.3.2 by Andrew Bennetts
Ensure that a request's next_read_size() is 0 once an error response is sent.
1404
    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
1405
        smart_protocol = protocol.SmartServerRequestProtocolOne(
1406
            None, lambda x: None)
1407
        self.assertEqual(1, smart_protocol.next_read_size())
2432.4.3 by Robert Collins
Refactor the HPSS Response code to take SmartServerResponse rather than args and body.
1408
        smart_protocol._send_response(
1409
            request.SuccessfulSmartServerResponse(('x',)))
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1410
        self.assertEqual(0, smart_protocol.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1411
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
1412
    def test__send_response_errors_with_base_response(self):
1413
        """Ensure that only the Successful/Failed subclasses are used."""
1414
        smart_protocol = protocol.SmartServerRequestProtocolOne(
1415
            None, lambda x: None)
1416
        self.assertRaises(AttributeError, smart_protocol._send_response,
1417
            request.SmartServerResponse(('x',)))
1418
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
1419
    def test_query_version(self):
1420
        """query_version on a SmartClientProtocolOne should return a number.
1421
        
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1422
        The protocol provides the query_version because the domain level clients
1423
        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.
1424
        """
1425
        # What we really want to test here is that SmartClientProtocolOne calls
1426
        # accept_bytes(tuple_based_encoding_of_hello) and reads and parses the
1427
        # 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.
1428
        # the error if the response is a non-understood version.
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1429
        input = StringIO('ok\x012\n')
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1430
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
1431
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
1432
        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
1433
        smart_protocol = protocol.SmartClientRequestProtocolOne(request)
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1434
        self.assertEqual(2, smart_protocol.query_version())
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1435
1436
    def test_client_call_empty_response(self):
1437
        # protocol.call() can get back an empty tuple as a response. This occurs
1438
        # when the parsed line is an empty line, and results in a tuple with
1439
        # one element - an empty string.
1440
        self.assertServerToClientEncoding('\n', ('', ), [(), ('', )])
1441
1442
    def test_client_call_three_element_response(self):
1443
        # protocol.call() can get back tuples of other lengths. A three element
1444
        # tuple should be unpacked as three strings.
1445
        self.assertServerToClientEncoding('a\x01b\x0134\n', ('a', 'b', '34'),
1446
            [('a', 'b', '34')])
1447
1448
    def test_client_call_with_body_bytes_uploads(self):
2018.5.131 by Andrew Bennetts
Be strict about unicode passed to transport.put_{bytes,file} and SmartClient.call_with_body_bytes, fixing part of TestLockableFiles_RemoteLockDir.test_read_write.
1449
        # protocol.call_with_body_bytes should length-prefix the bytes onto the
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1450
        # wire.
1451
        expected_bytes = "foo\n7\nabcdefgdone\n"
1452
        input = StringIO("\n")
1453
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
1454
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
1455
        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
1456
        smart_protocol = protocol.SmartClientRequestProtocolOne(request)
1457
        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.
1458
        self.assertEqual(expected_bytes, output.getvalue())
1459
1460
    def test_client_call_with_body_readv_array(self):
1461
        # protocol.call_with_upload should encode the readv array and then
1462
        # length-prefix the bytes onto the wire.
1463
        expected_bytes = "foo\n7\n1,2\n5,6done\n"
1464
        input = StringIO("\n")
1465
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
1466
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
1467
        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
1468
        smart_protocol = protocol.SmartClientRequestProtocolOne(request)
1469
        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.
1470
        self.assertEqual(expected_bytes, output.getvalue())
1471
1472
    def test_client_read_body_bytes_all(self):
1473
        # read_body_bytes should decode the body bytes from the wire into
1474
        # a response.
1475
        expected_bytes = "1234567"
1476
        server_bytes = "ok\n7\n1234567done\n"
1477
        input = StringIO(server_bytes)
1478
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
1479
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
1480
        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
1481
        smart_protocol = protocol.SmartClientRequestProtocolOne(request)
1482
        smart_protocol.call('foo')
1483
        smart_protocol.read_response_tuple(True)
1484
        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.
1485
1486
    def test_client_read_body_bytes_incremental(self):
1487
        # test reading a few bytes at a time from the body
1488
        # 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.
1489
        # 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.
1490
        # LengthPrefixedBodyDecoder that is already well tested - we can skip
1491
        # that.
1492
        expected_bytes = "1234567"
1493
        server_bytes = "ok\n7\n1234567done\n"
1494
        input = StringIO(server_bytes)
1495
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
1496
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
1497
        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
1498
        smart_protocol = protocol.SmartClientRequestProtocolOne(request)
1499
        smart_protocol.call('foo')
1500
        smart_protocol.read_response_tuple(True)
1501
        self.assertEqual(expected_bytes[0:2], smart_protocol.read_body_bytes(2))
1502
        self.assertEqual(expected_bytes[2:4], smart_protocol.read_body_bytes(2))
1503
        self.assertEqual(expected_bytes[4:6], smart_protocol.read_body_bytes(2))
1504
        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.
1505
1506
    def test_client_cancel_read_body_does_not_eat_body_bytes(self):
1507
        # cancelling the expected body needs to finish the request, but not
1508
        # read any more bytes.
1509
        expected_bytes = "1234567"
1510
        server_bytes = "ok\n7\n1234567done\n"
1511
        input = StringIO(server_bytes)
1512
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
1513
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
1514
        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
1515
        smart_protocol = protocol.SmartClientRequestProtocolOne(request)
1516
        smart_protocol.call('foo')
1517
        smart_protocol.read_response_tuple(True)
1518
        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.
1519
        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
1520
        self.assertRaises(
1521
            errors.ReadingCompleted, smart_protocol.read_body_bytes)
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1522
2018.2.15 by Andrew Bennetts
Remove SmartServerRequestProtocolOne.finished_reading attribute, replace with next_read_size method.
1523
2432.2.5 by Andrew Bennetts
Reduce duplication in test_smart_transport.
1524
class TestSmartProtocolTwo(TestSmartProtocol):
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1525
    """Tests for the smart protocol version two.
1526
1527
    This test case is mostly the same as TestSmartProtocolOne.
1528
    """
1529
2432.2.5 by Andrew Bennetts
Reduce duplication in test_smart_transport.
1530
    client_protocol_class = protocol.SmartClientRequestProtocolTwo
1531
    server_protocol_class = protocol.SmartServerRequestProtocolTwo
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1532
1533
    def test_construct_version_two_server_protocol(self):
1534
        smart_protocol = protocol.SmartServerRequestProtocolTwo(None, None)
1535
        self.assertEqual('', smart_protocol.excess_buffer)
1536
        self.assertEqual('', smart_protocol.in_buffer)
1537
        self.assertFalse(smart_protocol.has_dispatched)
1538
        self.assertEqual(1, smart_protocol.next_read_size())
1539
1540
    def test_construct_version_two_client_protocol(self):
1541
        # we can construct a client protocol from a client medium request
1542
        output = StringIO()
1543
        client_medium = medium.SmartSimplePipesClientMedium(None, output)
1544
        request = client_medium.get_request()
1545
        client_protocol = protocol.SmartClientRequestProtocolTwo(request)
1546
1547
    def test_server_offset_serialisation(self):
1548
        """The Smart protocol serialises offsets as a comma and \n string.
1549
1550
        We check a number of boundary cases are as expected: empty, one offset,
1551
        one with the order of reads not increasing (an out of order read), and
1552
        one that should coalesce.
1553
        """
1554
        self.assertOffsetSerialisation([], '', self.client_protocol)
1555
        self.assertOffsetSerialisation([(1,2)], '1,2', self.client_protocol)
1556
        self.assertOffsetSerialisation([(10,40), (0,5)], '10,40\n0,5',
1557
            self.client_protocol)
1558
        self.assertOffsetSerialisation([(1,2), (3,4), (100, 200)],
1559
            '1,2\n3,4\n100,200', self.client_protocol)
1560
1561
    def test_accept_bytes_of_bad_request_to_protocol(self):
1562
        out_stream = StringIO()
1563
        smart_protocol = protocol.SmartServerRequestProtocolTwo(
1564
            None, out_stream.write)
1565
        smart_protocol.accept_bytes('abc')
1566
        self.assertEqual('abc', smart_protocol.in_buffer)
1567
        smart_protocol.accept_bytes('\n')
1568
        self.assertEqual(
2432.2.7 by Andrew Bennetts
Use less confusing version strings, and define REQUEST_VERSION_TWO/RESPONSE_VERSION_TWO constants for them.
1569
            protocol.RESPONSE_VERSION_TWO +
2432.3.5 by Andrew Bennetts
Merge Robert's status prefix changes to protocol 2.
1570
            "failed\nerror\x01Generic bzr smart protocol error: bad request 'abc'\n",
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1571
            out_stream.getvalue())
1572
        self.assertTrue(smart_protocol.has_dispatched)
1573
        self.assertEqual(0, smart_protocol.next_read_size())
1574
1575
    def test_accept_body_bytes_to_protocol(self):
1576
        protocol = self.build_protocol_waiting_for_body()
1577
        self.assertEqual(6, protocol.next_read_size())
1578
        protocol.accept_bytes('7\nabc')
1579
        self.assertEqual(9, protocol.next_read_size())
1580
        protocol.accept_bytes('defgd')
1581
        protocol.accept_bytes('one\n')
1582
        self.assertEqual(0, protocol.next_read_size())
1583
        self.assertTrue(self.end_received)
1584
1585
    def test_accept_request_and_body_all_at_once(self):
1586
        self._captureVar('BZR_NO_SMART_VFS', None)
1587
        mem_transport = memory.MemoryTransport()
1588
        mem_transport.put_bytes('foo', 'abcdefghij')
1589
        out_stream = StringIO()
1590
        smart_protocol = protocol.SmartServerRequestProtocolTwo(mem_transport,
1591
                out_stream.write)
1592
        smart_protocol.accept_bytes('readv\x01foo\n3\n3,3done\n')
1593
        self.assertEqual(0, smart_protocol.next_read_size())
2432.3.5 by Andrew Bennetts
Merge Robert's status prefix changes to protocol 2.
1594
        self.assertEqual(protocol.RESPONSE_VERSION_TWO +
1595
                         'success\nreadv\n3\ndefdone\n',
2432.2.7 by Andrew Bennetts
Use less confusing version strings, and define REQUEST_VERSION_TWO/RESPONSE_VERSION_TWO constants for them.
1596
                         out_stream.getvalue())
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1597
        self.assertEqual('', smart_protocol.excess_buffer)
1598
        self.assertEqual('', smart_protocol.in_buffer)
1599
1600
    def test_accept_excess_bytes_are_preserved(self):
1601
        out_stream = StringIO()
1602
        smart_protocol = protocol.SmartServerRequestProtocolTwo(
1603
            None, out_stream.write)
1604
        smart_protocol.accept_bytes('hello\nhello\n')
2432.3.5 by Andrew Bennetts
Merge Robert's status prefix changes to protocol 2.
1605
        self.assertEqual(protocol.RESPONSE_VERSION_TWO + "success\nok\x012\n",
2432.2.7 by Andrew Bennetts
Use less confusing version strings, and define REQUEST_VERSION_TWO/RESPONSE_VERSION_TWO constants for them.
1606
                         out_stream.getvalue())
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1607
        self.assertEqual("hello\n", smart_protocol.excess_buffer)
1608
        self.assertEqual("", smart_protocol.in_buffer)
1609
1610
    def test_accept_excess_bytes_after_body(self):
1611
        # The excess bytes look like the start of another request.
2432.2.7 by Andrew Bennetts
Use less confusing version strings, and define REQUEST_VERSION_TWO/RESPONSE_VERSION_TWO constants for them.
1612
        server_protocol = self.build_protocol_waiting_for_body()
1613
        server_protocol.accept_bytes(
1614
            '7\nabcdefgdone\n' + protocol.RESPONSE_VERSION_TWO)
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1615
        self.assertTrue(self.end_received)
2432.2.7 by Andrew Bennetts
Use less confusing version strings, and define REQUEST_VERSION_TWO/RESPONSE_VERSION_TWO constants for them.
1616
        self.assertEqual(protocol.RESPONSE_VERSION_TWO,
1617
                         server_protocol.excess_buffer)
1618
        self.assertEqual("", server_protocol.in_buffer)
1619
        server_protocol.accept_bytes('Y')
1620
        self.assertEqual(protocol.RESPONSE_VERSION_TWO + "Y",
1621
                         server_protocol.excess_buffer)
1622
        self.assertEqual("", server_protocol.in_buffer)
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1623
1624
    def test_accept_excess_bytes_after_dispatch(self):
1625
        out_stream = StringIO()
1626
        smart_protocol = protocol.SmartServerRequestProtocolTwo(
1627
            None, out_stream.write)
1628
        smart_protocol.accept_bytes('hello\n')
2432.3.5 by Andrew Bennetts
Merge Robert's status prefix changes to protocol 2.
1629
        self.assertEqual(protocol.RESPONSE_VERSION_TWO + "success\nok\x012\n",
2432.2.7 by Andrew Bennetts
Use less confusing version strings, and define REQUEST_VERSION_TWO/RESPONSE_VERSION_TWO constants for them.
1630
                         out_stream.getvalue())
1631
        smart_protocol.accept_bytes(protocol.REQUEST_VERSION_TWO + 'hel')
1632
        self.assertEqual(protocol.REQUEST_VERSION_TWO + "hel",
1633
                         smart_protocol.excess_buffer)
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1634
        smart_protocol.accept_bytes('lo\n')
2432.2.7 by Andrew Bennetts
Use less confusing version strings, and define REQUEST_VERSION_TWO/RESPONSE_VERSION_TWO constants for them.
1635
        self.assertEqual(protocol.REQUEST_VERSION_TWO + "hello\n",
1636
                         smart_protocol.excess_buffer)
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1637
        self.assertEqual("", smart_protocol.in_buffer)
1638
1639
    def test__send_response_sets_finished_reading(self):
1640
        smart_protocol = protocol.SmartServerRequestProtocolTwo(
1641
            None, lambda x: None)
1642
        self.assertEqual(1, smart_protocol.next_read_size())
2432.4.3 by Robert Collins
Refactor the HPSS Response code to take SmartServerResponse rather than args and body.
1643
        smart_protocol._send_response(
1644
            request.SuccessfulSmartServerResponse(('x',)))
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1645
        self.assertEqual(0, smart_protocol.next_read_size())
1646
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
1647
    def test__send_response_errors_with_base_response(self):
1648
        """Ensure that only the Successful/Failed subclasses are used."""
2432.4.6 by Robert Collins
Include success/failure feedback in SmartProtocolTwo responses to allow robust handling in the future.
1649
        smart_protocol = protocol.SmartServerRequestProtocolTwo(
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
1650
            None, lambda x: None)
1651
        self.assertRaises(AttributeError, smart_protocol._send_response,
1652
            request.SmartServerResponse(('x',)))
1653
2432.4.6 by Robert Collins
Include success/failure feedback in SmartProtocolTwo responses to allow robust handling in the future.
1654
    def test__send_response_includes_failure_marker(self):
1655
        """FailedSmartServerResponse have 'failed\n' after the version."""
1656
        out_stream = StringIO()
1657
        smart_protocol = protocol.SmartServerRequestProtocolTwo(
1658
            None, out_stream.write)
1659
        smart_protocol._send_response(
1660
            request.FailedSmartServerResponse(('x',)))
2432.3.6 by Andrew Bennetts
Fix a couple of test failures introduced by the previous merge.
1661
        self.assertEqual(protocol.RESPONSE_VERSION_TWO + 'failed\nx\n',
1662
                         out_stream.getvalue())
2432.4.6 by Robert Collins
Include success/failure feedback in SmartProtocolTwo responses to allow robust handling in the future.
1663
1664
    def test__send_response_includes_success_marker(self):
1665
        """SuccessfulSmartServerResponse have 'success\n' after the version."""
1666
        out_stream = StringIO()
1667
        smart_protocol = protocol.SmartServerRequestProtocolTwo(
1668
            None, out_stream.write)
1669
        smart_protocol._send_response(
1670
            request.SuccessfulSmartServerResponse(('x',)))
2432.3.6 by Andrew Bennetts
Fix a couple of test failures introduced by the previous merge.
1671
        self.assertEqual(protocol.RESPONSE_VERSION_TWO + 'success\nx\n',
1672
                         out_stream.getvalue())
2432.4.6 by Robert Collins
Include success/failure feedback in SmartProtocolTwo responses to allow robust handling in the future.
1673
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1674
    def test_query_version(self):
1675
        """query_version on a SmartClientProtocolTwo should return a number.
1676
        
1677
        The protocol provides the query_version because the domain level clients
1678
        may all need to be able to probe for capabilities.
1679
        """
1680
        # What we really want to test here is that SmartClientProtocolTwo calls
1681
        # accept_bytes(tuple_based_encoding_of_hello) and reads and parses the
1682
        # response of tuple-encoded (ok, 1).  Also, seperately we should test
1683
        # the error if the response is a non-understood version.
2432.3.5 by Andrew Bennetts
Merge Robert's status prefix changes to protocol 2.
1684
        input = StringIO(protocol.RESPONSE_VERSION_TWO + 'success\nok\x012\n')
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1685
        output = StringIO()
1686
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
1687
        request = client_medium.get_request()
1688
        smart_protocol = protocol.SmartClientRequestProtocolTwo(request)
1689
        self.assertEqual(2, smart_protocol.query_version())
1690
1691
    def test_client_call_empty_response(self):
1692
        # protocol.call() can get back an empty tuple as a response. This occurs
1693
        # when the parsed line is an empty line, and results in a tuple with
1694
        # one element - an empty string.
2432.2.7 by Andrew Bennetts
Use less confusing version strings, and define REQUEST_VERSION_TWO/RESPONSE_VERSION_TWO constants for them.
1695
        self.assertServerToClientEncoding(
2432.3.5 by Andrew Bennetts
Merge Robert's status prefix changes to protocol 2.
1696
            protocol.RESPONSE_VERSION_TWO + 'success\n\n', ('', ), [(), ('', )])
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1697
2432.2.5 by Andrew Bennetts
Reduce duplication in test_smart_transport.
1698
    def test_client_call_three_element_response(self):
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1699
        # protocol.call() can get back tuples of other lengths. A three element
1700
        # tuple should be unpacked as three strings.
2432.2.7 by Andrew Bennetts
Use less confusing version strings, and define REQUEST_VERSION_TWO/RESPONSE_VERSION_TWO constants for them.
1701
        self.assertServerToClientEncoding(
2432.3.5 by Andrew Bennetts
Merge Robert's status prefix changes to protocol 2.
1702
            protocol.RESPONSE_VERSION_TWO + 'success\na\x01b\x0134\n',
1703
            ('a', 'b', '34'),
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1704
            [('a', 'b', '34')])
1705
1706
    def test_client_call_with_body_bytes_uploads(self):
1707
        # protocol.call_with_body_bytes should length-prefix the bytes onto the
1708
        # wire.
2432.2.7 by Andrew Bennetts
Use less confusing version strings, and define REQUEST_VERSION_TWO/RESPONSE_VERSION_TWO constants for them.
1709
        expected_bytes = protocol.REQUEST_VERSION_TWO + "foo\n7\nabcdefgdone\n"
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1710
        input = StringIO("\n")
1711
        output = StringIO()
1712
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
1713
        request = client_medium.get_request()
1714
        smart_protocol = protocol.SmartClientRequestProtocolTwo(request)
1715
        smart_protocol.call_with_body_bytes(('foo', ), "abcdefg")
1716
        self.assertEqual(expected_bytes, output.getvalue())
1717
1718
    def test_client_call_with_body_readv_array(self):
1719
        # protocol.call_with_upload should encode the readv array and then
1720
        # length-prefix the bytes onto the wire.
2432.2.7 by Andrew Bennetts
Use less confusing version strings, and define REQUEST_VERSION_TWO/RESPONSE_VERSION_TWO constants for them.
1721
        expected_bytes = protocol.REQUEST_VERSION_TWO+"foo\n7\n1,2\n5,6done\n"
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1722
        input = StringIO("\n")
1723
        output = StringIO()
1724
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
1725
        request = client_medium.get_request()
1726
        smart_protocol = protocol.SmartClientRequestProtocolTwo(request)
1727
        smart_protocol.call_with_body_readv_array(('foo', ), [(1,2),(5,6)])
1728
        self.assertEqual(expected_bytes, output.getvalue())
1729
2432.4.6 by Robert Collins
Include success/failure feedback in SmartProtocolTwo responses to allow robust handling in the future.
1730
    def test_client_read_response_tuple_sets_response_status(self):
2432.3.6 by Andrew Bennetts
Fix a couple of test failures introduced by the previous merge.
1731
        server_bytes = protocol.RESPONSE_VERSION_TWO + "success\nok\n"
2432.4.6 by Robert Collins
Include success/failure feedback in SmartProtocolTwo responses to allow robust handling in the future.
1732
        input = StringIO(server_bytes)
1733
        output = StringIO()
1734
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
1735
        request = client_medium.get_request()
1736
        smart_protocol = protocol.SmartClientRequestProtocolTwo(request)
1737
        smart_protocol.call('foo')
1738
        smart_protocol.read_response_tuple(False)
1739
        self.assertEqual(True, smart_protocol.response_status)
1740
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1741
    def test_client_read_body_bytes_all(self):
1742
        # read_body_bytes should decode the body bytes from the wire into
1743
        # a response.
1744
        expected_bytes = "1234567"
2432.3.5 by Andrew Bennetts
Merge Robert's status prefix changes to protocol 2.
1745
        server_bytes = (protocol.RESPONSE_VERSION_TWO +
1746
                        "success\nok\n7\n1234567done\n")
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1747
        input = StringIO(server_bytes)
1748
        output = StringIO()
1749
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
1750
        request = client_medium.get_request()
1751
        smart_protocol = protocol.SmartClientRequestProtocolTwo(request)
1752
        smart_protocol.call('foo')
1753
        smart_protocol.read_response_tuple(True)
1754
        self.assertEqual(expected_bytes, smart_protocol.read_body_bytes())
1755
1756
    def test_client_read_body_bytes_incremental(self):
1757
        # test reading a few bytes at a time from the body
1758
        # XXX: possibly we should test dribbling the bytes into the stringio
1759
        # to make the state machine work harder: however, as we use the
1760
        # LengthPrefixedBodyDecoder that is already well tested - we can skip
1761
        # that.
1762
        expected_bytes = "1234567"
2432.3.5 by Andrew Bennetts
Merge Robert's status prefix changes to protocol 2.
1763
        server_bytes = (protocol.RESPONSE_VERSION_TWO +
1764
                        "success\nok\n7\n1234567done\n")
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1765
        input = StringIO(server_bytes)
1766
        output = StringIO()
1767
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
1768
        request = client_medium.get_request()
1769
        smart_protocol = protocol.SmartClientRequestProtocolTwo(request)
1770
        smart_protocol.call('foo')
1771
        smart_protocol.read_response_tuple(True)
1772
        self.assertEqual(expected_bytes[0:2], smart_protocol.read_body_bytes(2))
1773
        self.assertEqual(expected_bytes[2:4], smart_protocol.read_body_bytes(2))
1774
        self.assertEqual(expected_bytes[4:6], smart_protocol.read_body_bytes(2))
1775
        self.assertEqual(expected_bytes[6], smart_protocol.read_body_bytes())
1776
1777
    def test_client_cancel_read_body_does_not_eat_body_bytes(self):
1778
        # cancelling the expected body needs to finish the request, but not
1779
        # read any more bytes.
1780
        expected_bytes = "1234567"
2432.3.5 by Andrew Bennetts
Merge Robert's status prefix changes to protocol 2.
1781
        server_bytes = (protocol.RESPONSE_VERSION_TWO +
1782
                        "success\nok\n7\n1234567done\n")
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1783
        input = StringIO(server_bytes)
1784
        output = StringIO()
1785
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
1786
        request = client_medium.get_request()
1787
        smart_protocol = protocol.SmartClientRequestProtocolTwo(request)
1788
        smart_protocol.call('foo')
1789
        smart_protocol.read_response_tuple(True)
1790
        smart_protocol.cancel_read_body()
2432.3.5 by Andrew Bennetts
Merge Robert's status prefix changes to protocol 2.
1791
        self.assertEqual(len(protocol.RESPONSE_VERSION_TWO + 'success\nok\n'),
2432.2.7 by Andrew Bennetts
Use less confusing version strings, and define REQUEST_VERSION_TWO/RESPONSE_VERSION_TWO constants for them.
1792
                         input.tell())
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1793
        self.assertRaises(
1794
            errors.ReadingCompleted, smart_protocol.read_body_bytes)
1795
1796
2018.5.131 by Andrew Bennetts
Be strict about unicode passed to transport.put_{bytes,file} and SmartClient.call_with_body_bytes, fixing part of TestLockableFiles_RemoteLockDir.test_read_write.
1797
class TestSmartClientUnicode(tests.TestCase):
2414.1.4 by Andrew Bennetts
Rename SmartClient to _SmartClient.
1798
    """_SmartClient tests for unicode arguments.
2018.5.131 by Andrew Bennetts
Be strict about unicode passed to transport.put_{bytes,file} and SmartClient.call_with_body_bytes, fixing part of TestLockableFiles_RemoteLockDir.test_read_write.
1799
1800
    Unicode arguments to call_with_body_bytes are not correct (remote method
1801
    names, arguments, and bodies must all be expressed as byte strings), but
2414.1.4 by Andrew Bennetts
Rename SmartClient to _SmartClient.
1802
    _SmartClient should gracefully reject them, rather than getting into a
1803
    broken state that prevents future correct calls from working.  That is, it
1804
    should be possible to issue more requests on the medium afterwards, rather
1805
    than allowing one bad call to call_with_body_bytes to cause later calls to
2018.5.131 by Andrew Bennetts
Be strict about unicode passed to transport.put_{bytes,file} and SmartClient.call_with_body_bytes, fixing part of TestLockableFiles_RemoteLockDir.test_read_write.
1806
    mysteriously fail with TooManyConcurrentRequests.
1807
    """
1808
1809
    def assertCallDoesNotBreakMedium(self, method, args, body):
1810
        """Call a medium with the given method, args and body, then assert that
1811
        the medium is left in a sane state, i.e. is capable of allowing further
1812
        requests.
1813
        """
1814
        input = StringIO("\n")
1815
        output = StringIO()
1816
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
2414.1.4 by Andrew Bennetts
Rename SmartClient to _SmartClient.
1817
        smart_client = client._SmartClient(client_medium)
2018.5.131 by Andrew Bennetts
Be strict about unicode passed to transport.put_{bytes,file} and SmartClient.call_with_body_bytes, fixing part of TestLockableFiles_RemoteLockDir.test_read_write.
1818
        self.assertRaises(TypeError,
1819
            smart_client.call_with_body_bytes, method, args, body)
1820
        self.assertEqual("", output.getvalue())
1821
        self.assertEqual(None, client_medium._current_request)
1822
1823
    def test_call_with_body_bytes_unicode_method(self):
1824
        self.assertCallDoesNotBreakMedium(u'method', ('args',), 'body')
1825
1826
    def test_call_with_body_bytes_unicode_args(self):
1827
        self.assertCallDoesNotBreakMedium('method', (u'args',), 'body')
2414.1.2 by Andrew Bennetts
Deal with review comments.
1828
        self.assertCallDoesNotBreakMedium('method', ('arg1', u'arg2'), 'body')
2018.5.131 by Andrew Bennetts
Be strict about unicode passed to transport.put_{bytes,file} and SmartClient.call_with_body_bytes, fixing part of TestLockableFiles_RemoteLockDir.test_read_write.
1829
1830
    def test_call_with_body_bytes_unicode_body(self):
1831
        self.assertCallDoesNotBreakMedium('method', ('args',), u'body')
1832
1833
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1834
class LengthPrefixedBodyDecoder(tests.TestCase):
1835
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1836
    # XXX: TODO: make accept_reading_trailer invoke translate_response or 
1837
    # something similar to the ProtocolBase method.
1838
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1839
    def test_construct(self):
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1840
        decoder = protocol.LengthPrefixedBodyDecoder()
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1841
        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.
1842
        self.assertEqual(6, decoder.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1843
        self.assertEqual('', decoder.read_pending_data())
1844
        self.assertEqual('', decoder.unused_data)
1845
1846
    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
1847
        decoder = protocol.LengthPrefixedBodyDecoder()
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1848
        decoder.accept_bytes('')
1849
        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.
1850
        self.assertEqual(6, decoder.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1851
        self.assertEqual('', decoder.read_pending_data())
1852
        self.assertEqual('', decoder.unused_data)
1853
        decoder.accept_bytes('7')
1854
        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.
1855
        self.assertEqual(6, decoder.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1856
        self.assertEqual('', decoder.read_pending_data())
1857
        self.assertEqual('', decoder.unused_data)
1858
        decoder.accept_bytes('\na')
1859
        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.
1860
        self.assertEqual(11, decoder.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1861
        self.assertEqual('a', decoder.read_pending_data())
1862
        self.assertEqual('', decoder.unused_data)
1863
        decoder.accept_bytes('bcdefgd')
1864
        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.
1865
        self.assertEqual(4, decoder.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1866
        self.assertEqual('bcdefg', decoder.read_pending_data())
1867
        self.assertEqual('', decoder.unused_data)
1868
        decoder.accept_bytes('one')
1869
        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.
1870
        self.assertEqual(1, decoder.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1871
        self.assertEqual('', decoder.read_pending_data())
1872
        self.assertEqual('', decoder.unused_data)
1873
        decoder.accept_bytes('\nblarg')
1874
        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.
1875
        self.assertEqual(1, decoder.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1876
        self.assertEqual('', decoder.read_pending_data())
1877
        self.assertEqual('blarg', decoder.unused_data)
1878
        
1879
    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
1880
        decoder = protocol.LengthPrefixedBodyDecoder()
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1881
        decoder.accept_bytes('1\nadone\nunused')
1882
        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.
1883
        self.assertEqual(1, decoder.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1884
        self.assertEqual('a', decoder.read_pending_data())
1885
        self.assertEqual('unused', decoder.unused_data)
1886
1887
    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
1888
        decoder = protocol.LengthPrefixedBodyDecoder()
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1889
        decoder.accept_bytes('1\na')
1890
        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.
1891
        self.assertEqual(5, decoder.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1892
        self.assertEqual('a', decoder.read_pending_data())
1893
        self.assertEqual('', decoder.unused_data)
1894
        decoder.accept_bytes('done\n')
1895
        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.
1896
        self.assertEqual(1, decoder.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1897
        self.assertEqual('', decoder.read_pending_data())
1898
        self.assertEqual('', decoder.unused_data)
1899
1900
2432.4.1 by Robert Collins
Add SuccessfulSmartServerResponse.
1901
class TestSuccessfulSmartServerResponse(tests.TestCase):
1902
1903
    def test_construct(self):
1904
        response = request.SuccessfulSmartServerResponse(('foo', 'bar'))
1905
        self.assertEqual(('foo', 'bar'), response.args)
1906
        self.assertEqual(None, response.body)
1907
        response = request.SuccessfulSmartServerResponse(('foo', 'bar'), 'bytes')
1908
        self.assertEqual(('foo', 'bar'), response.args)
1909
        self.assertEqual('bytes', response.body)
1910
1911
    def test_is_successful(self):
1912
        """is_successful should return True for SuccessfulSmartServerResponse."""
1913
        response = request.SuccessfulSmartServerResponse(('error',))
1914
        self.assertEqual(True, response.is_successful())
1915
1916
2432.4.2 by Robert Collins
Add FailedSmartServerResponse.
1917
class TestFailedSmartServerResponse(tests.TestCase):
1918
1919
    def test_construct(self):
1920
        response = request.FailedSmartServerResponse(('foo', 'bar'))
1921
        self.assertEqual(('foo', 'bar'), response.args)
1922
        self.assertEqual(None, response.body)
1923
        response = request.FailedSmartServerResponse(('foo', 'bar'), 'bytes')
1924
        self.assertEqual(('foo', 'bar'), response.args)
1925
        self.assertEqual('bytes', response.body)
1926
1927
    def test_is_successful(self):
1928
        """is_successful should return False for FailedSmartServerResponse."""
1929
        response = request.FailedSmartServerResponse(('error',))
1930
        self.assertEqual(False, response.is_successful())
1931
2432.4.1 by Robert Collins
Add SuccessfulSmartServerResponse.
1932
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
1933
class FakeHTTPMedium(object):
1934
    def __init__(self):
1935
        self.written_request = None
1936
        self._current_request = None
2018.2.8 by Andrew Bennetts
Make HttpTransportBase.get_smart_client return self again.
1937
    def send_http_smart_request(self, bytes):
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
1938
        self.written_request = bytes
1939
        return None
1940
1941
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1942
class HTTPTunnellingSmokeTest(tests.TestCaseWithTransport):
1943
    
2018.5.24 by Andrew Bennetts
Setting NO_SMART_VFS in environment will disable VFS methods in the smart server. (Robert Collins, John Arbash Meinel, Andrew Bennetts)
1944
    def setUp(self):
1945
        super(HTTPTunnellingSmokeTest, self).setUp()
1946
        # We use the VFS layer as part of HTTP tunnelling tests.
2402.1.2 by Andrew Bennetts
Deal with review comments.
1947
        self._captureVar('BZR_NO_SMART_VFS', None)
2018.5.24 by Andrew Bennetts
Setting NO_SMART_VFS in environment will disable VFS methods in the smart server. (Robert Collins, John Arbash Meinel, Andrew Bennetts)
1948
2018.2.7 by Andrew Bennetts
Implement _post on HttpTransport_urllib.
1949
    def _test_bulk_data(self, url_protocol):
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
1950
        # We should be able to send and receive bulk data in a single message.
1951
        # The 'readv' command in the smart protocol both sends and receives bulk
1952
        # data, so we use that.
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1953
        self.build_tree(['data-file'])
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
1954
        self.transport_readonly_server = HTTPServerWithSmarts
1955
1956
        http_transport = self.get_readonly_transport()
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
1957
        medium = http_transport.get_smart_medium()
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
1958
        #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)
1959
        remote_transport = remote.RemoteTransport('/', medium=medium)
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
1960
        self.assertEqual(
1961
            [(0, "c")], list(remote_transport.readv("data-file", [(0,1)])))
1962
2018.2.7 by Andrew Bennetts
Implement _post on HttpTransport_urllib.
1963
    def test_bulk_data_pycurl(self):
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
1964
        try:
1965
            self._test_bulk_data('http+pycurl')
1966
        except errors.UnsupportedProtocol, e:
1967
            raise tests.TestSkipped(str(e))
2018.2.7 by Andrew Bennetts
Implement _post on HttpTransport_urllib.
1968
    
1969
    def test_bulk_data_urllib(self):
1970
        self._test_bulk_data('http+urllib')
1971
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
1972
    def test_smart_http_medium_request_accept_bytes(self):
1973
        medium = FakeHTTPMedium()
2018.2.8 by Andrew Bennetts
Make HttpTransportBase.get_smart_client return self again.
1974
        request = SmartClientHTTPMediumRequest(medium)
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
1975
        request.accept_bytes('abc')
1976
        request.accept_bytes('def')
1977
        self.assertEqual(None, medium.written_request)
1978
        request.finished_writing()
1979
        self.assertEqual('abcdef', medium.written_request)
1980
2018.2.7 by Andrew Bennetts
Implement _post on HttpTransport_urllib.
1981
    def _test_http_send_smart_request(self, url_protocol):
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
1982
        http_server = HTTPServerWithSmarts()
2018.2.7 by Andrew Bennetts
Implement _post on HttpTransport_urllib.
1983
        http_server._url_protocol = url_protocol
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
1984
        http_server.setUp(self.get_vfs_only_server())
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
1985
        self.addCleanup(http_server.tearDown)
1986
1987
        post_body = 'hello\n'
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1988
        expected_reply_body = 'ok\x012\n'
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
1989
1990
        http_transport = get_transport(http_server.get_url())
1991
        medium = http_transport.get_smart_medium()
2018.2.8 by Andrew Bennetts
Make HttpTransportBase.get_smart_client return self again.
1992
        response = medium.send_http_smart_request(post_body)
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
1993
        reply_body = response.read()
1994
        self.assertEqual(expected_reply_body, reply_body)
1995
2018.2.7 by Andrew Bennetts
Implement _post on HttpTransport_urllib.
1996
    def test_http_send_smart_request_pycurl(self):
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
1997
        try:
1998
            self._test_http_send_smart_request('http+pycurl')
1999
        except errors.UnsupportedProtocol, e:
2000
            raise tests.TestSkipped(str(e))
2018.2.7 by Andrew Bennetts
Implement _post on HttpTransport_urllib.
2001
2002
    def test_http_send_smart_request_urllib(self):
2003
        self._test_http_send_smart_request('http+urllib')
2004
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
2005
    def test_http_server_with_smarts(self):
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
2006
        self.transport_readonly_server = HTTPServerWithSmarts
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
2007
2008
        post_body = 'hello\n'
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
2009
        expected_reply_body = 'ok\x012\n'
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
2010
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
2011
        smart_server_url = self.get_readonly_url('.bzr/smart')
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
2012
        reply = urllib2.urlopen(smart_server_url, post_body).read()
2013
2014
        self.assertEqual(expected_reply_body, reply)
2015
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
2016
    def test_smart_http_server_post_request_handler(self):
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
2017
        self.transport_readonly_server = HTTPServerWithSmarts
2018
        httpd = self.get_readonly_server()._get_httpd()
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
2019
2020
        socket = SampleSocket(
2021
            'POST /.bzr/smart HTTP/1.0\r\n'
2022
            # HTTP/1.0 posts must have a Content-Length.
2023
            'Content-Length: 6\r\n'
2024
            '\r\n'
2025
            'hello\n')
2164.2.13 by v.ladeuil+lp at free
Add tests for redirection. Preserve transport decorations.
2026
        # Beware: the ('localhost', 80) below is the
2027
        # client_address parameter, but we don't have one because
2164.2.14 by v.ladeuil+lp at free
Typo in comment
2028
        # we have defined a socket which is not bound to an
2164.2.13 by v.ladeuil+lp at free
Add tests for redirection. Preserve transport decorations.
2029
        # address. The test framework never uses this client
2030
        # address, so far...
2031
        request_handler = SmartRequestHandler(socket, ('localhost', 80), httpd)
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
2032
        response = socket.writefile.getvalue()
2033
        self.assertStartsWith(response, 'HTTP/1.0 200 ')
2034
        # This includes the end of the HTTP headers, and all the body.
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
2035
        expected_end_of_response = '\r\n\r\nok\x012\n'
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
2036
        self.assertEndsWith(response, expected_end_of_response)
2037
2038
2039
class SampleSocket(object):
2040
    """A socket-like object for use in testing the HTTP request handler."""
2041
    
2042
    def __init__(self, socket_read_content):
2043
        """Constructs a sample socket.
2044
2045
        :param socket_read_content: a byte sequence
2046
        """
2047
        # Use plain python StringIO so we can monkey-patch the close method to
2048
        # not discard the contents.
2049
        from StringIO import StringIO
2050
        self.readfile = StringIO(socket_read_content)
2051
        self.writefile = StringIO()
2052
        self.writefile.close = lambda: None
2053
        
2054
    def makefile(self, mode='r', bufsize=None):
2055
        if 'r' in mode:
2056
            return self.readfile
2057
        else:
2058
            return self.writefile
2059
2018.5.26 by Andrew Bennetts
Extract a simple SmartClient class from RemoteTransport, and a hack to avoid VFS operations when probing for a bzrdir over a smart transport.
2060
2208.4.4 by Andrew Bennetts
Merge bzr.dev.
2061
class RemoteHTTPTransportTestCase(tests.TestCase):
2208.4.2 by Andrew Bennetts
Always POST to the same .bzr/smart URL for a given branch, even when accessing files in subdirectories.
2062
2208.4.3 by Andrew Bennetts
Let SmartHTTPTransport.clone('..') continue to POST to the cloned URL (unlike clone('child')).
2063
    def test_remote_path_after_clone_child(self):
2208.4.2 by Andrew Bennetts
Always POST to the same .bzr/smart URL for a given branch, even when accessing files in subdirectories.
2064
        # If a user enters "bzr+http://host/foo", we want to sent all smart
2208.4.3 by Andrew Bennetts
Let SmartHTTPTransport.clone('..') continue to POST to the cloned URL (unlike clone('child')).
2065
        # requests for child URLs of that to the original URL.  i.e., we want to
2066
        # POST to "bzr+http://host/foo/.bzr/smart" and never something like
2067
        # "bzr+http://host/foo/.bzr/branch/.bzr/smart".  So, a cloned
2208.4.4 by Andrew Bennetts
Merge bzr.dev.
2068
        # RemoteHTTPTransport remembers the initial URL, and adjusts the relpaths
2208.4.2 by Andrew Bennetts
Always POST to the same .bzr/smart URL for a given branch, even when accessing files in subdirectories.
2069
        # it sends in smart requests accordingly.
2208.4.4 by Andrew Bennetts
Merge bzr.dev.
2070
        base_transport = remote.RemoteHTTPTransport('bzr+http://host/path')
2208.4.2 by Andrew Bennetts
Always POST to the same .bzr/smart URL for a given branch, even when accessing files in subdirectories.
2071
        new_transport = base_transport.clone('child_dir')
2208.4.3 by Andrew Bennetts
Let SmartHTTPTransport.clone('..') continue to POST to the cloned URL (unlike clone('child')).
2072
        self.assertEqual(base_transport._http_transport,
2073
                         new_transport._http_transport)
2208.4.2 by Andrew Bennetts
Always POST to the same .bzr/smart URL for a given branch, even when accessing files in subdirectories.
2074
        self.assertEqual('child_dir/foo', new_transport._remote_path('foo'))
2075
2208.4.3 by Andrew Bennetts
Let SmartHTTPTransport.clone('..') continue to POST to the cloned URL (unlike clone('child')).
2076
    def test_remote_path_after_clone_parent(self):
2077
        # However, accessing a parent directory should go direct to the parent's
2078
        # URL.  We don't send relpaths like "../foo" in smart requests.
2208.4.4 by Andrew Bennetts
Merge bzr.dev.
2079
        base_transport = remote.RemoteHTTPTransport('bzr+http://host/path1/path2')
2208.4.3 by Andrew Bennetts
Let SmartHTTPTransport.clone('..') continue to POST to the cloned URL (unlike clone('child')).
2080
        new_transport = base_transport.clone('..')
2081
        self.assertEqual('foo', new_transport._remote_path('foo'))
2082
        new_transport = base_transport.clone('../')
2083
        self.assertEqual('foo', new_transport._remote_path('foo'))
2084
        new_transport = base_transport.clone('../abc')
2085
        self.assertEqual('foo', new_transport._remote_path('foo'))
2086
        # "abc/../.." should be equivalent to ".."
2087
        new_transport = base_transport.clone('abc/../..')
2088
        self.assertEqual('foo', new_transport._remote_path('foo'))
2089
2466.3.1 by Andrew Bennetts
Normalise URLs in RemoteHTTPTransport before doing URL calculations to fix bad results.
2090
    def test_remote_path_unnormal_base(self):
2091
        # If the transport's base isn't normalised, the _remote_path should
2092
        # still be calculated correctly.
2093
        base_transport = remote.RemoteHTTPTransport('bzr+http://host/%7Ea/b')
2094
        self.assertEqual('c', base_transport._remote_path('c'))
2095
2096
    def test_clone_unnormal_base(self):
2097
        # If the transport's base isn't normalised, cloned transports should
2098
        # still work correctly.
2099
        base_transport = remote.RemoteHTTPTransport('bzr+http://host/%7Ea/b')
2100
        new_transport = base_transport.clone('c')
2101
        self.assertEqual('bzr+http://host/%7Ea/b/c/', new_transport.base)
2102
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
2103
        
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
2104
# TODO: Client feature that does get_bundle and then installs that into a
2105
# branch; this can be used in place of the regular pull/fetch operation when
2106
# coming from a smart server.
2107
#
2108
# TODO: Eventually, want to do a 'branch' command by fetching the whole
2109
# history as one big bundle.  How?  
2110
#
2111
# The branch command does 'br_from.sprout', which tries to preserve the same
2112
# format.  We don't necessarily even want that.  
2113
#
2114
# It might be simpler to handle cmd_pull first, which does a simpler fetch()
2115
# operation from one branch into another.  It already has some code for
2116
# pulling from a bundle, which it does by trying to see if the destination is
2117
# a bundle file.  So it seems the logic for pull ought to be:
2118
# 
2119
#  - if it's a smart server, get a bundle from there and install that
2120
#  - if it's a bundle, install that
2121
#  - if it's a branch, pull from there
2122
#
2123
# Getting a bundle from a smart server is a bit different from reading a
2124
# bundle from a URL:
2125
#
2126
#  - we can reasonably remember the URL we last read from 
2127
#  - you can specify a revision number to pull, and we need to pass it across
2128
#    to the server as a limit on what will be requested
2129
#
2130
# TODO: Given a URL, determine whether it is a smart server or not (or perhaps
2131
# otherwise whether it's a bundle?)  Should this be a property or method of
2132
# the transport?  For the ssh protocol, we always know it's a smart server.
2133
# For http, we potentially need to probe.  But if we're explicitly given
2134
# bzr+http:// then we can skip that for now.