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