/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'
2634.1.1 by Robert Collins
(robertc) Reinstate the accidentally backed out external_url patch.
837
            def external_url(self):
838
                return self.base
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.
839
            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
840
                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.
841
        smart_server = server.SmartTCPServer(backing_transport=FlakyTransport())
842
        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
843
        try:
2413.2.1 by Andrew Bennetts
Rename Smart.*Transport classes to RemoteTransport, RemoteTCPTransport, etc.
844
            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
845
            try:
846
                transport.get('something')
847
            except errors.TransportError, e:
848
                self.assertContainsRe(str(e), 'some random exception')
849
            else:
850
                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.
851
            transport.disconnect()
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
852
        finally:
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
853
            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
854
855
856
class SmartTCPTests(tests.TestCase):
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
857
    """Tests for connection/end to end behaviour using the TCP server.
858
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
859
    All of these tests are run with a server running on another thread serving
860
    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`.
861
862
    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
863
    """
864
2634.1.1 by Robert Collins
(robertc) Reinstate the accidentally backed out external_url patch.
865
    def setUpServer(self, readonly=False, backing_transport=None):
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
866
        """Setup the server.
867
868
        :param readonly: Create a readonly server.
869
        """
2634.1.1 by Robert Collins
(robertc) Reinstate the accidentally backed out external_url patch.
870
        if not backing_transport:
871
            self.backing_transport = memory.MemoryTransport()
872
        else:
873
            self.backing_transport = backing_transport
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
874
        if readonly:
875
            self.real_backing_transport = self.backing_transport
876
            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.
877
        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
878
        self.server.start_background_thread()
2413.2.1 by Andrew Bennetts
Rename Smart.*Transport classes to RemoteTransport, RemoteTCPTransport, etc.
879
        self.transport = remote.RemoteTCPTransport(self.server.get_url())
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
880
        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
881
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
882
    def tearDownServer(self):
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
883
        if getattr(self, 'transport', None):
884
            self.transport.disconnect()
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
885
            del self.transport
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
886
        if getattr(self, 'server', None):
887
            self.server.stop_background_thread()
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
888
            del self.server
889
890
891
class TestServerSocketUsage(SmartTCPTests):
892
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
    def test_server_setup_teardown(self):
894
        """It should be safe to teardown the server with no requests."""
895
        self.setUpServer()
896
        server = self.server
2413.2.1 by Andrew Bennetts
Rename Smart.*Transport classes to RemoteTransport, RemoteTCPTransport, etc.
897
        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.
898
        self.tearDownServer()
899
        self.assertRaises(errors.ConnectionError, transport.has, '.')
900
901
    def test_server_closes_listening_sock_on_shutdown_after_request(self):
2370.4.2 by Robert Collins
Review feedback.
902
        """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
903
        self.setUpServer()
2370.4.2 by Robert Collins
Review feedback.
904
        server = self.server
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
905
        self.transport.has('.')
906
        self.tearDownServer()
907
        # if the listening socket has closed, we should get a BADFD error
908
        # when connecting, rather than a hang.
2413.2.1 by Andrew Bennetts
Rename Smart.*Transport classes to RemoteTransport, RemoteTCPTransport, etc.
909
        transport = remote.RemoteTCPTransport(server.get_url())
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
910
        self.assertRaises(errors.ConnectionError, transport.has, '.')
911
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
912
913
class WritableEndToEndTests(SmartTCPTests):
914
    """Client to server tests that require a writable transport."""
915
916
    def setUp(self):
917
        super(WritableEndToEndTests, self).setUp()
918
        self.setUpServer()
919
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
920
    def test_start_tcp_server(self):
921
        url = self.server.get_url()
922
        self.assertContainsRe(url, r'^bzr://127\.0\.0\.1:[0-9]{2,}/')
923
924
    def test_smart_transport_has(self):
925
        """Checking for file existence over smart."""
2402.1.2 by Andrew Bennetts
Deal with review comments.
926
        self._captureVar('BZR_NO_SMART_VFS', None)
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
927
        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
928
        self.assertTrue(self.transport.has("foo"))
929
        self.assertFalse(self.transport.has("non-foo"))
930
931
    def test_smart_transport_get(self):
932
        """Read back a file over smart."""
2402.1.2 by Andrew Bennetts
Deal with review comments.
933
        self._captureVar('BZR_NO_SMART_VFS', None)
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
934
        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
935
        fp = self.transport.get("foo")
936
        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`.
937
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
938
    def test_get_error_enoent(self):
939
        """Error reported from server getting nonexistent file."""
1752.2.81 by Andrew Bennetts
Merge cleaned up underlying dependencies for remote bzrdir.
940
        # The path in a raised NoSuchFile exception should be the precise path
941
        # asked for by the client. This gives meaningful and unsurprising errors
942
        # for users.
2402.1.2 by Andrew Bennetts
Deal with review comments.
943
        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
944
        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.
945
            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
946
        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.
947
            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
948
        else:
949
            self.fail("get did not raise expected error")
950
951
    def test_simple_clone_conn(self):
952
        """Test that cloning reuses the same connection."""
953
        # we create a real connection not a loopback one, but it will use the
954
        # 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__.
955
        conn2 = self.transport.clone('.')
2485.8.54 by Vincent Ladeuil
Refactor medium uses by making a distinction betweem shared and real medium.
956
        self.assertIs(self.transport.get_smart_medium(),
957
                      conn2.get_smart_medium())
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
958
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)
959
    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
960
        self.assertEquals('/foo/bar',
961
                          self.transport._remote_path('foo/bar'))
962
963
    def test_clone_changes_base(self):
964
        """Cloning transport produces one with a new base location"""
965
        conn2 = self.transport.clone('subdir')
966
        self.assertEquals(self.transport.base + 'subdir/',
967
                          conn2.base)
968
969
    def test_open_dir(self):
970
        """Test changing directory"""
2402.1.2 by Andrew Bennetts
Deal with review comments.
971
        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
972
        transport = self.transport
973
        self.backing_transport.mkdir('toffee')
974
        self.backing_transport.mkdir('toffee/apple')
975
        self.assertEquals('/toffee', transport._remote_path('toffee'))
1910.19.13 by Andrew Bennetts
Address various review comments.
976
        toffee_trans = transport.clone('toffee')
977
        # Check that each transport has only the contents of its directory
978
        # directly visible. If state was being held in the wrong object, it's
979
        # conceivable that cloning a transport would alter the state of the
980
        # cloned-from transport.
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
981
        self.assertTrue(transport.has('toffee'))
1910.19.13 by Andrew Bennetts
Address various review comments.
982
        self.assertFalse(toffee_trans.has('toffee'))
983
        self.assertFalse(transport.has('apple'))
984
        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
985
986
    def test_open_bzrdir(self):
987
        """Open an existing bzrdir over smart transport"""
988
        transport = self.transport
989
        t = self.backing_transport
990
        bzrdir.BzrDirFormat.get_default_format().initialize_on_transport(t)
991
        result_dir = bzrdir.BzrDir.open_containing_from_transport(transport)
992
993
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
994
class ReadOnlyEndToEndTests(SmartTCPTests):
995
    """Tests from the client to the server using a readonly backing transport."""
996
997
    def test_mkdir_error_readonly(self):
998
        """TransportNotPossible should be preserved from the backing transport."""
2402.1.2 by Andrew Bennetts
Deal with review comments.
999
        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`.
1000
        self.setUpServer(readonly=True)
1001
        self.assertRaises(errors.TransportNotPossible, self.transport.mkdir,
1002
            'foo')
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
1003
1004
1005
class TestServerHooks(SmartTCPTests):
1006
2634.1.1 by Robert Collins
(robertc) Reinstate the accidentally backed out external_url patch.
1007
    def capture_server_call(self, backing_urls, public_url):
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
1008
        """Record a server_started|stopped hook firing."""
2634.1.1 by Robert Collins
(robertc) Reinstate the accidentally backed out external_url patch.
1009
        self.hook_calls.append((backing_urls, public_url))
1010
1011
    def test_server_started_hook_memory(self):
1012
        """The server_started hook fires when the server is started."""
1013
        self.hook_calls = []
1014
        server.SmartTCPServer.hooks.install_hook('server_started',
1015
            self.capture_server_call)
1016
        self.setUpServer()
1017
        # at this point, the server will be starting a thread up.
1018
        # there is no indicator at the moment, so bodge it by doing a request.
1019
        self.transport.has('.')
1020
        # The default test server uses MemoryTransport and that has no external
1021
        # url:
1022
        self.assertEqual([([self.backing_transport.base], self.transport.base)],
1023
            self.hook_calls)
1024
1025
    def test_server_started_hook_file(self):
1026
        """The server_started hook fires when the server is started."""
1027
        self.hook_calls = []
1028
        server.SmartTCPServer.hooks.install_hook('server_started',
1029
            self.capture_server_call)
1030
        self.setUpServer(backing_transport=get_transport("."))
1031
        # at this point, the server will be starting a thread up.
1032
        # there is no indicator at the moment, so bodge it by doing a request.
1033
        self.transport.has('.')
1034
        # The default test server uses MemoryTransport and that has no external
1035
        # url:
1036
        self.assertEqual([([
1037
            self.backing_transport.base, self.backing_transport.external_url()],
1038
             self.transport.base)],
1039
            self.hook_calls)
1040
1041
    def test_server_stopped_hook_simple_memory(self):
1042
        """The server_stopped hook fires when the server is stopped."""
1043
        self.hook_calls = []
1044
        server.SmartTCPServer.hooks.install_hook('server_stopped',
1045
            self.capture_server_call)
1046
        self.setUpServer()
1047
        result = [([self.backing_transport.base], self.transport.base)]
1048
        # check the stopping message isn't emitted up front.
1049
        self.assertEqual([], self.hook_calls)
1050
        # nor after a single message
1051
        self.transport.has('.')
1052
        self.assertEqual([], self.hook_calls)
1053
        # clean up the server
1054
        self.tearDownServer()
1055
        # now it should have fired.
1056
        self.assertEqual(result, self.hook_calls)
1057
1058
    def test_server_stopped_hook_simple_file(self):
1059
        """The server_stopped hook fires when the server is stopped."""
1060
        self.hook_calls = []
1061
        server.SmartTCPServer.hooks.install_hook('server_stopped',
1062
            self.capture_server_call)
1063
        self.setUpServer(backing_transport=get_transport("."))
1064
        result = [(
1065
            [self.backing_transport.base, self.backing_transport.external_url()]
1066
            , 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.
1067
        # check the stopping message isn't emitted up front.
1068
        self.assertEqual([], self.hook_calls)
1069
        # nor after a single message
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
1070
        self.transport.has('.')
1071
        self.assertEqual([], self.hook_calls)
1072
        # clean up the server
1073
        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.
1074
        # now it should have fired.
2370.4.1 by Robert Collins
New SmartServer hooks facility. There are two initial hooks documented
1075
        self.assertEqual(result, self.hook_calls)
1076
1077
# TODO: test that when the server suffers an exception that it calls the
1078
# server-stopped hook.
1079
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
1080
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
1081
class SmartServerCommandTests(tests.TestCaseWithTransport):
1082
    """Tests that call directly into the command objects, bypassing the network
