198
253
client_medium._accept_bytes('abc')
199
254
self.assertEqual('abc', output.getvalue())
256
def test_simple_pipes__accept_bytes_subprocess_closed(self):
257
# It is unfortunate that we have to use Popen for this. However,
258
# os.pipe() does not behave the same as subprocess.Popen().
259
# On Windows, if you use os.pipe() and close the write side,
260
# read.read() hangs. On Linux, read.read() returns the empty string.
261
p = subprocess.Popen([sys.executable, '-c',
263
'sys.stdout.write(sys.stdin.read(4))\n'
264
'sys.stdout.close()\n'],
265
stdout=subprocess.PIPE, stdin=subprocess.PIPE)
266
client_medium = medium.SmartSimplePipesClientMedium(
267
p.stdout, p.stdin, 'base')
268
client_medium._accept_bytes('abc\n')
269
self.assertEqual('abc', client_medium._read_bytes(3))
271
# While writing to the underlying pipe,
272
# Windows py2.6.6 we get IOError(EINVAL)
273
# Lucid py2.6.5, we get IOError(EPIPE)
274
# In both cases, it should be wrapped to ConnectionReset
275
self.assertRaises(errors.ConnectionReset,
276
client_medium._accept_bytes, 'more')
278
def test_simple_pipes__accept_bytes_pipe_closed(self):
279
child_read, client_write = create_file_pipes()
280
client_medium = medium.SmartSimplePipesClientMedium(
281
None, client_write, 'base')
282
client_medium._accept_bytes('abc\n')
283
self.assertEqual('abc\n', child_read.read(4))
284
# While writing to the underlying pipe,
285
# Windows py2.6.6 we get IOError(EINVAL)
286
# Lucid py2.6.5, we get IOError(EPIPE)
287
# In both cases, it should be wrapped to ConnectionReset
289
self.assertRaises(errors.ConnectionReset,
290
client_medium._accept_bytes, 'more')
292
def test_simple_pipes__flush_pipe_closed(self):
293
child_read, client_write = create_file_pipes()
294
client_medium = medium.SmartSimplePipesClientMedium(
295
None, client_write, 'base')
296
client_medium._accept_bytes('abc\n')
298
# Even though the pipe is closed, flush on the write side seems to be a
299
# no-op, rather than a failure.
300
client_medium._flush()
302
def test_simple_pipes__flush_subprocess_closed(self):
303
p = subprocess.Popen([sys.executable, '-c',
305
'sys.stdout.write(sys.stdin.read(4))\n'
306
'sys.stdout.close()\n'],
307
stdout=subprocess.PIPE, stdin=subprocess.PIPE)
308
client_medium = medium.SmartSimplePipesClientMedium(
309
p.stdout, p.stdin, 'base')
310
client_medium._accept_bytes('abc\n')
312
# Even though the child process is dead, flush seems to be a no-op.
313
client_medium._flush()
315
def test_simple_pipes__read_bytes_pipe_closed(self):
316
child_read, client_write = create_file_pipes()
317
client_medium = medium.SmartSimplePipesClientMedium(
318
child_read, client_write, 'base')
319
client_medium._accept_bytes('abc\n')
321
self.assertEqual('abc\n', client_medium._read_bytes(4))
322
self.assertEqual('', client_medium._read_bytes(4))
324
def test_simple_pipes__read_bytes_subprocess_closed(self):
325
p = subprocess.Popen([sys.executable, '-c',
327
'if sys.platform == "win32":\n'
328
' import msvcrt, os\n'
329
' msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)\n'
330
' msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)\n'
331
'sys.stdout.write(sys.stdin.read(4))\n'
332
'sys.stdout.close()\n'],
333
stdout=subprocess.PIPE, stdin=subprocess.PIPE)
334
client_medium = medium.SmartSimplePipesClientMedium(
335
p.stdout, p.stdin, 'base')
336
client_medium._accept_bytes('abc\n')
338
self.assertEqual('abc\n', client_medium._read_bytes(4))
339
self.assertEqual('', client_medium._read_bytes(4))
201
341
def test_simple_pipes_client_disconnect_does_nothing(self):
202
342
# calling disconnect does nothing.
203
343
input = StringIO()
3718
class Test_SmartClientRequest(tests.TestCase):
3720
def make_client_with_failing_medium(self, fail_at_write=True, response=''):
3721
response_io = StringIO(response)
3723
vendor = FirstRejectedStringIOSSHVendor(response_io, output,
3724
fail_at_write=fail_at_write)
3725
ssh_params = medium.SSHParams('a host', 'a port', 'a user', 'a pass')
3726
client_medium = medium.SmartSSHClientMedium('base', ssh_params, vendor)
3727
smart_client = client._SmartClient(client_medium, headers={})
3728
return output, vendor, smart_client
3730
def make_response(self, args, body=None, body_stream=None):
3731
response_io = StringIO()
3732
response = _mod_request.SuccessfulSmartServerResponse(args, body=body,
3733
body_stream=body_stream)
3734
responder = protocol.ProtocolThreeResponder(response_io.write)
3735
responder.send_response(response)
3736
return response_io.getvalue()
3738
def test__call_doesnt_retry_append(self):
3739
response = self.make_response(('appended', '8'))
3740
output, vendor, smart_client = self.make_client_with_failing_medium(
3741
fail_at_write=False, response=response)
3742
smart_request = client._SmartClientRequest(smart_client, 'append',
3743
('foo', ''), body='content\n')
3744
self.assertRaises(errors.ConnectionReset, smart_request._call, 3)
3746
def test__call_retries_get_bytes(self):
3747
response = self.make_response(('ok',), 'content\n')
3748
output, vendor, smart_client = self.make_client_with_failing_medium(
3749
fail_at_write=False, response=response)
3750
smart_request = client._SmartClientRequest(smart_client, 'get',
3752
response, response_handler = smart_request._call(3)
3753
self.assertEqual(('ok',), response)
3754
self.assertEqual('content\n', response_handler.read_body_bytes())
3756
def test__call_noretry_get_bytes(self):
3757
debug.debug_flags.add('noretry')
3758
response = self.make_response(('ok',), 'content\n')
3759
output, vendor, smart_client = self.make_client_with_failing_medium(
3760
fail_at_write=False, response=response)
3761
smart_request = client._SmartClientRequest(smart_client, 'get',
3763
self.assertRaises(errors.ConnectionReset, smart_request._call, 3)
3765
def test__send_no_retry_pipes(self):
3766
client_read, server_write = create_file_pipes()
3767
server_read, client_write = create_file_pipes()
3768
client_medium = medium.SmartSimplePipesClientMedium(client_read,
3769
client_write, base='/')
3770
smart_client = client._SmartClient(client_medium)
3771
smart_request = client._SmartClientRequest(smart_client,
3773
# Close the server side
3775
encoder, response_handler = smart_request._construct_protocol(3)
3776
self.assertRaises(errors.ConnectionReset,
3777
smart_request._send_no_retry, encoder)
3779
def test__send_read_response_sockets(self):
3780
listen_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
3781
listen_sock.bind(('127.0.0.1', 0))
3782
listen_sock.listen(1)
3783
host, port = listen_sock.getsockname()
3784
client_medium = medium.SmartTCPClientMedium(host, port, '/')
3785
client_medium._ensure_connection()
3786
smart_client = client._SmartClient(client_medium)
3787
smart_request = client._SmartClientRequest(smart_client, 'hello', ())
3788
# Accept the connection, but don't actually talk to the client.
3789
server_sock, _ = listen_sock.accept()
3791
# Sockets buffer and don't really notice that the server has closed the
3792
# connection until we try to read again.
3793
handler = smart_request._send(3)
3794
self.assertRaises(errors.ConnectionReset,
3795
handler.read_response_tuple, expect_body=False)
3797
def test__send_retries_on_write(self):
3798
output, vendor, smart_client = self.make_client_with_failing_medium()
3799
smart_request = client._SmartClientRequest(smart_client, 'hello', ())
3800
handler = smart_request._send(3)
3801
self.assertEqual('bzr message 3 (bzr 1.6)\n' # protocol
3802
'\x00\x00\x00\x02de' # empty headers
3803
's\x00\x00\x00\tl5:helloee',
3806
[('connect_ssh', 'a user', 'a pass', 'a host', 'a port',
3807
['bzr', 'serve', '--inet', '--directory=/', '--allow-writes']),
3809
('connect_ssh', 'a user', 'a pass', 'a host', 'a port',
3810
['bzr', 'serve', '--inet', '--directory=/', '--allow-writes']),
3814
def test__send_doesnt_retry_read_failure(self):
3815
output, vendor, smart_client = self.make_client_with_failing_medium(
3816
fail_at_write=False)
3817
smart_request = client._SmartClientRequest(smart_client, 'hello', ())
3818
handler = smart_request._send(3)
3819
self.assertEqual('bzr message 3 (bzr 1.6)\n' # protocol
3820
'\x00\x00\x00\x02de' # empty headers
3821
's\x00\x00\x00\tl5:helloee',
3824
[('connect_ssh', 'a user', 'a pass', 'a host', 'a port',
3825
['bzr', 'serve', '--inet', '--directory=/', '--allow-writes']),
3828
self.assertRaises(errors.ConnectionReset, handler.read_response_tuple)
3830
def test__send_request_retries_body_stream_if_not_started(self):
3831
output, vendor, smart_client = self.make_client_with_failing_medium()
3832
smart_request = client._SmartClientRequest(smart_client, 'hello', (),
3833
body_stream=['a', 'b'])
3834
response_handler = smart_request._send(3)
3835
# We connect, get disconnected, and notice before consuming the stream,
3836
# so we try again one time and succeed.
3838
[('connect_ssh', 'a user', 'a pass', 'a host', 'a port',
3839
['bzr', 'serve', '--inet', '--directory=/', '--allow-writes']),
3841
('connect_ssh', 'a user', 'a pass', 'a host', 'a port',
3842
['bzr', 'serve', '--inet', '--directory=/', '--allow-writes']),
3845
self.assertEqual('bzr message 3 (bzr 1.6)\n' # protocol
3846
'\x00\x00\x00\x02de' # empty headers
3847
's\x00\x00\x00\tl5:helloe'
3848
'b\x00\x00\x00\x01a'
3849
'b\x00\x00\x00\x01b'
3853
def test__send_request_stops_if_body_started(self):
3854
# We intentionally use the python StringIO so that we can subclass it.
3855
from StringIO import StringIO
3856
response = StringIO()
3858
class FailAfterFirstWrite(StringIO):
3859
"""Allow one 'write' call to pass, fail the rest"""
3861
StringIO.__init__(self)
3867
return StringIO.write(self, s)
3868
raise IOError(errno.EINVAL, 'invalid file handle')
3869
output = FailAfterFirstWrite()
3871
vendor = FirstRejectedStringIOSSHVendor(response, output,
3872
fail_at_write=False)
3873
ssh_params = medium.SSHParams('a host', 'a port', 'a user', 'a pass')
3874
client_medium = medium.SmartSSHClientMedium('base', ssh_params, vendor)
3875
smart_client = client._SmartClient(client_medium, headers={})
3876
smart_request = client._SmartClientRequest(smart_client, 'hello', (),
3877
body_stream=['a', 'b'])
3878
self.assertRaises(errors.ConnectionReset, smart_request._send, 3)
3879
# We connect, and manage to get to the point that we start consuming
3880
# the body stream. The next write fails, so we just stop.
3882
[('connect_ssh', 'a user', 'a pass', 'a host', 'a port',
3883
['bzr', 'serve', '--inet', '--directory=/', '--allow-writes']),
3887
self.assertEqual('bzr message 3 (bzr 1.6)\n' # protocol
3888
'\x00\x00\x00\x02de' # empty headers
3889
's\x00\x00\x00\tl5:helloe',
3892
def test__send_disabled_retry(self):
3893
debug.debug_flags.add('noretry')
3894
output, vendor, smart_client = self.make_client_with_failing_medium()
3895
smart_request = client._SmartClientRequest(smart_client, 'hello', ())
3896
self.assertRaises(errors.ConnectionReset, smart_request._send, 3)
3898
[('connect_ssh', 'a user', 'a pass', 'a host', 'a port',
3899
['bzr', 'serve', '--inet', '--directory=/', '--allow-writes']),
3529
3905
class LengthPrefixedBodyDecoder(tests.TestCase):
3531
3907
# XXX: TODO: make accept_reading_trailer invoke translate_response or