69
69
def __init__(self, vendor):
70
70
self.vendor = vendor
73
73
self.vendor.calls.append(('close', ))
75
75
def get_filelike_channels(self):
76
76
return self.vendor.read_from, self.vendor.write_to
79
79
class _InvalidHostnameFeature(tests.Feature):
80
80
"""Does 'non_existent.invalid' fail to resolve?
82
82
RFC 2606 states that .invalid is reserved for invalid domain names, and
83
83
also underscores are not a valid character in domain names. Despite this,
84
84
it's possible a badly misconfigured name server might decide to always
132
132
t = threading.Thread(target=_receive_bytes_on_server)
136
136
def test_construct_smart_simple_pipes_client_medium(self):
137
137
# the SimplePipes client medium takes two pipes:
138
138
# readable pipe, writeable pipe.
139
139
# Constructing one should just save these and do nothing.
140
140
# We test this by passing in None.
141
141
client_medium = medium.SmartSimplePipesClientMedium(None, None, None)
143
143
def test_simple_pipes_client_request_type(self):
144
144
# SimplePipesClient should use SmartClientStreamMediumRequest's.
145
145
client_medium = medium.SmartSimplePipesClientMedium(None, None, None)
149
149
def test_simple_pipes_client_get_concurrent_requests(self):
150
150
# the simple_pipes client does not support pipelined requests:
151
# but it does support serial requests: we construct one after
151
# but it does support serial requests: we construct one after
152
152
# another is finished. This is a smoke test testing the integration
153
153
# of the SmartClientStreamMediumRequest and the SmartClientStreamMedium
154
154
# classes - as the sibling classes share this logic, they do not have
212
212
self.assertEqual('abc', client_medium.read_bytes(3))
213
213
client_medium.disconnect()
214
214
self.assertEqual('def', client_medium.read_bytes(3))
216
216
def test_simple_pipes_client_supports__flush(self):
217
# invoking _flush on a SimplePipesClient should flush the output
217
# invoking _flush on a SimplePipesClient should flush the output
218
218
# pipe. We test this by creating an output pipe that records
219
219
# flush calls made to it.
220
220
from StringIO import StringIO # get regular StringIO
469
469
class TestSmartClientStreamMediumRequest(tests.TestCase):
470
470
"""Tests the for SmartClientStreamMediumRequest.
472
SmartClientStreamMediumRequest is a helper for the three stream based
472
SmartClientStreamMediumRequest is a helper for the three stream based
473
473
mediums: TCP, SSH, SimplePipes, so we only test it once, and then test that
474
474
those three mediums implement the interface it expects.
477
477
def test_accept_bytes_after_finished_writing_errors(self):
478
# calling accept_bytes after calling finished_writing raises
478
# calling accept_bytes after calling finished_writing raises
479
479
# WritingCompleted to prevent bad assumptions on stream environments
480
480
# breaking the needs of message-based environments.
481
481
output = StringIO()
537
537
None, None, 'base')
538
538
request = medium.SmartClientStreamMediumRequest(client_medium)
539
539
self.assertRaises(errors.WritingNotComplete, request.finished_reading)
541
541
def test_read_bytes(self):
542
542
# read bytes should invoke _read_bytes on the stream medium.
543
543
# we test this by using the SimplePipes medium - the most trivial one
544
# and checking that the data is supplied. Its possible that a
544
# and checking that the data is supplied. Its possible that a
545
545
# faulty implementation could poke at the pipe variables them selves,
546
546
# but we trust that this will be caught as it will break the integration
1229
1229
Note: these tests are rudimentary versions of the command object tests in
1233
1233
def test_hello(self):
1234
1234
cmd = _mod_request.HelloRequest(None, '/')
1235
1235
response = cmd.execute()
1236
1236
self.assertEqual(('ok', '2'), response.args)
1237
1237
self.assertEqual(None, response.body)
1239
1239
def test_get_bundle(self):
1240
1240
from bzrlib.bundle import serializer
1241
1241
wt = self.make_branch_and_tree('.')
1242
1242
self.build_tree_contents([('hello', 'hello world')])
1243
1243
wt.add('hello')
1244
1244
rev_id = wt.commit('add hello')
1246
1246
cmd = _mod_request.GetBundleRequest(self.get_transport(), '/')
1247
1247
response = cmd.execute('.', rev_id)
1248
1248
bundle = serializer.read_bundle(StringIO(response.body))
1272
1272
handler.dispatch_command('hello', ())
1273
1273
self.assertEqual(('ok', '2'), handler.response.args)
1274
1274
self.assertEqual(None, handler.response.body)
1276
1276
def test_disable_vfs_handler_classes_via_environment(self):
1277
1277
# VFS handler classes will raise an error from "execute" if
1278
1278
# BZR_NO_SMART_VFS is set.
1468
1468
def assertOffsetSerialisation(self, expected_offsets, expected_serialised,
1470
1470
"""Check that smart (de)serialises offsets as expected.
1472
1472
We check both serialisation and deserialisation at the same time
1473
1473
to ensure that the round tripping cannot skew: both directions should
1474
1474
be as expected.
1476
1476
:param expected_offsets: a readv offset list.
1477
1477
:param expected_seralised: an expected serial form of the offsets.
1489
1489
smart_protocol._has_dispatched = True
1490
1490
smart_protocol.request = _mod_request.SmartServerRequestHandler(
1491
1491
None, _mod_request.request_handlers, '/')
1492
class FakeCommand(object):
1493
def do_body(cmd, body_bytes):
1492
class FakeCommand(_mod_request.SmartServerRequest):
1493
def do_body(self_cmd, body_bytes):
1494
1494
self.end_received = True
1495
1495
self.assertEqual('abcdefg', body_bytes)
1496
1496
return _mod_request.SuccessfulSmartServerResponse(('ok', ))
1497
smart_protocol.request._command = FakeCommand()
1497
smart_protocol.request._command = FakeCommand(None)
1498
1498
# Call accept_bytes to make sure that internal state like _body_decoder
1499
1499
# is initialised. This test should probably be given a clearer
1500
1500
# interface to work with that will not cause this inconsistency.
2409
2401
exc = self.assertRaises(errors.ErrorFromSmartServer, stream.next)
2410
2402
self.assertEqual(('error', 'abc'), exc.error_tuple)
2412
def test_body_stream_interrupted_by_connection_lost(self):
2404
def test_interrupted_by_connection_lost(self):
2413
2405
interrupted_body_stream = (
2414
2406
'oS' # successful response
2415
2407
's\0\0\0\x02le' # empty args
2427
2419
self.assertRaises(
2428
2420
errors.ConnectionReset, response_handler.read_body_bytes)
2422
def test_multiple_bytes_parts(self):
2423
multiple_bytes_parts = (
2424
'oS' # successful response
2425
's\0\0\0\x02le' # empty args
2426
'b\0\0\0\x0bSome bytes\n' # some bytes
2427
'b\0\0\0\x0aMore bytes' # more bytes
2430
response_handler = self.make_response_handler(multiple_bytes_parts)
2432
'Some bytes\nMore bytes', response_handler.read_body_bytes())
2433
response_handler = self.make_response_handler(multiple_bytes_parts)
2435
['Some bytes\n', 'More bytes'],
2436
list(response_handler.read_streamed_body()))
2439
class FakeResponder(object):
2441
response_sent = False
2443
def send_error(self, exc):
2446
def send_response(self, response):
2450
class TestConventionalRequestHandlerBodyStream(tests.TestCase):
2451
"""Tests for ConventionalRequestHandler's handling of request bodies."""
2453
def make_request_handler(self, request_bytes):
2454
"""Make a ConventionalRequestHandler for the given bytes using test
2455
doubles for the request_handler and the responder.
2457
from bzrlib.smart.message import ConventionalRequestHandler
2458
request_handler = InstrumentedRequestHandler()
2459
request_handler.response = _mod_request.SuccessfulSmartServerResponse(('arg', 'arg'))
2460
responder = FakeResponder()
2461
message_handler = ConventionalRequestHandler(request_handler, responder)
2462
protocol_decoder = protocol.ProtocolThreeDecoder(message_handler)
2463
# put decoder in desired state (waiting for message parts)
2464
protocol_decoder.state_accept = protocol_decoder._state_accept_expecting_message_part
2465
protocol_decoder.accept_bytes(request_bytes)
2466
return request_handler
2468
def test_multiple_bytes_parts(self):
2469
"""Each bytes part triggers a call to the request_handler's
2472
multiple_bytes_parts = (
2473
's\0\0\0\x07l3:fooe' # args
2474
'b\0\0\0\x0bSome bytes\n' # some bytes
2475
'b\0\0\0\x0aMore bytes' # more bytes
2478
request_handler = self.make_request_handler(multiple_bytes_parts)
2479
accept_body_calls = [
2480
call_info[1] for call_info in request_handler.calls
2481
if call_info[0] == 'accept_body']
2483
['Some bytes\n', 'More bytes'], accept_body_calls)
2485
def test_error_flag_after_body(self):
2487
's\0\0\0\x07l3:fooe' # request args
2488
'b\0\0\0\x0bSome bytes\n' # some bytes
2489
'b\0\0\0\x0aMore bytes' # more bytes
2491
's\0\0\0\x07l3:bare' # error args
2494
request_handler = self.make_request_handler(body_then_error)
2496
[('post_body_error_received', ('bar',)), ('end_received',)],
2497
request_handler.calls[-2:])
2431
2500
class TestMessageHandlerErrors(tests.TestCase):
2432
2501
"""Tests for v3 that unrecognised (but well-formed) requests/responses are
2477
2546
def __init__(self):
2478
2547
self.calls = []
2480
def body_chunk_received(self, chunk_bytes):
2481
self.calls.append(('body_chunk_received', chunk_bytes))
2548
self.finished_reading = False
2483
2550
def no_body_received(self):
2484
2551
self.calls.append(('no_body_received',))
2486
def prefixed_body_received(self, body_bytes):
2487
self.calls.append(('prefixed_body_received', body_bytes))
2489
2553
def end_received(self):
2490
2554
self.calls.append(('end_received',))
2555
self.finished_reading = True
2557
def dispatch_command(self, cmd, args):
2558
self.calls.append(('dispatch_command', cmd, args))
2560
def accept_body(self, bytes):
2561
self.calls.append(('accept_body', bytes))
2563
def end_of_body(self):
2564
self.calls.append(('end_of_body',))
2565
self.finished_reading = True
2567
def post_body_error_received(self, error_args):
2568
self.calls.append(('post_body_error_received', error_args))
2493
2571
class StubRequest(object):
2529
2607
# The message handler has been invoked with all the parts of the
2530
2608
# trivial response: empty headers, status byte, no args, end.
2531
2609
self.assertEqual(
2532
[('headers', {}), ('byte', 'S'), ('structure', []), ('end',)],
2610
[('headers', {}), ('byte', 'S'), ('structure', ()), ('end',)],
2533
2611
response_handler.event_log)
2535
2613
def test_incomplete_message(self):
2643
2721
self.assertEqual(
2644
2722
['accept_bytes', 'finished_writing'], medium_request.calls)
2724
def test_call_with_body_stream_smoke_test(self):
2725
"""A smoke test for ProtocolThreeRequester.call_with_body_stream.
2727
This test checks that a particular simple invocation of
2728
call_with_body_stream emits the correct bytes for that invocation.
2730
requester, output = self.make_client_encoder_and_output()
2731
requester.set_headers({'header name': 'header value'})
2732
stream = ['chunk 1', 'chunk two']
2733
requester.call_with_body_stream(('one arg',), stream)
2735
'bzr message 3 (bzr 1.6)\n' # protocol version
2736
'\x00\x00\x00\x1fd11:header name12:header valuee' # headers
2737
's\x00\x00\x00\x0bl7:one arge' # args
2738
'b\x00\x00\x00\x07chunk 1' # a prefixed body chunk
2739
'b\x00\x00\x00\x09chunk two' # a prefixed body chunk
2743
def test_call_with_body_stream_empty_stream(self):
2744
"""call_with_body_stream with an empty stream."""
2745
requester, output = self.make_client_encoder_and_output()
2746
requester.set_headers({})
2748
requester.call_with_body_stream(('one arg',), stream)
2750
'bzr message 3 (bzr 1.6)\n' # protocol version
2751
'\x00\x00\x00\x02de' # headers
2752
's\x00\x00\x00\x0bl7:one arge' # args
2757
def test_call_with_body_stream_error(self):
2758
"""call_with_body_stream will abort the streamed body with an
2759
error if the stream raises an error during iteration.
2761
The resulting request will still be a complete message.
2763
requester, output = self.make_client_encoder_and_output()
2764
requester.set_headers({})
2765
def stream_that_fails():
2768
raise Exception('Boom!')
2769
self.assertRaises(Exception, requester.call_with_body_stream,
2770
('one arg',), stream_that_fails())
2772
'bzr message 3 (bzr 1.6)\n' # protocol version
2773
'\x00\x00\x00\x02de' # headers
2774
's\x00\x00\x00\x0bl7:one arge' # args
2775
'b\x00\x00\x00\x03aaa' # body
2776
'b\x00\x00\x00\x03bbb' # more body
2778
's\x00\x00\x00\x09l5:errore' # error args: ('error',)
2647
2783
class StubMediumRequest(object):
2648
2784
"""A stub medium request that tracks the number of times accept_bytes is
2790
2926
super(MockMedium, self).__init__('dummy base')
2791
2927
self._mock_request = _MockMediumRequest(self)
2792
2928
self._expected_events = []
2794
2930
def expect_request(self, request_bytes, response_bytes,
2795
2931
allow_partial_read=False):
2796
2932
"""Expect 'request_bytes' to be sent, and reply with 'response_bytes'.
3412
3548
self.assertNotEquals(type(r), type(t))
3415
# TODO: Client feature that does get_bundle and then installs that into a
3416
# branch; this can be used in place of the regular pull/fetch operation when
3417
# coming from a smart server.
3419
# TODO: Eventually, want to do a 'branch' command by fetching the whole
3420
# history as one big bundle. How?
3422
# The branch command does 'br_from.sprout', which tries to preserve the same
3423
# format. We don't necessarily even want that.
3425
# It might be simpler to handle cmd_pull first, which does a simpler fetch()
3426
# operation from one branch into another. It already has some code for
3427
# pulling from a bundle, which it does by trying to see if the destination is
3428
# a bundle file. So it seems the logic for pull ought to be:
3430
# - if it's a smart server, get a bundle from there and install that
3431
# - if it's a bundle, install that
3432
# - if it's a branch, pull from there
3434
# Getting a bundle from a smart server is a bit different from reading a
3435
# bundle from a URL:
3437
# - we can reasonably remember the URL we last read from
3438
# - you can specify a revision number to pull, and we need to pass it across
3439
# to the server as a limit on what will be requested
3441
# TODO: Given a URL, determine whether it is a smart server or not (or perhaps
3442
# otherwise whether it's a bundle?) Should this be a property or method of
3443
# the transport? For the ssh protocol, we always know it's a smart server.
3444
# For http, we potentially need to probe. But if we're explicitly given
3445
# bzr+http:// then we can skip that for now.