1083
    and the request dispatching.
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1084
1085
    Note: these tests are rudimentary versions of the command object tests in
1086
    test_remote.py.
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
1087
    """
1088
        
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
1089
    def test_hello(self):
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
1090
        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)
1091
        response = cmd.execute()
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1092
        self.assertEqual(('ok', '2'), response.args)
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
1093
        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
1094
        
1095
    def test_get_bundle(self):
1096
        from bzrlib.bundle import serializer
1097
        wt = self.make_branch_and_tree('.')
1910.19.13 by Andrew Bennetts
Address various review comments.
1098
        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
1099
        wt.add('hello')
1910.19.13 by Andrew Bennetts
Address various review comments.
1100
        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
1101
        
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
1102
        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)
1103
        response = cmd.execute('.', rev_id)
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
1104
        bundle = serializer.read_bundle(StringIO(response.body))
1105
        self.assertEqual((), response.args)
1106
1107
1108
class SmartServerRequestHandlerTests(tests.TestCaseWithTransport):
1109
    """Test that call directly into the handler logic, bypassing the network."""
1110
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)
1111
    def setUp(self):
1112
        super(SmartServerRequestHandlerTests, self).setUp()
2402.1.2 by Andrew Bennetts
Deal with review comments.
1113
        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)
1114
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
1115
    def build_handler(self, transport):
1116
        """Returns a handler for the commands in protocol version one."""
2018.5.160 by Andrew Bennetts
Merge from bzr.dev.
1117
        return request.SmartServerRequestHandler(transport,
1118
                                                 request.request_handlers)
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
1119
1120
    def test_construct_request_handler(self):
1121
        """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.
1122
        handler = request.SmartServerRequestHandler(None, None)
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
1123
        self.assertFalse(handler.finished_reading)
1124
1125
    def test_hello(self):
1126
        handler = self.build_handler(None)
1127
        handler.dispatch_command('hello', ())
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1128
        self.assertEqual(('ok', '2'), handler.response.args)
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
1129
        self.assertEqual(None, handler.response.body)
1130
        
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)
1131
    def test_disable_vfs_handler_classes_via_environment(self):
2018.5.140 by Andrew Bennetts
Merge from bzr.dev
1132
        # VFS handler classes will raise an error from "execute" if
1133
        # 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)
1134
        handler = vfs.HasRequest(None)
1135
        # set environment variable after construction to make sure it's
1136
        # examined.
2018.5.140 by Andrew Bennetts
Merge from bzr.dev
1137
        # Note that we can safely clobber BZR_NO_SMART_VFS here, because setUp
1138
        # 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)
1139
        # afterwards.
2402.1.2 by Andrew Bennetts
Deal with review comments.
1140
        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)
1141
        self.assertRaises(errors.DisabledMethod, handler.execute)
1142
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
1143
    def test_readonly_exception_becomes_transport_not_possible(self):
1144
        """The response for a read-only error is ('ReadOnlyError')."""
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
1145
        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`.
1146
        # send a mkdir for foo, with no explicit mode - should fail.
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1147
        handler.dispatch_command('mkdir', ('foo', ''))
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
1148
        # and the failure should be an explicit ReadOnlyError
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1149
        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`.
1150
        # XXX: TODO: test that other TransportNotPossible errors are
1151
        # presented as TransportNotPossible - not possible to do that
1152
        # until I figure out how to trigger that relatively cleanly via
1153
        # the api. RBC 20060918
1154
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1155
    def test_hello_has_finished_body_on_dispatch(self):
1156
        """The 'hello' command should set finished_reading."""
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
1157
        handler = self.build_handler(None)
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1158
        handler.dispatch_command('hello', ())
1159
        self.assertTrue(handler.finished_reading)
1160
        self.assertNotEqual(None, handler.response)
1161
1162
    def test_put_bytes_non_atomic(self):
1163
        """'put_...' should set finished_reading after reading the bytes."""
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
1164
        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
1165
        handler.dispatch_command('put_non_atomic', ('a-file', '', 'F', ''))
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1166
        self.assertFalse(handler.finished_reading)
1167
        handler.accept_body('1234')
1168
        self.assertFalse(handler.finished_reading)
1169
        handler.accept_body('5678')
1170
        handler.end_of_body()
1171
        self.assertTrue(handler.finished_reading)
1172
        self.assertEqual(('ok', ), handler.response.args)
1173
        self.assertEqual(None, handler.response.body)
1174
        
1175
    def test_readv_accept_body(self):
1176
        """'readv' should set finished_reading after reading offsets."""
1177
        self.build_tree(['a-file'])
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
1178
        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
1179
        handler.dispatch_command('readv', ('a-file', ))
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1180
        self.assertFalse(handler.finished_reading)
1181
        handler.accept_body('2,')
1182
        self.assertFalse(handler.finished_reading)
1183
        handler.accept_body('3')
1184
        handler.end_of_body()
1185
        self.assertTrue(handler.finished_reading)
1186
        self.assertEqual(('readv', ), handler.response.args)
1187
        # co - nte - nt of a-file is the file contents we are extracting from.
1188
        self.assertEqual('nte', handler.response.body)
1189
1190
    def test_readv_short_read_response_contents(self):
1191
        """'readv' when a short read occurs sets the response appropriately."""
1192
        self.build_tree(['a-file'])
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
1193
        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
1194
        handler.dispatch_command('readv', ('a-file', ))
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1195
        # read beyond the end of the file.
1196
        handler.accept_body('100,1')
1197
        handler.end_of_body()
1198
        self.assertTrue(handler.finished_reading)
1199
        self.assertEqual(('ShortReadvError', 'a-file', '100', '1', '0'),
1200
            handler.response.args)
1201
        self.assertEqual(None, handler.response.body)
1202
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.
1203
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)
1204
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.
1205
1206
    def test_registration(self):
1207
        t = get_transport('bzr+ssh://example.com/path')
2413.2.1 by Andrew Bennetts
Rename Smart.*Transport classes to RemoteTransport, RemoteTCPTransport, etc.
1208
        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.
1209
        self.assertEqual('example.com', t._host)
1210
1211
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)
1212
class TestRemoteTransport(tests.TestCase):
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
1213
        
1214
    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)
1215
        # 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.
1216
        input = StringIO("ok\n3\nbardone\n")
1217
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
1218
        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)
1219
        transport = remote.RemoteTransport(
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
1220
            'bzr://localhost/', medium=client_medium)
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
1221
1222
        # 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.
1223
        # method is called.  No data should have been sent, or read.
1224
        self.assertEqual(0, input.tell())
1225
        self.assertEqual('', output.getvalue())
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
1226
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1227
        # Now call a method that should result in a single request : as the
1228
        # transport makes its own protocol instances, we check on the wire.
1229
        # XXX: TODO: give the transport a protocol factory, which can make
1230
        # an instrumented protocol for us.
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
1231
        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.
1232
        # only the needed data should have been sent/received.
1233
        self.assertEqual(13, input.tell())
1234
        self.assertEqual('get\x01/foo\n', output.getvalue())
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
1235
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
1236
    def test__translate_error_readonly(self):
1237
        """Sending a ReadOnlyError to _translate_error raises TransportNotPossible."""
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
1238
        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)
1239
        transport = remote.RemoteTransport(
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
1240
            'bzr://localhost/', medium=client_medium)
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
1241
        self.assertRaises(errors.TransportNotPossible,
1242
            transport._translate_error, ("ReadOnlyError", ))
1243
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
1244
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
1245
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.
1246
    """A smart server which is backed by memory and saves its write requests."""
1247
1248
    def __init__(self, write_output_list):
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
1249
        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.
1250
        self._write_output_list = write_output_list
1251
1252
2432.2.5 by Andrew Bennetts
Reduce duplication in test_smart_transport.
1253
class TestSmartProtocol(tests.TestCase):
1254
    """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.
1255
1256
    Each test case gets a smart_server and smart_client created during setUp().
1257
1258
    It is planned that the client can be called with self.call_client() giving
1259
    it an expected server response, which will be fed into it when it tries to
1260
    read. Likewise, self.call_server will call a servers method with a canned
1261
    serialised client request. Output done by the client or server for these
1262
    calls will be captured to self.to_server and self.to_client. Each element
1263
    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.
1264
1265
    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.
1266
    """
1267
2432.2.5 by Andrew Bennetts
Reduce duplication in test_smart_transport.
1268
    client_protocol_class = None
1269
    server_protocol_class = None
1270
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.
1271
    def setUp(self):
2432.2.5 by Andrew Bennetts
Reduce duplication in test_smart_transport.
1272
        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.
1273
        # XXX: self.server_to_client doesn't seem to be used.  If so,
1274
        # InstrumentedServerProtocol is redundant too.
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1275
        self.server_to_client = []
1276
        self.to_server = StringIO()
1277
        self.to_client = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
1278
        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.
1279
            self.to_server)
2432.2.5 by Andrew Bennetts
Reduce duplication in test_smart_transport.
1280
        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.
1281
        self.smart_server = InstrumentedServerProtocol(self.server_to_client)
2402.1.1 by Andrew Bennetts
Use the Command pattern for handling smart server commands.
1282
        self.smart_server_request = request.SmartServerRequestHandler(
2018.5.23 by Andrew Bennetts
Use a Registry for smart server command handlers.
1283
            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.
1284
1285
    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.
1286
        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.
1287
        """Check that smart (de)serialises offsets as expected.
1288
        
1289
        We check both serialisation and deserialisation at the same time
1290
        to ensure that the round tripping cannot skew: both directions should
1291
        be as expected.
1292
        
1293
        :param expected_offsets: a readv offset list.
1294
        :param expected_seralised: an expected serial form of the offsets.
1295
        """
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.
1296
        # XXX: '_deserialise_offsets' should be a method of the
1297
        # SmartServerRequestProtocol in future.
2018.5.37 by Andrew Bennetts
Make sure all the request handlers in bzrlib/smart/vfs.py have consistent names.
1298
        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.
1299
        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.
1300
        self.assertEqual(expected_offsets, offsets)
1301
        serialised = client._serialise_offsets(offsets)
1302
        self.assertEqual(expected_serialised, serialised)
1303
2018.2.10 by Andrew Bennetts
Tidy up TODOs, further testing and fixes for SmartServerRequestProtocolOne, and remove a read_bytes(1) call.
1304
    def build_protocol_waiting_for_body(self):
2400.1.4 by Andrew Bennetts
Tidy up accidental changes.
1305
        out_stream = StringIO()
2432.2.5 by Andrew Bennetts
Reduce duplication in test_smart_transport.
1306
        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
1307
        smart_protocol.has_dispatched = True
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
1308
        smart_protocol.request = self.smart_server_request
2018.5.7 by Andrew Bennetts
Simplify dispatch_command.
1309
        class FakeCommand(object):
1310
            def do_body(cmd, body_bytes):
1311
                self.end_received = True
1312
                self.assertEqual('abcdefg', body_bytes)
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
1313
                return request.SuccessfulSmartServerResponse(('ok', ))
2018.5.12 by Andrew Bennetts
Rename SmartServerRequestHandler's command attribute to _command; it's private.
1314
        smart_protocol.request._command = FakeCommand()
2400.1.4 by Andrew Bennetts
Tidy up accidental changes.
1315
        # Call accept_bytes to make sure that internal state like _body_decoder
1316
        # is initialised.  This test should probably be given a clearer
1317
        # interface to work with that will not cause this inconsistency.
1318
        #   -- 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
1319
        smart_protocol.accept_bytes('')
1320
        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.
1321
2432.2.5 by Andrew Bennetts
Reduce duplication in test_smart_transport.
1322
    def assertServerToClientEncoding(self, expected_bytes, expected_tuple,
1323
            input_tuples):
1324
        """Assert that each input_tuple serialises as expected_bytes, and the
1325
        bytes deserialise as expected_tuple.
1326
        """
1327
        # check the encoding of the server for all input_tuples matches
1328
        # expected bytes
1329
        for input_tuple in input_tuples:
1330
            server_output = StringIO()
1331
            server_protocol = self.server_protocol_class(
1332
                None, server_output.write)
2432.4.4 by Robert Collins
Merge hpss-protocol2.
1333
            server_protocol._send_response(
1334
                _mod_request.SuccessfulSmartServerResponse(input_tuple))
2432.2.5 by Andrew Bennetts
Reduce duplication in test_smart_transport.
1335
            self.assertEqual(expected_bytes, server_output.getvalue())
1336
        # check the decoding of the client smart_protocol from expected_bytes:
1337
        input = StringIO(expected_bytes)
1338
        output = StringIO()
1339
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
1340
        request = client_medium.get_request()
1341
        smart_protocol = self.client_protocol_class(request)
1342
        smart_protocol.call('foo')
1343
        self.assertEqual(expected_tuple, smart_protocol.read_response_tuple())
1344
1345
2621.3.1 by Andrew Bennetts
Log errors from the smart server in the trace file, to make debugging test failures (and live failures!) easier.
1346
class CommonSmartProtocolTestMixin(object):
1347
1348
    def test_errors_are_logged(self):
1349
        """If an error occurs during testing, it is logged to the test log."""
1350
        out_stream = StringIO()
1351
        smart_protocol = self.server_protocol_class(None, out_stream.write)
1352
        # This triggers a "bad request" error.
1353
        smart_protocol.accept_bytes('abc\n')
1354
        test_log = self._get_log(keep_log_file=True)
1355
        self.assertContainsRe(test_log, 'Traceback')
1356
        self.assertContainsRe(test_log, 'SmartProtocolError')
1357
1358
1359
class TestSmartProtocolOne(TestSmartProtocol, CommonSmartProtocolTestMixin):
2432.2.5 by Andrew Bennetts
Reduce duplication in test_smart_transport.
1360
    """Tests for the smart protocol version one."""
1361
1362
    client_protocol_class = protocol.SmartClientRequestProtocolOne
1363
    server_protocol_class = protocol.SmartServerRequestProtocolOne
1364
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1365
    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
1366
        smart_protocol = protocol.SmartServerRequestProtocolOne(None, None)
1367
        self.assertEqual('', smart_protocol.excess_buffer)
1368
        self.assertEqual('', smart_protocol.in_buffer)
1369
        self.assertFalse(smart_protocol.has_dispatched)
1370
        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.
1371
1372
    def test_construct_version_one_client_protocol(self):
1373
        # we can construct a client protocol from a client medium request
1374
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
1375
        client_medium = medium.SmartSimplePipesClientMedium(None, output)
1376
        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
1377
        client_protocol = protocol.SmartClientRequestProtocolOne(request)
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1378
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.
1379
    def test_server_offset_serialisation(self):
1380
        """The Smart protocol serialises offsets as a comma and \n string.
1381
1382
        We check a number of boundary cases are as expected: empty, one offset,
1383
        one with the order of reads not increasing (an out of order read), and
1384
        one that should coalesce.
1385
        """
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.
1386
        self.assertOffsetSerialisation([], '', self.client_protocol)
1387
        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.
1388
        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.
1389
            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.
1390
        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.
1391
            '1,2\n3,4\n100,200', self.client_protocol)
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1392
2018.3.2 by Andrew Bennetts
Ensure that a request's next_read_size() is 0 once an error response is sent.
1393
    def test_accept_bytes_of_bad_request_to_protocol(self):
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1394
        out_stream = StringIO()
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1395
        smart_protocol = protocol.SmartServerRequestProtocolOne(
1396
            None, out_stream.write)
1397
        smart_protocol.accept_bytes('abc')
1398
        self.assertEqual('abc', smart_protocol.in_buffer)
1399
        smart_protocol.accept_bytes('\n')
1400
        self.assertEqual(
1401
            "error\x01Generic bzr smart protocol error: bad request 'abc'\n",
1402
            out_stream.getvalue())
1403
        self.assertTrue(smart_protocol.has_dispatched)
1404
        self.assertEqual(0, smart_protocol.next_read_size())
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
1405
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1406
    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.
1407
        protocol = self.build_protocol_waiting_for_body()
2018.2.15 by Andrew Bennetts
Remove SmartServerRequestProtocolOne.finished_reading attribute, replace with next_read_size method.
1408
        self.assertEqual(6, protocol.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1409
        protocol.accept_bytes('7\nabc')
2018.2.15 by Andrew Bennetts
Remove SmartServerRequestProtocolOne.finished_reading attribute, replace with next_read_size method.
1410
        self.assertEqual(9, protocol.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1411
        protocol.accept_bytes('defgd')
1412
        protocol.accept_bytes('one\n')
2018.2.15 by Andrew Bennetts
Remove SmartServerRequestProtocolOne.finished_reading attribute, replace with next_read_size method.
1413
        self.assertEqual(0, protocol.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1414
        self.assertTrue(self.end_received)
1415
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
1416
    def test_accept_request_and_body_all_at_once(self):
2402.1.2 by Andrew Bennetts
Deal with review comments.
1417
        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.
1418
        mem_transport = memory.MemoryTransport()
1419
        mem_transport.put_bytes('foo', 'abcdefghij')
1420
        out_stream = StringIO()
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1421
        smart_protocol = protocol.SmartServerRequestProtocolOne(mem_transport,
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
1422
                out_stream.write)
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1423
        smart_protocol.accept_bytes('readv\x01foo\n3\n3,3done\n')
1424
        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.
1425
        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
1426
        self.assertEqual('', smart_protocol.excess_buffer)
1427
        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.
1428
2018.2.10 by Andrew Bennetts
Tidy up TODOs, further testing and fixes for SmartServerRequestProtocolOne, and remove a read_bytes(1) call.
1429
    def test_accept_excess_bytes_are_preserved(self):
1430
        out_stream = StringIO()
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1431
        smart_protocol = protocol.SmartServerRequestProtocolOne(
1432
            None, out_stream.write)
1433
        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.
1434
        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
1435
        self.assertEqual("hello\n", smart_protocol.excess_buffer)
1436
        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.
1437
1438
    def test_accept_excess_bytes_after_body(self):
1439
        protocol = self.build_protocol_waiting_for_body()
1440
        protocol.accept_bytes('7\nabcdefgdone\nX')
1441
        self.assertTrue(self.end_received)
1442
        self.assertEqual("X", protocol.excess_buffer)
1443
        self.assertEqual("", protocol.in_buffer)
1444
        protocol.accept_bytes('Y')
1445
        self.assertEqual("XY", protocol.excess_buffer)
1446
        self.assertEqual("", protocol.in_buffer)
1447
1448
    def test_accept_excess_bytes_after_dispatch(self):
1449
        out_stream = StringIO()
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1450
        smart_protocol = protocol.SmartServerRequestProtocolOne(
1451
            None, out_stream.write)
1452
        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.
1453
        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
1454
        smart_protocol.accept_bytes('hel')
1455
        self.assertEqual("hel", smart_protocol.excess_buffer)
1456
        smart_protocol.accept_bytes('lo\n')
1457
        self.assertEqual("hello\n", smart_protocol.excess_buffer)
1458
        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.
1459
2018.3.2 by Andrew Bennetts
Ensure that a request's next_read_size() is 0 once an error response is sent.
1460
    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
1461
        smart_protocol = protocol.SmartServerRequestProtocolOne(
1462
            None, lambda x: None)
1463
        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.
1464
        smart_protocol._send_response(
1465
            request.SuccessfulSmartServerResponse(('x',)))
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1466
        self.assertEqual(0, smart_protocol.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1467
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
1468
    def test__send_response_errors_with_base_response(self):
1469
        """Ensure that only the Successful/Failed subclasses are used."""
1470
        smart_protocol = protocol.SmartServerRequestProtocolOne(
1471
            None, lambda x: None)
1472
        self.assertRaises(AttributeError, smart_protocol._send_response,
1473
            request.SmartServerResponse(('x',)))
1474
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
1475
    def test_query_version(self):
1476
        """query_version on a SmartClientProtocolOne should return a number.
1477
        
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1478
        The protocol provides the query_version because the domain level clients
1479
        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.
1480
        """
1481
        # What we really want to test here is that SmartClientProtocolOne calls
1482
        # accept_bytes(tuple_based_encoding_of_hello) and reads and parses the
1483
        # 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.
1484
        # 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.
1485
        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.
1486
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
1487
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
1488
        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
1489
        smart_protocol = protocol.SmartClientRequestProtocolOne(request)
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1490
        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.
1491
1492
    def test_client_call_empty_response(self):
1493
        # protocol.call() can get back an empty tuple as a response. This occurs
1494
        # when the parsed line is an empty line, and results in a tuple with
1495
        # one element - an empty string.
1496
        self.assertServerToClientEncoding('\n', ('', ), [(), ('', )])
1497
1498
    def test_client_call_three_element_response(self):
1499
        # protocol.call() can get back tuples of other lengths. A three element
1500
        # tuple should be unpacked as three strings.
1501
        self.assertServerToClientEncoding('a\x01b\x0134\n', ('a', 'b', '34'),
1502
            [('a', 'b', '34')])
1503
1504
    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.
1505
        # 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.
1506
        # wire.
1507
        expected_bytes = "foo\n7\nabcdefgdone\n"
1508
        input = StringIO("\n")
1509
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
1510
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
1511
        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
1512
        smart_protocol = protocol.SmartClientRequestProtocolOne(request)
1513
        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.
1514
        self.assertEqual(expected_bytes, output.getvalue())
1515
1516
    def test_client_call_with_body_readv_array(self):
1517
        # protocol.call_with_upload should encode the readv array and then
1518
        # length-prefix the bytes onto the wire.
1519
        expected_bytes = "foo\n7\n1,2\n5,6done\n"
1520
        input = StringIO("\n")
1521
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
1522
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
1523
        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
1524
        smart_protocol = protocol.SmartClientRequestProtocolOne(request)
1525
        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.
1526
        self.assertEqual(expected_bytes, output.getvalue())
1527
1528
    def test_client_read_body_bytes_all(self):
1529
        # read_body_bytes should decode the body bytes from the wire into
1530
        # a response.
1531
        expected_bytes = "1234567"
1532
        server_bytes = "ok\n7\n1234567done\n"
1533
        input = StringIO(server_bytes)
1534
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
1535
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
1536
        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
1537
        smart_protocol = protocol.SmartClientRequestProtocolOne(request)
1538
        smart_protocol.call('foo')
1539
        smart_protocol.read_response_tuple(True)
1540
        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.
1541
1542
    def test_client_read_body_bytes_incremental(self):
1543
        # test reading a few bytes at a time from the body
1544
        # 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.
1545
        # 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.
1546
        # LengthPrefixedBodyDecoder that is already well tested - we can skip
1547
        # that.
1548
        expected_bytes = "1234567"
1549
        server_bytes = "ok\n7\n1234567done\n"
1550
        input = StringIO(server_bytes)
1551
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
1552
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
1553
        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
1554
        smart_protocol = protocol.SmartClientRequestProtocolOne(request)
1555
        smart_protocol.call('foo')
1556
        smart_protocol.read_response_tuple(True)
1557
        self.assertEqual(expected_bytes[0:2], smart_protocol.read_body_bytes(2))
1558
        self.assertEqual(expected_bytes[2:4], smart_protocol.read_body_bytes(2))
1559
        self.assertEqual(expected_bytes[4:6], smart_protocol.read_body_bytes(2))
1560
        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.
1561
1562
    def test_client_cancel_read_body_does_not_eat_body_bytes(self):
1563
        # cancelling the expected body needs to finish the request, but not
1564
        # read any more bytes.
1565
        expected_bytes = "1234567"
1566
        server_bytes = "ok\n7\n1234567done\n"
1567
        input = StringIO(server_bytes)
1568
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
1569
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
1570
        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
1571
        smart_protocol = protocol.SmartClientRequestProtocolOne(request)
1572
        smart_protocol.call('foo')
1573
        smart_protocol.read_response_tuple(True)
1574
        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.
1575
        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
1576
        self.assertRaises(
1577
            errors.ReadingCompleted, smart_protocol.read_body_bytes)
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1578
2018.2.15 by Andrew Bennetts
Remove SmartServerRequestProtocolOne.finished_reading attribute, replace with next_read_size method.
1579
2621.3.1 by Andrew Bennetts
Log errors from the smart server in the trace file, to make debugging test failures (and live failures!) easier.
1580
class TestSmartProtocolTwo(TestSmartProtocol, CommonSmartProtocolTestMixin):
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1581
    """Tests for the smart protocol version two.
1582
1583
    This test case is mostly the same as TestSmartProtocolOne.
1584
    """
1585
2432.2.5 by Andrew Bennetts
Reduce duplication in test_smart_transport.
1586
    client_protocol_class = protocol.SmartClientRequestProtocolTwo
1587
    server_protocol_class = protocol.SmartServerRequestProtocolTwo
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1588
1589
    def test_construct_version_two_server_protocol(self):
1590
        smart_protocol = protocol.SmartServerRequestProtocolTwo(None, None)
1591
        self.assertEqual('', smart_protocol.excess_buffer)
1592
        self.assertEqual('', smart_protocol.in_buffer)
1593
        self.assertFalse(smart_protocol.has_dispatched)
1594
        self.assertEqual(1, smart_protocol.next_read_size())
1595
1596
    def test_construct_version_two_client_protocol(self):
1597
        # we can construct a client protocol from a client medium request
1598
        output = StringIO()
1599
        client_medium = medium.SmartSimplePipesClientMedium(None, output)
1600
        request = client_medium.get_request()
1601
        client_protocol = protocol.SmartClientRequestProtocolTwo(request)
1602
1603
    def test_server_offset_serialisation(self):
1604
        """The Smart protocol serialises offsets as a comma and \n string.
1605
1606
        We check a number of boundary cases are as expected: empty, one offset,
1607
        one with the order of reads not increasing (an out of order read), and
1608
        one that should coalesce.
1609
        """
1610
        self.assertOffsetSerialisation([], '', self.client_protocol)
1611
        self.assertOffsetSerialisation([(1,2)], '1,2', self.client_protocol)
1612
        self.assertOffsetSerialisation([(10,40), (0,5)], '10,40\n0,5',
1613
            self.client_protocol)
1614
        self.assertOffsetSerialisation([(1,2), (3,4), (100, 200)],
1615
            '1,2\n3,4\n100,200', self.client_protocol)
1616
1617
    def test_accept_bytes_of_bad_request_to_protocol(self):
1618
        out_stream = StringIO()
1619
        smart_protocol = protocol.SmartServerRequestProtocolTwo(
1620
            None, out_stream.write)
1621
        smart_protocol.accept_bytes('abc')
1622
        self.assertEqual('abc', smart_protocol.in_buffer)
1623
        smart_protocol.accept_bytes('\n')
1624
        self.assertEqual(
2432.2.7 by Andrew Bennetts
Use less confusing version strings, and define REQUEST_VERSION_TWO/RESPONSE_VERSION_TWO constants for them.
1625
            protocol.RESPONSE_VERSION_TWO +
2432.3.5 by Andrew Bennetts
Merge Robert's status prefix changes to protocol 2.
1626
            "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.
1627
            out_stream.getvalue())
1628
        self.assertTrue(smart_protocol.has_dispatched)
1629
        self.assertEqual(0, smart_protocol.next_read_size())
1630
1631
    def test_accept_body_bytes_to_protocol(self):
1632
        protocol = self.build_protocol_waiting_for_body()
1633
        self.assertEqual(6, protocol.next_read_size())
1634
        protocol.accept_bytes('7\nabc')
1635
        self.assertEqual(9, protocol.next_read_size())
1636
        protocol.accept_bytes('defgd')
1637
        protocol.accept_bytes('one\n')
1638
        self.assertEqual(0, protocol.next_read_size())
1639
        self.assertTrue(self.end_received)
1640
1641
    def test_accept_request_and_body_all_at_once(self):
1642
        self._captureVar('BZR_NO_SMART_VFS', None)
1643
        mem_transport = memory.MemoryTransport()
1644
        mem_transport.put_bytes('foo', 'abcdefghij')
1645
        out_stream = StringIO()
1646
        smart_protocol = protocol.SmartServerRequestProtocolTwo(mem_transport,
1647
                out_stream.write)
1648
        smart_protocol.accept_bytes('readv\x01foo\n3\n3,3done\n')
1649
        self.assertEqual(0, smart_protocol.next_read_size())
2432.3.5 by Andrew Bennetts
Merge Robert's status prefix changes to protocol 2.
1650
        self.assertEqual(protocol.RESPONSE_VERSION_TWO +
1651
                         '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.
1652
                         out_stream.getvalue())
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1653
        self.assertEqual('', smart_protocol.excess_buffer)
1654
        self.assertEqual('', smart_protocol.in_buffer)
1655
1656
    def test_accept_excess_bytes_are_preserved(self):
1657
        out_stream = StringIO()
1658
        smart_protocol = protocol.SmartServerRequestProtocolTwo(
1659
            None, out_stream.write)
1660
        smart_protocol.accept_bytes('hello\nhello\n')
2432.3.5 by Andrew Bennetts
Merge Robert's status prefix changes to protocol 2.
1661
        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.
1662
                         out_stream.getvalue())
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1663
        self.assertEqual("hello\n", smart_protocol.excess_buffer)
1664
        self.assertEqual("", smart_protocol.in_buffer)
1665
1666
    def test_accept_excess_bytes_after_body(self):
1667
        # 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.
1668
        server_protocol = self.build_protocol_waiting_for_body()
1669
        server_protocol.accept_bytes(
1670
            '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.
1671
        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.
1672
        self.assertEqual(protocol.RESPONSE_VERSION_TWO,
1673
                         server_protocol.excess_buffer)
1674
        self.assertEqual("", server_protocol.in_buffer)
1675
        server_protocol.accept_bytes('Y')
1676
        self.assertEqual(protocol.RESPONSE_VERSION_TWO + "Y",
1677
                         server_protocol.excess_buffer)
1678
        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.
1679
1680
    def test_accept_excess_bytes_after_dispatch(self):
1681
        out_stream = StringIO()
1682
        smart_protocol = protocol.SmartServerRequestProtocolTwo(
1683
            None, out_stream.write)
1684
        smart_protocol.accept_bytes('hello\n')
2432.3.5 by Andrew Bennetts
Merge Robert's status prefix changes to protocol 2.
1685
        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.
1686
                         out_stream.getvalue())
1687
        smart_protocol.accept_bytes(protocol.REQUEST_VERSION_TWO + 'hel')
1688
        self.assertEqual(protocol.REQUEST_VERSION_TWO + "hel",
1689
                         smart_protocol.excess_buffer)
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1690
        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.
1691
        self.assertEqual(protocol.REQUEST_VERSION_TWO + "hello\n",
1692
                         smart_protocol.excess_buffer)
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1693
        self.assertEqual("", smart_protocol.in_buffer)
1694
1695
    def test__send_response_sets_finished_reading(self):
1696
        smart_protocol = protocol.SmartServerRequestProtocolTwo(
1697
            None, lambda x: None)
1698
        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.
1699
        smart_protocol._send_response(
1700
            request.SuccessfulSmartServerResponse(('x',)))
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1701
        self.assertEqual(0, smart_protocol.next_read_size())
1702
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
1703
    def test__send_response_errors_with_base_response(self):
1704
        """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.
1705
        smart_protocol = protocol.SmartServerRequestProtocolTwo(
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
1706
            None, lambda x: None)
1707
        self.assertRaises(AttributeError, smart_protocol._send_response,
1708
            request.SmartServerResponse(('x',)))
1709
2432.4.6 by Robert Collins
Include success/failure feedback in SmartProtocolTwo responses to allow robust handling in the future.
1710
    def test__send_response_includes_failure_marker(self):
1711
        """FailedSmartServerResponse have 'failed\n' after the version."""
1712
        out_stream = StringIO()
1713
        smart_protocol = protocol.SmartServerRequestProtocolTwo(
1714
            None, out_stream.write)
1715
        smart_protocol._send_response(
1716
            request.FailedSmartServerResponse(('x',)))
2432.3.6 by Andrew Bennetts
Fix a couple of test failures introduced by the previous merge.
1717
        self.assertEqual(protocol.RESPONSE_VERSION_TWO + 'failed\nx\n',
1718
                         out_stream.getvalue())
2432.4.6 by Robert Collins
Include success/failure feedback in SmartProtocolTwo responses to allow robust handling in the future.
1719
1720
    def test__send_response_includes_success_marker(self):
1721
        """SuccessfulSmartServerResponse have 'success\n' after the version."""
1722
        out_stream = StringIO()
1723
        smart_protocol = protocol.SmartServerRequestProtocolTwo(
1724
            None, out_stream.write)
1725
        smart_protocol._send_response(
1726
            request.SuccessfulSmartServerResponse(('x',)))
2432.3.6 by Andrew Bennetts
Fix a couple of test failures introduced by the previous merge.
1727
        self.assertEqual(protocol.RESPONSE_VERSION_TWO + 'success\nx\n',
1728
                         out_stream.getvalue())
2432.4.6 by Robert Collins
Include success/failure feedback in SmartProtocolTwo responses to allow robust handling in the future.
1729
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1730
    def test_query_version(self):
1731
        """query_version on a SmartClientProtocolTwo should return a number.
1732
        
1733
        The protocol provides the query_version because the domain level clients
1734
        may all need to be able to probe for capabilities.
1735
        """
1736
        # What we really want to test here is that SmartClientProtocolTwo calls
1737
        # accept_bytes(tuple_based_encoding_of_hello) and reads and parses the
1738
        # response of tuple-encoded (ok, 1).  Also, seperately we should test
1739
        # 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.
1740
        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.
1741
        output = StringIO()
1742
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
1743
        request = client_medium.get_request()
1744
        smart_protocol = protocol.SmartClientRequestProtocolTwo(request)
1745
        self.assertEqual(2, smart_protocol.query_version())
1746
1747
    def test_client_call_empty_response(self):
1748
        # protocol.call() can get back an empty tuple as a response. This occurs
1749
        # when the parsed line is an empty line, and results in a tuple with
1750
        # 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.
1751
        self.assertServerToClientEncoding(
2432.3.5 by Andrew Bennetts
Merge Robert's status prefix changes to protocol 2.
1752
            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.
1753
2432.2.5 by Andrew Bennetts
Reduce duplication in test_smart_transport.
1754
    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.
1755
        # protocol.call() can get back tuples of other lengths. A three element
1756
        # 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.
1757
        self.assertServerToClientEncoding(
2432.3.5 by Andrew Bennetts
Merge Robert's status prefix changes to protocol 2.
1758
            protocol.RESPONSE_VERSION_TWO + 'success\na\x01b\x0134\n',
1759
            ('a', 'b', '34'),
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1760
            [('a', 'b', '34')])
1761
1762
    def test_client_call_with_body_bytes_uploads(self):
1763
        # protocol.call_with_body_bytes should length-prefix the bytes onto the
1764
        # wire.
2432.2.7 by Andrew Bennetts
Use less confusing version strings, and define REQUEST_VERSION_TWO/RESPONSE_VERSION_TWO constants for them.
1765
        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.
1766
        input = StringIO("\n")
1767
        output = StringIO()
1768
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
1769
        request = client_medium.get_request()
1770
        smart_protocol = protocol.SmartClientRequestProtocolTwo(request)
1771
        smart_protocol.call_with_body_bytes(('foo', ), "abcdefg")
1772
        self.assertEqual(expected_bytes, output.getvalue())
1773
1774
    def test_client_call_with_body_readv_array(self):
1775
        # protocol.call_with_upload should encode the readv array and then
1776
        # 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.
1777
        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.
1778
        input = StringIO("\n")
1779
        output = StringIO()
1780
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
1781
        request = client_medium.get_request()
1782
        smart_protocol = protocol.SmartClientRequestProtocolTwo(request)
1783
        smart_protocol.call_with_body_readv_array(('foo', ), [(1,2),(5,6)])
1784
        self.assertEqual(expected_bytes, output.getvalue())
1785
2432.4.6 by Robert Collins
Include success/failure feedback in SmartProtocolTwo responses to allow robust handling in the future.
1786
    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.
1787
        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.
1788
        input = StringIO(server_bytes)
1789
        output = StringIO()
1790
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
1791
        request = client_medium.get_request()
1792
        smart_protocol = protocol.SmartClientRequestProtocolTwo(request)
1793
        smart_protocol.call('foo')
1794
        smart_protocol.read_response_tuple(False)
1795
        self.assertEqual(True, smart_protocol.response_status)
1796
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1797
    def test_client_read_body_bytes_all(self):
1798
        # read_body_bytes should decode the body bytes from the wire into
1799
        # a response.
1800
        expected_bytes = "1234567"
2432.3.5 by Andrew Bennetts
Merge Robert's status prefix changes to protocol 2.
1801
        server_bytes = (protocol.RESPONSE_VERSION_TWO +
1802
                        "success\nok\n7\n1234567done\n")
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1803
        input = StringIO(server_bytes)
1804
        output = StringIO()
1805
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
1806
        request = client_medium.get_request()
1807
        smart_protocol = protocol.SmartClientRequestProtocolTwo(request)
1808
        smart_protocol.call('foo')
1809
        smart_protocol.read_response_tuple(True)
1810
        self.assertEqual(expected_bytes, smart_protocol.read_body_bytes())
1811
1812
    def test_client_read_body_bytes_incremental(self):
1813
        # test reading a few bytes at a time from the body
1814
        # XXX: possibly we should test dribbling the bytes into the stringio
1815
        # to make the state machine work harder: however, as we use the
1816
        # LengthPrefixedBodyDecoder that is already well tested - we can skip
1817
        # that.
1818
        expected_bytes = "1234567"
2432.3.5 by Andrew Bennetts
Merge Robert's status prefix changes to protocol 2.
1819
        server_bytes = (protocol.RESPONSE_VERSION_TWO +
1820
                        "success\nok\n7\n1234567done\n")
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1821
        input = StringIO(server_bytes)
1822
        output = StringIO()
1823
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
1824
        request = client_medium.get_request()
1825
        smart_protocol = protocol.SmartClientRequestProtocolTwo(request)
1826
        smart_protocol.call('foo')
1827
        smart_protocol.read_response_tuple(True)
1828
        self.assertEqual(expected_bytes[0:2], smart_protocol.read_body_bytes(2))
1829
        self.assertEqual(expected_bytes[2:4], smart_protocol.read_body_bytes(2))
1830
        self.assertEqual(expected_bytes[4:6], smart_protocol.read_body_bytes(2))
1831
        self.assertEqual(expected_bytes[6], smart_protocol.read_body_bytes())
1832
1833
    def test_client_cancel_read_body_does_not_eat_body_bytes(self):
1834
        # cancelling the expected body needs to finish the request, but not
1835
        # read any more bytes.
1836
        expected_bytes = "1234567"
2432.3.5 by Andrew Bennetts
Merge Robert's status prefix changes to protocol 2.
1837
        server_bytes = (protocol.RESPONSE_VERSION_TWO +
1838
                        "success\nok\n7\n1234567done\n")
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1839
        input = StringIO(server_bytes)
1840
        output = StringIO()
1841
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
1842
        request = client_medium.get_request()
1843
        smart_protocol = protocol.SmartClientRequestProtocolTwo(request)
1844
        smart_protocol.call('foo')
1845
        smart_protocol.read_response_tuple(True)
1846
        smart_protocol.cancel_read_body()
2432.3.5 by Andrew Bennetts
Merge Robert's status prefix changes to protocol 2.
1847
        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.
1848
                         input.tell())
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
1849
        self.assertRaises(
1850
            errors.ReadingCompleted, smart_protocol.read_body_bytes)
1851
1852
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.
1853
class TestSmartClientUnicode(tests.TestCase):
2414.1.4 by Andrew Bennetts
Rename SmartClient to _SmartClient.
1854
    """_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.
1855
1856
    Unicode arguments to call_with_body_bytes are not correct (remote method
1857
    names, arguments, and bodies must all be expressed as byte strings), but
2414.1.4 by Andrew Bennetts
Rename SmartClient to _SmartClient.
1858
    _SmartClient should gracefully reject them, rather than getting into a
1859
    broken state that prevents future correct calls from working.  That is, it
1860
    should be possible to issue more requests on the medium afterwards, rather
1861
    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.
1862
    mysteriously fail with TooManyConcurrentRequests.
1863
    """
1864
1865
    def assertCallDoesNotBreakMedium(self, method, args, body):
1866
        """Call a medium with the given method, args and body, then assert that
1867
        the medium is left in a sane state, i.e. is capable of allowing further
1868
        requests.
1869
        """
1870
        input = StringIO("\n")
1871
        output = StringIO()
1872
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
2414.1.4 by Andrew Bennetts
Rename SmartClient to _SmartClient.
1873
        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.
1874
        self.assertRaises(TypeError,
1875
            smart_client.call_with_body_bytes, method, args, body)
1876
        self.assertEqual("", output.getvalue())
1877
        self.assertEqual(None, client_medium._current_request)
1878
1879
    def test_call_with_body_bytes_unicode_method(self):
1880
        self.assertCallDoesNotBreakMedium(u'method', ('args',), 'body')
1881
1882
    def test_call_with_body_bytes_unicode_args(self):
1883
        self.assertCallDoesNotBreakMedium('method', (u'args',), 'body')
2414.1.2 by Andrew Bennetts
Deal with review comments.
1884
        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.
1885
1886
    def test_call_with_body_bytes_unicode_body(self):
1887
        self.assertCallDoesNotBreakMedium('method', ('args',), u'body')
1888
1889
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1890
class LengthPrefixedBodyDecoder(tests.TestCase):
1891
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1892
    # XXX: TODO: make accept_reading_trailer invoke translate_response or 
1893
    # something similar to the ProtocolBase method.
1894
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1895
    def test_construct(self):
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1896
        decoder = protocol.LengthPrefixedBodyDecoder()
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1897
        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.
1898
        self.assertEqual(6, decoder.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1899
        self.assertEqual('', decoder.read_pending_data())
1900
        self.assertEqual('', decoder.unused_data)
1901
1902
    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
1903
        decoder = protocol.LengthPrefixedBodyDecoder()
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1904
        decoder.accept_bytes('')
1905
        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.
1906
        self.assertEqual(6, decoder.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1907
        self.assertEqual('', decoder.read_pending_data())
1908
        self.assertEqual('', decoder.unused_data)
1909
        decoder.accept_bytes('7')
1910
        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.
1911
        self.assertEqual(6, decoder.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1912
        self.assertEqual('', decoder.read_pending_data())
1913
        self.assertEqual('', decoder.unused_data)
1914
        decoder.accept_bytes('\na')
1915
        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.
1916
        self.assertEqual(11, decoder.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1917
        self.assertEqual('a', decoder.read_pending_data())
1918
        self.assertEqual('', decoder.unused_data)
1919
        decoder.accept_bytes('bcdefgd')
1920
        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.
1921
        self.assertEqual(4, decoder.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1922
        self.assertEqual('bcdefg', decoder.read_pending_data())
1923
        self.assertEqual('', decoder.unused_data)
1924
        decoder.accept_bytes('one')
1925
        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.
1926
        self.assertEqual(1, decoder.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1927
        self.assertEqual('', decoder.read_pending_data())
1928
        self.assertEqual('', decoder.unused_data)
1929
        decoder.accept_bytes('\nblarg')
1930
        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.
1931
        self.assertEqual(1, decoder.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1932
        self.assertEqual('', decoder.read_pending_data())
1933
        self.assertEqual('blarg', decoder.unused_data)
1934
        
1935
    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
1936
        decoder = protocol.LengthPrefixedBodyDecoder()
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1937
        decoder.accept_bytes('1\nadone\nunused')
1938
        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.
1939
        self.assertEqual(1, decoder.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1940
        self.assertEqual('a', decoder.read_pending_data())
1941
        self.assertEqual('unused', decoder.unused_data)
1942
1943
    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
1944
        decoder = protocol.LengthPrefixedBodyDecoder()
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1945
        decoder.accept_bytes('1\na')
1946
        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.
1947
        self.assertEqual(5, decoder.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1948
        self.assertEqual('a', decoder.read_pending_data())
1949
        self.assertEqual('', decoder.unused_data)
1950
        decoder.accept_bytes('done\n')
1951
        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.
1952
        self.assertEqual(1, decoder.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1953
        self.assertEqual('', decoder.read_pending_data())
1954
        self.assertEqual('', decoder.unused_data)
1955
1956
2432.4.1 by Robert Collins
Add SuccessfulSmartServerResponse.
1957
class TestSuccessfulSmartServerResponse(tests.TestCase):
1958
1959
    def test_construct(self):
1960
        response = request.SuccessfulSmartServerResponse(('foo', 'bar'))
1961
        self.assertEqual(('foo', 'bar'), response.args)
1962
        self.assertEqual(None, response.body)
1963
        response = request.SuccessfulSmartServerResponse(('foo', 'bar'), 'bytes')
1964
        self.assertEqual(('foo', 'bar'), response.args)
1965
        self.assertEqual('bytes', response.body)
1966
1967
    def test_is_successful(self):
1968
        """is_successful should return True for SuccessfulSmartServerResponse."""
1969
        response = request.SuccessfulSmartServerResponse(('error',))
1970
        self.assertEqual(True, response.is_successful())
1971
1972
2432.4.2 by Robert Collins
Add FailedSmartServerResponse.
1973
class TestFailedSmartServerResponse(tests.TestCase):
1974
1975
    def test_construct(self):
1976
        response = request.FailedSmartServerResponse(('foo', 'bar'))
1977
        self.assertEqual(('foo', 'bar'), response.args)
1978
        self.assertEqual(None, response.body)
1979
        response = request.FailedSmartServerResponse(('foo', 'bar'), 'bytes')
1980
        self.assertEqual(('foo', 'bar'), response.args)
1981
        self.assertEqual('bytes', response.body)
1982
1983
    def test_is_successful(self):
1984
        """is_successful should return False for FailedSmartServerResponse."""
1985
        response = request.FailedSmartServerResponse(('error',))
1986
        self.assertEqual(False, response.is_successful())
1987
2432.4.1 by Robert Collins
Add SuccessfulSmartServerResponse.
1988
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
1989
class FakeHTTPMedium(object):
1990
    def __init__(self):
1991
        self.written_request = None
1992
        self._current_request = None
2018.2.8 by Andrew Bennetts
Make HttpTransportBase.get_smart_client return self again.
1993
    def send_http_smart_request(self, bytes):
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
1994
        self.written_request = bytes
1995
        return None
1996
1997
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1998
class HTTPTunnellingSmokeTest(tests.TestCaseWithTransport):
1999
    
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)
2000
    def setUp(self):
2001
        super(HTTPTunnellingSmokeTest, self).setUp()
2002
        # We use the VFS layer as part of HTTP tunnelling tests.
2402.1.2 by Andrew Bennetts
Deal with review comments.
2003
        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)
2004
2018.2.7 by Andrew Bennetts
Implement _post on HttpTransport_urllib.
2005
    def _test_bulk_data(self, url_protocol):
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
2006
        # We should be able to send and receive bulk data in a single message.
2007
        # The 'readv' command in the smart protocol both sends and receives bulk
2008
        # data, so we use that.
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
2009
        self.build_tree(['data-file'])
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
2010
        self.transport_readonly_server = HTTPServerWithSmarts
2011
2012
        http_transport = self.get_readonly_transport()
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
2013
        medium = http_transport.get_smart_medium()
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
2014
        #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)
2015
        remote_transport = remote.RemoteTransport('/', medium=medium)
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
2016
        self.assertEqual(
2017
            [(0, "c")], list(remote_transport.readv("data-file", [(0,1)])))
2018
2018.2.7 by Andrew Bennetts
Implement _post on HttpTransport_urllib.
2019
    def test_bulk_data_pycurl(self):
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
2020
        try:
2021
            self._test_bulk_data('http+pycurl')
2022
        except errors.UnsupportedProtocol, e:
2023
            raise tests.TestSkipped(str(e))
2018.2.7 by Andrew Bennetts
Implement _post on HttpTransport_urllib.
2024
    
2025
    def test_bulk_data_urllib(self):
2026
        self._test_bulk_data('http+urllib')
2027
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
2028
    def test_smart_http_medium_request_accept_bytes(self):
2029
        medium = FakeHTTPMedium()
2018.2.8 by Andrew Bennetts
Make HttpTransportBase.get_smart_client return self again.
2030
        request = SmartClientHTTPMediumRequest(medium)
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
2031
        request.accept_bytes('abc')
2032
        request.accept_bytes('def')
2033
        self.assertEqual(None, medium.written_request)
2034
        request.finished_writing()
2035
        self.assertEqual('abcdef', medium.written_request)
2036
2018.2.7 by Andrew Bennetts
Implement _post on HttpTransport_urllib.
2037
    def _test_http_send_smart_request(self, url_protocol):
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
2038
        http_server = HTTPServerWithSmarts()
2018.2.7 by Andrew Bennetts
Implement _post on HttpTransport_urllib.
2039
        http_server._url_protocol = url_protocol
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
2040
        http_server.setUp(self.get_vfs_only_server())
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
2041
        self.addCleanup(http_server.tearDown)
2042
2043
        post_body = 'hello\n'
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
2044
        expected_reply_body = 'ok\x012\n'
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
2045
2046
        http_transport = get_transport(http_server.get_url())
2047
        medium = http_transport.get_smart_medium()
2018.2.8 by Andrew Bennetts
Make HttpTransportBase.get_smart_client return self again.
2048
        response = medium.send_http_smart_request(post_body)
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
2049
        reply_body = response.read()
2050
        self.assertEqual(expected_reply_body, reply_body)
2051
2018.2.7 by Andrew Bennetts
Implement _post on HttpTransport_urllib.
2052
    def test_http_send_smart_request_pycurl(self):
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
2053
        try:
2054
            self._test_http_send_smart_request('http+pycurl')
2055
        except errors.UnsupportedProtocol, e:
2056
            raise tests.TestSkipped(str(e))
2018.2.7 by Andrew Bennetts
Implement _post on HttpTransport_urllib.
2057
2058
    def test_http_send_smart_request_urllib(self):
2059
        self._test_http_send_smart_request('http+urllib')
2060
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
2061
    def test_http_server_with_smarts(self):
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
2062
        self.transport_readonly_server = HTTPServerWithSmarts
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
2063
2064
        post_body = 'hello\n'
2432.2.1 by Andrew Bennetts
Add Smart{Client,Server}RequestProtocolTwo, that prefix args tuples with a version marker.
2065
        expected_reply_body = 'ok\x012\n'
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
2066
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
2067
        smart_server_url = self.get_readonly_url('.bzr/smart')
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
2068
        reply = urllib2.urlopen(smart_server_url, post_body).read()
2069
2070
        self.assertEqual(expected_reply_body, reply)
2071
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
2072
    def test_smart_http_server_post_request_handler(self):
2400.1.3 by Andrew Bennetts
Split smart transport code into several separate modules.
2073
        self.transport_readonly_server = HTTPServerWithSmarts
2074
        httpd = self.get_readonly_server()._get_httpd()
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
2075
2076
        socket = SampleSocket(
2077
            'POST /.bzr/smart HTTP/1.0\r\n'
2078
            # HTTP/1.0 posts must have a Content-Length.
2079
            'Content-Length: 6\r\n'
2080
            '\r\n'
2081
            'hello\n')
2164.2.13 by v.ladeuil+lp at free
Add tests for redirection. Preserve transport decorations.
2082
        # Beware: the ('localhost', 80) below is the
2083
        # client_address parameter, but we don't have one because
2164.2.14 by v.ladeuil+lp at free
Typo in comment
2084
        # 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.
2085
        # address. The test framework never uses this client
2086
        # address, so far...
2087
        request_handler = SmartRequestHandler(socket, ('localhost', 80), httpd)
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
2088
        response = socket.writefile.getvalue()
2089
        self.assertStartsWith(response, 'HTTP/1.0 200 ')
2090
        # 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.
2091
        expected_end_of_response = '\r\n\r\nok\x012\n'
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
2092
        self.assertEndsWith(response, expected_end_of_response)
2093
2094
2095
class SampleSocket(object):
2096
    """A socket-like object for use in testing the HTTP request handler."""
2097
    
2098
    def __init__(self, socket_read_content):
2099
        """Constructs a sample socket.
2100
2101
        :param socket_read_content: a byte sequence
2102
        """
2103
        # Use plain python StringIO so we can monkey-patch the close method to
2104
        # not discard the contents.
2105
        from StringIO import StringIO
2106
        self.readfile = StringIO(socket_read_content)
2107
        self.writefile = StringIO()
2108
        self.writefile.close = lambda: None
2109
        
2110
    def makefile(self, mode='r', bufsize=None):
2111
        if 'r' in mode:
2112
            return self.readfile
2113
        else:
2114
            return self.writefile
2115
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.
2116
2208.4.4 by Andrew Bennetts
Merge bzr.dev.
2117
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.
2118
2208.4.3 by Andrew Bennetts
Let SmartHTTPTransport.clone('..') continue to POST to the cloned URL (unlike clone('child')).
2119
    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.
2120
        # 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')).
2121
        # requests for child URLs of that to the original URL.  i.e., we want to
2122
        # POST to "bzr+http://host/foo/.bzr/smart" and never something like
2123
        # "bzr+http://host/foo/.bzr/branch/.bzr/smart".  So, a cloned
2208.4.4 by Andrew Bennetts
Merge bzr.dev.
2124
        # 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.
2125
        # it sends in smart requests accordingly.
2208.4.4 by Andrew Bennetts
Merge bzr.dev.
2126
        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.
2127
        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')).
2128
        self.assertEqual(base_transport._http_transport,
2129
                         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.
2130
        self.assertEqual('child_dir/foo', new_transport._remote_path('foo'))
2131
2208.4.3 by Andrew Bennetts
Let SmartHTTPTransport.clone('..') continue to POST to the cloned URL (unlike clone('child')).
2132
    def test_remote_path_after_clone_parent(self):
2133
        # However, accessing a parent directory should go direct to the parent's
2134
        # URL.  We don't send relpaths like "../foo" in smart requests.
2208.4.4 by Andrew Bennetts
Merge bzr.dev.
2135
        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')).
2136
        new_transport = base_transport.clone('..')
2137
        self.assertEqual('foo', new_transport._remote_path('foo'))
2138
        new_transport = base_transport.clone('../')
2139
        self.assertEqual('foo', new_transport._remote_path('foo'))
2140
        new_transport = base_transport.clone('../abc')
2141
        self.assertEqual('foo', new_transport._remote_path('foo'))
2142
        # "abc/../.." should be equivalent to ".."
2143
        new_transport = base_transport.clone('abc/../..')
2144
        self.assertEqual('foo', new_transport._remote_path('foo'))
2145
2466.3.1 by Andrew Bennetts
Normalise URLs in RemoteHTTPTransport before doing URL calculations to fix bad results.
2146
    def test_remote_path_unnormal_base(self):
2147
        # If the transport's base isn't normalised, the _remote_path should
2148
        # still be calculated correctly.
2149
        base_transport = remote.RemoteHTTPTransport('bzr+http://host/%7Ea/b')
2150
        self.assertEqual('c', base_transport._remote_path('c'))
2151
2152
    def test_clone_unnormal_base(self):
2153
        # If the transport's base isn't normalised, cloned transports should
2154
        # still work correctly.
2155
        base_transport = remote.RemoteHTTPTransport('bzr+http://host/%7Ea/b')
2156
        new_transport = base_transport.clone('c')
2157
        self.assertEqual('bzr+http://host/%7Ea/b/c/', new_transport.base)
2158
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
2159
        
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
2160
# TODO: Client feature that does get_bundle and then installs that into a
2161
# branch; this can be used in place of the regular pull/fetch operation when
2162
# coming from a smart server.
2163
#
2164
# TODO: Eventually, want to do a 'branch' command by fetching the whole
2165
# history as one big bundle.  How?  
2166
#
2167
# The branch command does 'br_from.sprout', which tries to preserve the same
2168
# format.  We don't necessarily even want that.  
2169
#
2170
# It might be simpler to handle cmd_pull first, which does a simpler fetch()
2171
# operation from one branch into another.  It already has some code for
2172
# pulling from a bundle, which it does by trying to see if the destination is
2173
# a bundle file.  So it seems the logic for pull ought to be:
2174
# 
2175
#  - if it's a smart server, get a bundle from there and install that
2176
#  - if it's a bundle, install that
2177
#  - if it's a branch, pull from there
2178
#
2179
# Getting a bundle from a smart server is a bit different from reading a
2180
# bundle from a URL:
2181
#
2182
#  - we can reasonably remember the URL we last read from 
2183
#  - you can specify a revision number to pull, and we need to pass it across
2184
#    to the server as a limit on what will be requested
2185
#
2186
# TODO: Given a URL, determine whether it is a smart server or not (or perhaps
2187
# otherwise whether it's a bundle?)  Should this be a property or method of
2188
# the transport?  For the ssh protocol, we always know it's a smart server.
2189
# For http, we potentially need to probe.  But if we're explicitly given
2190
# bzr+http:// then we can skip that for now.