1392
1515
# check the encoding of the server for all input_tuples matches
1393
1516
# expected bytes
1394
1517
for input_tuple in input_tuples:
1395
server_output = StringIO()
1396
server_protocol = self.server_protocol_class(
1397
None, server_output.write)
1518
server_protocol, server_output = self.make_server_protocol()
1398
1519
server_protocol._send_response(
1399
1520
_mod_request.SuccessfulSmartServerResponse(input_tuple))
1400
1521
self.assertEqual(expected_bytes, server_output.getvalue())
1401
1522
# check the decoding of the client smart_protocol from expected_bytes:
1402
input = StringIO(expected_bytes)
1404
client_medium = medium.SmartSimplePipesClientMedium(input, output)
1405
request = client_medium.get_request()
1406
smart_protocol = self.client_protocol_class(request)
1407
smart_protocol.call('foo')
1408
self.assertEqual(expected_tuple, smart_protocol.read_response_tuple())
1523
requester, response_handler = self.make_client_protocol(expected_bytes)
1524
requester.call('foo')
1525
self.assertEqual(expected_tuple, response_handler.read_response_tuple())
1411
1528
class CommonSmartProtocolTestMixin(object):
1413
def test_errors_are_logged(self):
1414
"""If an error occurs during testing, it is logged to the test log."""
1415
out_stream = StringIO()
1416
smart_protocol = self.server_protocol_class(None, out_stream.write)
1417
# This triggers a "bad request" error.
1418
smart_protocol.accept_bytes('abc\n')
1419
test_log = self._get_log(keep_log_file=True)
1420
self.assertContainsRe(test_log, 'Traceback')
1421
self.assertContainsRe(test_log, 'SmartProtocolError')
1423
1530
def test_connection_closed_reporting(self):
1426
client_medium = medium.SmartSimplePipesClientMedium(input, output)
1427
request = client_medium.get_request()
1428
smart_protocol = self.client_protocol_class(request)
1429
smart_protocol.call('hello')
1430
ex = self.assertRaises(errors.ConnectionReset,
1431
smart_protocol.read_response_tuple)
1531
requester, response_handler = self.make_client_protocol()
1532
requester.call('hello')
1533
ex = self.assertRaises(errors.ConnectionReset,
1534
response_handler.read_response_tuple)
1432
1535
self.assertEqual("Connection closed: "
1433
1536
"please check connectivity and permissions "
1434
1537
"(and try -Dhpss if further diagnosis is required)", str(ex))
1437
class TestSmartProtocolOne(TestSmartProtocol, CommonSmartProtocolTestMixin):
1438
"""Tests for the smart protocol version one."""
1539
def test_server_offset_serialisation(self):
1540
"""The Smart protocol serialises offsets as a comma and \n string.
1542
We check a number of boundary cases are as expected: empty, one offset,
1543
one with the order of reads not increasing (an out of order read), and
1544
one that should coalesce.
1546
requester, response_handler = self.make_client_protocol()
1547
self.assertOffsetSerialisation([], '', requester)
1548
self.assertOffsetSerialisation([(1,2)], '1,2', requester)
1549
self.assertOffsetSerialisation([(10,40), (0,5)], '10,40\n0,5',
1551
self.assertOffsetSerialisation([(1,2), (3,4), (100, 200)],
1552
'1,2\n3,4\n100,200', requester)
1555
class TestVersionOneFeaturesInProtocolOne(
1556
TestSmartProtocol, CommonSmartProtocolTestMixin):
1557
"""Tests for version one smart protocol features as implemeted by version
1440
1560
client_protocol_class = protocol.SmartClientRequestProtocolOne
1441
1561
server_protocol_class = protocol.SmartServerRequestProtocolOne
1443
1563
def test_construct_version_one_server_protocol(self):
1444
1564
smart_protocol = protocol.SmartServerRequestProtocolOne(None, None)
1445
self.assertEqual('', smart_protocol.excess_buffer)
1565
self.assertEqual('', smart_protocol.unused_data)
1446
1566
self.assertEqual('', smart_protocol.in_buffer)
1447
self.assertFalse(smart_protocol.has_dispatched)
1567
self.assertFalse(smart_protocol._has_dispatched)
1448
1568
self.assertEqual(1, smart_protocol.next_read_size())
1450
1570
def test_construct_version_one_client_protocol(self):
1451
1571
# we can construct a client protocol from a client medium request
1452
1572
output = StringIO()
1453
client_medium = medium.SmartSimplePipesClientMedium(None, output)
1573
client_medium = medium.SmartSimplePipesClientMedium(
1574
None, output, 'base')
1454
1575
request = client_medium.get_request()
1455
1576
client_protocol = protocol.SmartClientRequestProtocolOne(request)
1457
def test_server_offset_serialisation(self):
1458
"""The Smart protocol serialises offsets as a comma and \n string.
1460
We check a number of boundary cases are as expected: empty, one offset,
1461
one with the order of reads not increasing (an out of order read), and
1462
one that should coalesce.
1464
self.assertOffsetSerialisation([], '', self.client_protocol)
1465
self.assertOffsetSerialisation([(1,2)], '1,2', self.client_protocol)
1466
self.assertOffsetSerialisation([(10,40), (0,5)], '10,40\n0,5',
1467
self.client_protocol)
1468
self.assertOffsetSerialisation([(1,2), (3,4), (100, 200)],
1469
'1,2\n3,4\n100,200', self.client_protocol)
1471
1578
def test_accept_bytes_of_bad_request_to_protocol(self):
1472
1579
out_stream = StringIO()
1473
1580
smart_protocol = protocol.SmartServerRequestProtocolOne(
1667
1829
def test_construct_version_two_server_protocol(self):
1668
1830
smart_protocol = protocol.SmartServerRequestProtocolTwo(None, None)
1669
self.assertEqual('', smart_protocol.excess_buffer)
1831
self.assertEqual('', smart_protocol.unused_data)
1670
1832
self.assertEqual('', smart_protocol.in_buffer)
1671
self.assertFalse(smart_protocol.has_dispatched)
1833
self.assertFalse(smart_protocol._has_dispatched)
1672
1834
self.assertEqual(1, smart_protocol.next_read_size())
1674
1836
def test_construct_version_two_client_protocol(self):
1675
1837
# we can construct a client protocol from a client medium request
1676
1838
output = StringIO()
1677
client_medium = medium.SmartSimplePipesClientMedium(None, output)
1839
client_medium = medium.SmartSimplePipesClientMedium(
1840
None, output, 'base')
1678
1841
request = client_medium.get_request()
1679
1842
client_protocol = protocol.SmartClientRequestProtocolTwo(request)
1681
def test_server_offset_serialisation(self):
1682
"""The Smart protocol serialises offsets as a comma and \n string.
1684
We check a number of boundary cases are as expected: empty, one offset,
1685
one with the order of reads not increasing (an out of order read), and
1686
one that should coalesce.
1844
def test_accept_bytes_of_bad_request_to_protocol(self):
1845
out_stream = StringIO()
1846
smart_protocol = self.server_protocol_class(None, out_stream.write)
1847
smart_protocol.accept_bytes('abc')
1848
self.assertEqual('abc', smart_protocol.in_buffer)
1849
smart_protocol.accept_bytes('\n')
1851
self.response_marker +
1852
"failed\nerror\x01Generic bzr smart protocol error: bad request 'abc'\n",
1853
out_stream.getvalue())
1854
self.assertTrue(smart_protocol._has_dispatched)
1855
self.assertEqual(0, smart_protocol.next_read_size())
1857
def test_accept_body_bytes_to_protocol(self):
1858
protocol = self.build_protocol_waiting_for_body()
1859
self.assertEqual(6, protocol.next_read_size())
1860
protocol.accept_bytes('7\nabc')
1861
self.assertEqual(9, protocol.next_read_size())
1862
protocol.accept_bytes('defgd')
1863
protocol.accept_bytes('one\n')
1864
self.assertEqual(0, protocol.next_read_size())
1865
self.assertTrue(self.end_received)
1867
def test_accept_request_and_body_all_at_once(self):
1868
self._captureVar('BZR_NO_SMART_VFS', None)
1869
mem_transport = memory.MemoryTransport()
1870
mem_transport.put_bytes('foo', 'abcdefghij')
1871
out_stream = StringIO()
1872
smart_protocol = self.server_protocol_class(
1873
mem_transport, out_stream.write)
1874
smart_protocol.accept_bytes('readv\x01foo\n3\n3,3done\n')
1875
self.assertEqual(0, smart_protocol.next_read_size())
1876
self.assertEqual(self.response_marker +
1877
'success\nreadv\n3\ndefdone\n',
1878
out_stream.getvalue())
1879
self.assertEqual('', smart_protocol.unused_data)
1880
self.assertEqual('', smart_protocol.in_buffer)
1882
def test_accept_excess_bytes_are_preserved(self):
1883
out_stream = StringIO()
1884
smart_protocol = self.server_protocol_class(None, out_stream.write)
1885
smart_protocol.accept_bytes('hello\nhello\n')
1886
self.assertEqual(self.response_marker + "success\nok\x012\n",
1887
out_stream.getvalue())
1888
self.assertEqual("hello\n", smart_protocol.unused_data)
1889
self.assertEqual("", smart_protocol.in_buffer)
1891
def test_accept_excess_bytes_after_body(self):
1892
# The excess bytes look like the start of another request.
1893
server_protocol = self.build_protocol_waiting_for_body()
1894
server_protocol.accept_bytes('7\nabcdefgdone\n' + self.response_marker)
1895
self.assertTrue(self.end_received)
1896
self.assertEqual(self.response_marker,
1897
server_protocol.unused_data)
1898
self.assertEqual("", server_protocol.in_buffer)
1899
server_protocol.accept_bytes('Y')
1900
self.assertEqual(self.response_marker + "Y",
1901
server_protocol.unused_data)
1902
self.assertEqual("", server_protocol.in_buffer)
1904
def test_accept_excess_bytes_after_dispatch(self):
1905
out_stream = StringIO()
1906
smart_protocol = self.server_protocol_class(None, out_stream.write)
1907
smart_protocol.accept_bytes('hello\n')
1908
self.assertEqual(self.response_marker + "success\nok\x012\n",
1909
out_stream.getvalue())
1910
smart_protocol.accept_bytes(self.request_marker + 'hel')
1911
self.assertEqual(self.request_marker + "hel",
1912
smart_protocol.unused_data)
1913
smart_protocol.accept_bytes('lo\n')
1914
self.assertEqual(self.request_marker + "hello\n",
1915
smart_protocol.unused_data)
1916
self.assertEqual("", smart_protocol.in_buffer)
1918
def test__send_response_sets_finished_reading(self):
1919
smart_protocol = self.server_protocol_class(None, lambda x: None)
1920
self.assertEqual(1, smart_protocol.next_read_size())
1921
smart_protocol._send_response(
1922
_mod_request.SuccessfulSmartServerResponse(('x',)))
1923
self.assertEqual(0, smart_protocol.next_read_size())
1925
def test__send_response_errors_with_base_response(self):
1926
"""Ensure that only the Successful/Failed subclasses are used."""
1927
smart_protocol = self.server_protocol_class(None, lambda x: None)
1928
self.assertRaises(AttributeError, smart_protocol._send_response,
1929
_mod_request.SmartServerResponse(('x',)))
1931
def test_query_version(self):
1932
"""query_version on a SmartClientProtocolTwo should return a number.
1934
The protocol provides the query_version because the domain level clients
1935
may all need to be able to probe for capabilities.
1688
self.assertOffsetSerialisation([], '', self.client_protocol)
1689
self.assertOffsetSerialisation([(1,2)], '1,2', self.client_protocol)
1690
self.assertOffsetSerialisation([(10,40), (0,5)], '10,40\n0,5',
1691
self.client_protocol)
1692
self.assertOffsetSerialisation([(1,2), (3,4), (100, 200)],
1693
'1,2\n3,4\n100,200', self.client_protocol)
1937
# What we really want to test here is that SmartClientProtocolTwo calls
1938
# accept_bytes(tuple_based_encoding_of_hello) and reads and parses the
1939
# response of tuple-encoded (ok, 1). Also, seperately we should test
1940
# the error if the response is a non-understood version.
1941
input = StringIO(self.response_marker + 'success\nok\x012\n')
1943
client_medium = medium.SmartSimplePipesClientMedium(
1944
input, output, 'base')
1945
request = client_medium.get_request()
1946
smart_protocol = self.client_protocol_class(request)
1947
self.assertEqual(2, smart_protocol.query_version())
1949
def test_client_call_empty_response(self):
1950
# protocol.call() can get back an empty tuple as a response. This occurs
1951
# when the parsed line is an empty line, and results in a tuple with
1952
# one element - an empty string.
1953
self.assertServerToClientEncoding(
1954
self.response_marker + 'success\n\n', ('', ), [(), ('', )])
1956
def test_client_call_three_element_response(self):
1957
# protocol.call() can get back tuples of other lengths. A three element
1958
# tuple should be unpacked as three strings.
1959
self.assertServerToClientEncoding(
1960
self.response_marker + 'success\na\x01b\x0134\n',
1964
def test_client_call_with_body_bytes_uploads(self):
1965
# protocol.call_with_body_bytes should length-prefix the bytes onto the
1967
expected_bytes = self.request_marker + "foo\n7\nabcdefgdone\n"
1968
input = StringIO("\n")
1970
client_medium = medium.SmartSimplePipesClientMedium(
1971
input, output, 'base')
1972
request = client_medium.get_request()
1973
smart_protocol = self.client_protocol_class(request)
1974
smart_protocol.call_with_body_bytes(('foo', ), "abcdefg")
1975
self.assertEqual(expected_bytes, output.getvalue())
1977
def test_client_call_with_body_readv_array(self):
1978
# protocol.call_with_upload should encode the readv array and then
1979
# length-prefix the bytes onto the wire.
1980
expected_bytes = self.request_marker + "foo\n7\n1,2\n5,6done\n"
1981
input = StringIO("\n")
1983
client_medium = medium.SmartSimplePipesClientMedium(
1984
input, output, 'base')
1985
request = client_medium.get_request()
1986
smart_protocol = self.client_protocol_class(request)
1987
smart_protocol.call_with_body_readv_array(('foo', ), [(1,2),(5,6)])
1988
self.assertEqual(expected_bytes, output.getvalue())
1990
def test_client_read_body_bytes_all(self):
1991
# read_body_bytes should decode the body bytes from the wire into
1993
expected_bytes = "1234567"
1994
server_bytes = (self.response_marker +
1995
"success\nok\n7\n1234567done\n")
1996
input = StringIO(server_bytes)
1998
client_medium = medium.SmartSimplePipesClientMedium(
1999
input, output, 'base')
2000
request = client_medium.get_request()
2001
smart_protocol = self.client_protocol_class(request)
2002
smart_protocol.call('foo')
2003
smart_protocol.read_response_tuple(True)
2004
self.assertEqual(expected_bytes, smart_protocol.read_body_bytes())
2006
def test_client_read_body_bytes_incremental(self):
2007
# test reading a few bytes at a time from the body
2008
# XXX: possibly we should test dribbling the bytes into the stringio
2009
# to make the state machine work harder: however, as we use the
2010
# LengthPrefixedBodyDecoder that is already well tested - we can skip
2012
expected_bytes = "1234567"
2013
server_bytes = self.response_marker + "success\nok\n7\n1234567done\n"
2014
input = StringIO(server_bytes)
2016
client_medium = medium.SmartSimplePipesClientMedium(
2017
input, output, 'base')
2018
request = client_medium.get_request()
2019
smart_protocol = self.client_protocol_class(request)
2020
smart_protocol.call('foo')
2021
smart_protocol.read_response_tuple(True)
2022
self.assertEqual(expected_bytes[0:2], smart_protocol.read_body_bytes(2))
2023
self.assertEqual(expected_bytes[2:4], smart_protocol.read_body_bytes(2))
2024
self.assertEqual(expected_bytes[4:6], smart_protocol.read_body_bytes(2))
2025
self.assertEqual(expected_bytes[6], smart_protocol.read_body_bytes())
2027
def test_client_cancel_read_body_does_not_eat_body_bytes(self):
2028
# cancelling the expected body needs to finish the request, but not
2029
# read any more bytes.
2030
server_bytes = self.response_marker + "success\nok\n7\n1234567done\n"
2031
input = StringIO(server_bytes)
2033
client_medium = medium.SmartSimplePipesClientMedium(
2034
input, output, 'base')
2035
request = client_medium.get_request()
2036
smart_protocol = self.client_protocol_class(request)
2037
smart_protocol.call('foo')
2038
smart_protocol.read_response_tuple(True)
2039
smart_protocol.cancel_read_body()
2040
self.assertEqual(len(self.response_marker + 'success\nok\n'),
2043
errors.ReadingCompleted, smart_protocol.read_body_bytes)
2045
def test_client_read_body_bytes_interrupted_connection(self):
2046
server_bytes = (self.response_marker +
2047
"success\nok\n999\nincomplete body")
2048
input = StringIO(server_bytes)
2050
client_medium = medium.SmartSimplePipesClientMedium(
2051
input, output, 'base')
2052
request = client_medium.get_request()
2053
smart_protocol = self.client_protocol_class(request)
2054
smart_protocol.call('foo')
2055
smart_protocol.read_response_tuple(True)
2057
errors.ConnectionReset, smart_protocol.read_body_bytes)
2060
class TestSmartProtocolTwoSpecificsMixin(object):
1695
2062
def assertBodyStreamSerialisation(self, expected_serialisation,
1744
2111
self.assertBodyStreamSerialisation(expected_bytes, stream)
1745
2112
self.assertBodyStreamRoundTrips(stream)
1747
def test_accept_bytes_of_bad_request_to_protocol(self):
1748
out_stream = StringIO()
1749
smart_protocol = protocol.SmartServerRequestProtocolTwo(
1750
None, out_stream.write)
1751
smart_protocol.accept_bytes('abc')
1752
self.assertEqual('abc', smart_protocol.in_buffer)
1753
smart_protocol.accept_bytes('\n')
1755
protocol.RESPONSE_VERSION_TWO +
1756
"failed\nerror\x01Generic bzr smart protocol error: bad request 'abc'\n",
1757
out_stream.getvalue())
1758
self.assertTrue(smart_protocol.has_dispatched)
1759
self.assertEqual(0, smart_protocol.next_read_size())
1761
def test_accept_body_bytes_to_protocol(self):
1762
protocol = self.build_protocol_waiting_for_body()
1763
self.assertEqual(6, protocol.next_read_size())
1764
protocol.accept_bytes('7\nabc')
1765
self.assertEqual(9, protocol.next_read_size())
1766
protocol.accept_bytes('defgd')
1767
protocol.accept_bytes('one\n')
1768
self.assertEqual(0, protocol.next_read_size())
1769
self.assertTrue(self.end_received)
1771
def test_accept_request_and_body_all_at_once(self):
1772
self._captureVar('BZR_NO_SMART_VFS', None)
1773
mem_transport = memory.MemoryTransport()
1774
mem_transport.put_bytes('foo', 'abcdefghij')
1775
out_stream = StringIO()
1776
smart_protocol = protocol.SmartServerRequestProtocolTwo(mem_transport,
1778
smart_protocol.accept_bytes('readv\x01foo\n3\n3,3done\n')
1779
self.assertEqual(0, smart_protocol.next_read_size())
1780
self.assertEqual(protocol.RESPONSE_VERSION_TWO +
1781
'success\nreadv\n3\ndefdone\n',
1782
out_stream.getvalue())
1783
self.assertEqual('', smart_protocol.excess_buffer)
1784
self.assertEqual('', smart_protocol.in_buffer)
1786
def test_accept_excess_bytes_are_preserved(self):
1787
out_stream = StringIO()
1788
smart_protocol = protocol.SmartServerRequestProtocolTwo(
1789
None, out_stream.write)
1790
smart_protocol.accept_bytes('hello\nhello\n')
1791
self.assertEqual(protocol.RESPONSE_VERSION_TWO + "success\nok\x012\n",
1792
out_stream.getvalue())
1793
self.assertEqual("hello\n", smart_protocol.excess_buffer)
1794
self.assertEqual("", smart_protocol.in_buffer)
1796
def test_accept_excess_bytes_after_body(self):
1797
# The excess bytes look like the start of another request.
1798
server_protocol = self.build_protocol_waiting_for_body()
1799
server_protocol.accept_bytes(
1800
'7\nabcdefgdone\n' + protocol.RESPONSE_VERSION_TWO)
1801
self.assertTrue(self.end_received)
1802
self.assertEqual(protocol.RESPONSE_VERSION_TWO,
1803
server_protocol.excess_buffer)
1804
self.assertEqual("", server_protocol.in_buffer)
1805
server_protocol.accept_bytes('Y')
1806
self.assertEqual(protocol.RESPONSE_VERSION_TWO + "Y",
1807
server_protocol.excess_buffer)
1808
self.assertEqual("", server_protocol.in_buffer)
1810
def test_accept_excess_bytes_after_dispatch(self):
1811
out_stream = StringIO()
1812
smart_protocol = protocol.SmartServerRequestProtocolTwo(
1813
None, out_stream.write)
1814
smart_protocol.accept_bytes('hello\n')
1815
self.assertEqual(protocol.RESPONSE_VERSION_TWO + "success\nok\x012\n",
1816
out_stream.getvalue())
1817
smart_protocol.accept_bytes(protocol.REQUEST_VERSION_TWO + 'hel')
1818
self.assertEqual(protocol.REQUEST_VERSION_TWO + "hel",
1819
smart_protocol.excess_buffer)
1820
smart_protocol.accept_bytes('lo\n')
1821
self.assertEqual(protocol.REQUEST_VERSION_TWO + "hello\n",
1822
smart_protocol.excess_buffer)
1823
self.assertEqual("", smart_protocol.in_buffer)
1825
def test__send_response_sets_finished_reading(self):
1826
smart_protocol = protocol.SmartServerRequestProtocolTwo(
1827
None, lambda x: None)
1828
self.assertEqual(1, smart_protocol.next_read_size())
1829
smart_protocol._send_response(
1830
request.SuccessfulSmartServerResponse(('x',)))
1831
self.assertEqual(0, smart_protocol.next_read_size())
1833
def test__send_response_with_body_stream_sets_finished_reading(self):
1834
smart_protocol = protocol.SmartServerRequestProtocolTwo(
1835
None, lambda x: None)
1836
self.assertEqual(1, smart_protocol.next_read_size())
1837
smart_protocol._send_response(
1838
request.SuccessfulSmartServerResponse(('x',), body_stream=[]))
1839
self.assertEqual(0, smart_protocol.next_read_size())
1841
def test__send_response_errors_with_base_response(self):
1842
"""Ensure that only the Successful/Failed subclasses are used."""
1843
smart_protocol = protocol.SmartServerRequestProtocolTwo(
1844
None, lambda x: None)
1845
self.assertRaises(AttributeError, smart_protocol._send_response,
1846
request.SmartServerResponse(('x',)))
1848
2114
def test__send_response_includes_failure_marker(self):
1849
2115
"""FailedSmartServerResponse have 'failed\n' after the version."""
1850
2116
out_stream = StringIO()
1851
2117
smart_protocol = protocol.SmartServerRequestProtocolTwo(
1852
2118
None, out_stream.write)
1853
2119
smart_protocol._send_response(
1854
request.FailedSmartServerResponse(('x',)))
2120
_mod_request.FailedSmartServerResponse(('x',)))
1855
2121
self.assertEqual(protocol.RESPONSE_VERSION_TWO + 'failed\nx\n',
1856
2122
out_stream.getvalue())
1861
2127
smart_protocol = protocol.SmartServerRequestProtocolTwo(
1862
2128
None, out_stream.write)
1863
2129
smart_protocol._send_response(
1864
request.SuccessfulSmartServerResponse(('x',)))
2130
_mod_request.SuccessfulSmartServerResponse(('x',)))
1865
2131
self.assertEqual(protocol.RESPONSE_VERSION_TWO + 'success\nx\n',
1866
2132
out_stream.getvalue())
1868
def test_query_version(self):
1869
"""query_version on a SmartClientProtocolTwo should return a number.
1871
The protocol provides the query_version because the domain level clients
1872
may all need to be able to probe for capabilities.
1874
# What we really want to test here is that SmartClientProtocolTwo calls
1875
# accept_bytes(tuple_based_encoding_of_hello) and reads and parses the
1876
# response of tuple-encoded (ok, 1). Also, seperately we should test
1877
# the error if the response is a non-understood version.
1878
input = StringIO(protocol.RESPONSE_VERSION_TWO + 'success\nok\x012\n')
1880
client_medium = medium.SmartSimplePipesClientMedium(input, output)
1881
request = client_medium.get_request()
1882
smart_protocol = protocol.SmartClientRequestProtocolTwo(request)
1883
self.assertEqual(2, smart_protocol.query_version())
1885
def test_client_call_empty_response(self):
1886
# protocol.call() can get back an empty tuple as a response. This occurs
1887
# when the parsed line is an empty line, and results in a tuple with
1888
# one element - an empty string.
1889
self.assertServerToClientEncoding(
1890
protocol.RESPONSE_VERSION_TWO + 'success\n\n', ('', ), [(), ('', )])
1892
def test_client_call_three_element_response(self):
1893
# protocol.call() can get back tuples of other lengths. A three element
1894
# tuple should be unpacked as three strings.
1895
self.assertServerToClientEncoding(
1896
protocol.RESPONSE_VERSION_TWO + 'success\na\x01b\x0134\n',
1900
def test_client_call_with_body_bytes_uploads(self):
1901
# protocol.call_with_body_bytes should length-prefix the bytes onto the
1903
expected_bytes = protocol.REQUEST_VERSION_TWO + "foo\n7\nabcdefgdone\n"
1904
input = StringIO("\n")
1906
client_medium = medium.SmartSimplePipesClientMedium(input, output)
1907
request = client_medium.get_request()
1908
smart_protocol = protocol.SmartClientRequestProtocolTwo(request)
1909
smart_protocol.call_with_body_bytes(('foo', ), "abcdefg")
1910
self.assertEqual(expected_bytes, output.getvalue())
1912
def test_client_call_with_body_readv_array(self):
1913
# protocol.call_with_upload should encode the readv array and then
1914
# length-prefix the bytes onto the wire.
1915
expected_bytes = protocol.REQUEST_VERSION_TWO+"foo\n7\n1,2\n5,6done\n"
1916
input = StringIO("\n")
1918
client_medium = medium.SmartSimplePipesClientMedium(input, output)
1919
request = client_medium.get_request()
1920
smart_protocol = protocol.SmartClientRequestProtocolTwo(request)
1921
smart_protocol.call_with_body_readv_array(('foo', ), [(1,2),(5,6)])
1922
self.assertEqual(expected_bytes, output.getvalue())
1924
def test_client_read_response_tuple_sets_response_status(self):
1925
server_bytes = protocol.RESPONSE_VERSION_TWO + "success\nok\n"
1926
input = StringIO(server_bytes)
1928
client_medium = medium.SmartSimplePipesClientMedium(input, output)
1929
request = client_medium.get_request()
1930
smart_protocol = protocol.SmartClientRequestProtocolTwo(request)
1931
smart_protocol.call('foo')
1932
smart_protocol.read_response_tuple(False)
1933
self.assertEqual(True, smart_protocol.response_status)
1935
def test_client_read_body_bytes_all(self):
1936
# read_body_bytes should decode the body bytes from the wire into
1938
expected_bytes = "1234567"
1939
server_bytes = (protocol.RESPONSE_VERSION_TWO +
1940
"success\nok\n7\n1234567done\n")
1941
input = StringIO(server_bytes)
1943
client_medium = medium.SmartSimplePipesClientMedium(input, output)
1944
request = client_medium.get_request()
1945
smart_protocol = protocol.SmartClientRequestProtocolTwo(request)
1946
smart_protocol.call('foo')
1947
smart_protocol.read_response_tuple(True)
1948
self.assertEqual(expected_bytes, smart_protocol.read_body_bytes())
1950
def test_client_read_body_bytes_incremental(self):
1951
# test reading a few bytes at a time from the body
1952
# XXX: possibly we should test dribbling the bytes into the stringio
1953
# to make the state machine work harder: however, as we use the
1954
# LengthPrefixedBodyDecoder that is already well tested - we can skip
1956
expected_bytes = "1234567"
1957
server_bytes = (protocol.RESPONSE_VERSION_TWO +
1958
"success\nok\n7\n1234567done\n")
1959
input = StringIO(server_bytes)
1961
client_medium = medium.SmartSimplePipesClientMedium(input, output)
1962
request = client_medium.get_request()
1963
smart_protocol = protocol.SmartClientRequestProtocolTwo(request)
1964
smart_protocol.call('foo')
1965
smart_protocol.read_response_tuple(True)
1966
self.assertEqual(expected_bytes[0:2], smart_protocol.read_body_bytes(2))
1967
self.assertEqual(expected_bytes[2:4], smart_protocol.read_body_bytes(2))
1968
self.assertEqual(expected_bytes[4:6], smart_protocol.read_body_bytes(2))
1969
self.assertEqual(expected_bytes[6], smart_protocol.read_body_bytes())
1971
def test_client_cancel_read_body_does_not_eat_body_bytes(self):
1972
# cancelling the expected body needs to finish the request, but not
1973
# read any more bytes.
1974
server_bytes = (protocol.RESPONSE_VERSION_TWO +
1975
"success\nok\n7\n1234567done\n")
1976
input = StringIO(server_bytes)
1978
client_medium = medium.SmartSimplePipesClientMedium(input, output)
1979
request = client_medium.get_request()
1980
smart_protocol = protocol.SmartClientRequestProtocolTwo(request)
1981
smart_protocol.call('foo')
1982
smart_protocol.read_response_tuple(True)
1983
smart_protocol.cancel_read_body()
1984
self.assertEqual(len(protocol.RESPONSE_VERSION_TWO + 'success\nok\n'),
1987
errors.ReadingCompleted, smart_protocol.read_body_bytes)
2134
def test__send_response_with_body_stream_sets_finished_reading(self):
2135
smart_protocol = protocol.SmartServerRequestProtocolTwo(
2136
None, lambda x: None)
2137
self.assertEqual(1, smart_protocol.next_read_size())
2138
smart_protocol._send_response(
2139
_mod_request.SuccessfulSmartServerResponse(('x',), body_stream=[]))
2140
self.assertEqual(0, smart_protocol.next_read_size())
1989
2142
def test_streamed_body_bytes(self):
1990
2143
body_header = 'chunked\n'
2015
2169
"success\nok\n" + body)
2016
2170
input = StringIO(server_bytes)
2017
2171
output = StringIO()
2018
client_medium = medium.SmartSimplePipesClientMedium(input, output)
2172
client_medium = medium.SmartSimplePipesClientMedium(
2173
input, output, 'base')
2019
2174
smart_request = client_medium.get_request()
2020
2175
smart_protocol = protocol.SmartClientRequestProtocolTwo(smart_request)
2021
2176
smart_protocol.call('foo')
2022
2177
smart_protocol.read_response_tuple(True)
2023
2178
expected_chunks = [
2025
request.FailedSmartServerResponse(('error arg1', 'arg2'))]
2180
_mod_request.FailedSmartServerResponse(('error arg1', 'arg2'))]
2026
2181
stream = smart_protocol.read_streamed_body()
2027
2182
self.assertEqual(expected_chunks, list(stream))
2184
def test_streamed_body_bytes_interrupted_connection(self):
2185
body_header = 'chunked\n'
2186
incomplete_body_chunk = "9999\nincomplete chunk"
2187
server_bytes = (protocol.RESPONSE_VERSION_TWO +
2188
"success\nok\n" + body_header + incomplete_body_chunk)
2189
input = StringIO(server_bytes)
2191
client_medium = medium.SmartSimplePipesClientMedium(
2192
input, output, 'base')
2193
request = client_medium.get_request()
2194
smart_protocol = protocol.SmartClientRequestProtocolTwo(request)
2195
smart_protocol.call('foo')
2196
smart_protocol.read_response_tuple(True)
2197
stream = smart_protocol.read_streamed_body()
2198
self.assertRaises(errors.ConnectionReset, stream.next)
2200
def test_client_read_response_tuple_sets_response_status(self):
2201
server_bytes = protocol.RESPONSE_VERSION_TWO + "success\nok\n"
2202
input = StringIO(server_bytes)
2204
client_medium = medium.SmartSimplePipesClientMedium(
2205
input, output, 'base')
2206
request = client_medium.get_request()
2207
smart_protocol = protocol.SmartClientRequestProtocolTwo(request)
2208
smart_protocol.call('foo')
2209
smart_protocol.read_response_tuple(False)
2210
self.assertEqual(True, smart_protocol.response_status)
2212
def test_client_read_response_tuple_raises_UnknownSmartMethod(self):
2213
"""read_response_tuple raises UnknownSmartMethod if the response says
2214
the server did not recognise the request.
2217
protocol.RESPONSE_VERSION_TWO +
2219
"error\x01Generic bzr smart protocol error: bad request 'foo'\n")
2220
input = StringIO(server_bytes)
2222
client_medium = medium.SmartSimplePipesClientMedium(
2223
input, output, 'base')
2224
request = client_medium.get_request()
2225
smart_protocol = protocol.SmartClientRequestProtocolTwo(request)
2226
smart_protocol.call('foo')
2228
errors.UnknownSmartMethod, smart_protocol.read_response_tuple)
2229
# The request has been finished. There is no body to read, and
2230
# attempts to read one will fail.
2232
errors.ReadingCompleted, smart_protocol.read_body_bytes)
2235
class TestSmartProtocolTwoSpecifics(
2236
TestSmartProtocol, TestSmartProtocolTwoSpecificsMixin):
2237
"""Tests for aspects of smart protocol version two that are unique to
2240
Thus tests involving body streams and success/failure markers belong here.
2243
client_protocol_class = protocol.SmartClientRequestProtocolTwo
2244
server_protocol_class = protocol.SmartServerRequestProtocolTwo
2247
class TestVersionOneFeaturesInProtocolThree(
2248
TestSmartProtocol, CommonSmartProtocolTestMixin):
2249
"""Tests for version one smart protocol features as implemented by version
2253
request_encoder = protocol.ProtocolThreeRequester
2254
response_decoder = protocol.ProtocolThreeDecoder
2255
# build_server_protocol_three is a function, so we can't set it as a class
2256
# attribute directly, because then Python will assume it is actually a
2257
# method. So we make server_protocol_class be a static method, rather than
2259
# "server_protocol_class = protocol.build_server_protocol_three".
2260
server_protocol_class = staticmethod(protocol.build_server_protocol_three)
2263
super(TestVersionOneFeaturesInProtocolThree, self).setUp()
2264
self.response_marker = protocol.MESSAGE_VERSION_THREE
2265
self.request_marker = protocol.MESSAGE_VERSION_THREE
2267
def test_construct_version_three_server_protocol(self):
2268
smart_protocol = protocol.ProtocolThreeDecoder(None)
2269
self.assertEqual('', smart_protocol.unused_data)
2270
self.assertEqual('', smart_protocol._in_buffer)
2271
self.assertFalse(smart_protocol._has_dispatched)
2272
# The protocol starts by expecting four bytes, a length prefix for the
2274
self.assertEqual(4, smart_protocol.next_read_size())
2277
class NoOpRequest(_mod_request.SmartServerRequest):
2280
return _mod_request.SuccessfulSmartServerResponse(())
2282
dummy_registry = {'ARG': NoOpRequest}
2285
class LoggingMessageHandler(object):
2290
def _log(self, *args):
2291
self.event_log.append(args)
2293
def headers_received(self, headers):
2294
self._log('headers', headers)
2296
def protocol_error(self, exception):
2297
self._log('protocol_error', exception)
2299
def byte_part_received(self, byte):
2300
self._log('byte', byte)
2302
def bytes_part_received(self, bytes):
2303
self._log('bytes', bytes)
2305
def structure_part_received(self, structure):
2306
self._log('structure', structure)
2308
def end_received(self):
2312
class TestProtocolThree(TestSmartProtocol):
2313
"""Tests for v3 of the server-side protocol."""
2315
request_encoder = protocol.ProtocolThreeRequester
2316
response_decoder = protocol.ProtocolThreeDecoder
2317
server_protocol_class = protocol.ProtocolThreeDecoder
2319
def test_trivial_request(self):
2320
"""Smoke test for the simplest possible v3 request: empty headers, no
2324
headers = '\0\0\0\x02de' # length-prefixed, bencoded empty dict
2326
request_bytes = headers + end
2327
smart_protocol = self.server_protocol_class(LoggingMessageHandler())
2328
smart_protocol.accept_bytes(request_bytes)
2329
self.assertEqual(0, smart_protocol.next_read_size())
2330
self.assertEqual('', smart_protocol.unused_data)
2332
def make_protocol_expecting_message_part(self):
2333
headers = '\0\0\0\x02de' # length-prefixed, bencoded empty dict
2334
message_handler = LoggingMessageHandler()
2335
smart_protocol = self.server_protocol_class(message_handler)
2336
smart_protocol.accept_bytes(headers)
2337
# Clear the event log
2338
del message_handler.event_log[:]
2339
return smart_protocol, message_handler.event_log
2341
def test_decode_one_byte(self):
2342
"""The protocol can decode a 'one byte' message part."""
2343
smart_protocol, event_log = self.make_protocol_expecting_message_part()
2344
smart_protocol.accept_bytes('ox')
2345
self.assertEqual([('byte', 'x')], event_log)
2347
def test_decode_bytes(self):
2348
"""The protocol can decode a 'bytes' message part."""
2349
smart_protocol, event_log = self.make_protocol_expecting_message_part()
2350
smart_protocol.accept_bytes(
2351
'b' # message part kind
2352
'\0\0\0\x07' # length prefix
2355
self.assertEqual([('bytes', 'payload')], event_log)
2357
def test_decode_structure(self):
2358
"""The protocol can decode a 'structure' message part."""
2359
smart_protocol, event_log = self.make_protocol_expecting_message_part()
2360
smart_protocol.accept_bytes(
2361
's' # message part kind
2362
'\0\0\0\x07' # length prefix
2365
self.assertEqual([('structure', ['ARG'])], event_log)
2367
def test_decode_multiple_bytes(self):
2368
"""The protocol can decode a multiple 'bytes' message parts."""
2369
smart_protocol, event_log = self.make_protocol_expecting_message_part()
2370
smart_protocol.accept_bytes(
2371
'b' # message part kind
2372
'\0\0\0\x05' # length prefix
2374
'b' # message part kind
2379
[('bytes', 'first'), ('bytes', 'second')], event_log)
2382
class TestConventionalResponseHandler(tests.TestCase):
2384
def make_response_handler(self, response_bytes):
2385
from bzrlib.smart.message import ConventionalResponseHandler
2386
response_handler = ConventionalResponseHandler()
2387
protocol_decoder = protocol.ProtocolThreeDecoder(response_handler)
2388
# put decoder in desired state (waiting for message parts)
2389
protocol_decoder.state_accept = protocol_decoder._state_accept_expecting_message_part
2391
client_medium = medium.SmartSimplePipesClientMedium(
2392
StringIO(response_bytes), output, 'base')
2393
medium_request = client_medium.get_request()
2394
medium_request.finished_writing()
2395
response_handler.setProtoAndMediumRequest(
2396
protocol_decoder, medium_request)
2397
return response_handler
2399
def test_body_stream_interrupted_by_error(self):
2400
interrupted_body_stream = (
2401
'oS' # successful response
2402
's\0\0\0\x02le' # empty args
2403
'b\0\0\0\x09chunk one' # first chunk
2404
'b\0\0\0\x09chunk two' # second chunk
2406
's\0\0\0\x0el5:error3:abce' # bencoded error
2409
response_handler = self.make_response_handler(interrupted_body_stream)
2410
stream = response_handler.read_streamed_body()
2411
self.assertEqual('chunk one', stream.next())
2412
self.assertEqual('chunk two', stream.next())
2413
exc = self.assertRaises(errors.ErrorFromSmartServer, stream.next)
2414
self.assertEqual(('error', 'abc'), exc.error_tuple)
2416
def test_body_stream_interrupted_by_connection_lost(self):
2417
interrupted_body_stream = (
2418
'oS' # successful response
2419
's\0\0\0\x02le' # empty args
2420
'b\0\0\xff\xffincomplete chunk')
2421
response_handler = self.make_response_handler(interrupted_body_stream)
2422
stream = response_handler.read_streamed_body()
2423
self.assertRaises(errors.ConnectionReset, stream.next)
2425
def test_read_body_bytes_interrupted_by_connection_lost(self):
2426
interrupted_body_stream = (
2427
'oS' # successful response
2428
's\0\0\0\x02le' # empty args
2429
'b\0\0\xff\xffincomplete chunk')
2430
response_handler = self.make_response_handler(interrupted_body_stream)
2432
errors.ConnectionReset, response_handler.read_body_bytes)
2435
class TestMessageHandlerErrors(tests.TestCase):
2436
"""Tests for v3 that unrecognised (but well-formed) requests/responses are
2437
still fully read off the wire, so that subsequent requests/responses on the
2438
same medium can be decoded.
2441
def test_non_conventional_request(self):
2442
"""ConventionalRequestHandler (the default message handler on the
2443
server side) will reject an unconventional message, but still consume
2444
all the bytes of that message and signal when it has done so.
2446
This is what allows a server to continue to accept requests after the
2447
client sends a completely unrecognised request.
2449
# Define an invalid request (but one that is a well-formed message).
2450
# This particular invalid request not only lacks the mandatory
2451
# verb+args tuple, it has a single-byte part, which is forbidden. In
2452
# fact it has that part twice, to trigger multiple errors.
2454
protocol.MESSAGE_VERSION_THREE + # protocol version marker
2455
'\0\0\0\x02de' + # empty headers
2456
'oX' + # a single byte part: 'X'. ConventionalRequestHandler will
2457
# error at this part.
2459
'e' # end of message
2462
to_server = StringIO(invalid_request)
2463
from_server = StringIO()
2464
transport = memory.MemoryTransport('memory:///')
2465
server = medium.SmartServerPipeStreamMedium(
2466
to_server, from_server, transport)
2467
proto = server._build_protocol()
2468
message_handler = proto.message_handler
2469
server._serve_one_request(proto)
2470
# All the bytes have been read from the medium...
2471
self.assertEqual('', to_server.read())
2472
# ...and the protocol decoder has consumed all the bytes, and has
2474
self.assertEqual('', proto.unused_data)
2475
self.assertEqual(0, proto.next_read_size())
2478
class InstrumentedRequestHandler(object):
2479
"""Test Double of SmartServerRequestHandler."""
2484
def body_chunk_received(self, chunk_bytes):
2485
self.calls.append(('body_chunk_received', chunk_bytes))
2487
def no_body_received(self):
2488
self.calls.append(('no_body_received',))
2490
def prefixed_body_received(self, body_bytes):
2491
self.calls.append(('prefixed_body_received', body_bytes))
2493
def end_received(self):
2494
self.calls.append(('end_received',))
2497
class StubRequest(object):
2499
def finished_reading(self):
2503
class TestClientDecodingProtocolThree(TestSmartProtocol):
2504
"""Tests for v3 of the client-side protocol decoding."""
2506
def make_logging_response_decoder(self):
2507
"""Make v3 response decoder using a test response handler."""
2508
response_handler = LoggingMessageHandler()
2509
decoder = protocol.ProtocolThreeDecoder(response_handler)
2510
return decoder, response_handler
2512
def make_conventional_response_decoder(self):
2513
"""Make v3 response decoder using a conventional response handler."""
2514
response_handler = message.ConventionalResponseHandler()
2515
decoder = protocol.ProtocolThreeDecoder(response_handler)
2516
response_handler.setProtoAndMediumRequest(decoder, StubRequest())
2517
return decoder, response_handler
2519
def test_trivial_response_decoding(self):
2520
"""Smoke test for the simplest possible v3 response: empty headers,
2521
status byte, empty args, no body.
2523
headers = '\0\0\0\x02de' # length-prefixed, bencoded empty dict
2524
response_status = 'oS' # success
2525
args = 's\0\0\0\x02le' # length-prefixed, bencoded empty list
2526
end = 'e' # end marker
2527
message_bytes = headers + response_status + args + end
2528
decoder, response_handler = self.make_logging_response_decoder()
2529
decoder.accept_bytes(message_bytes)
2530
# The protocol decoder has finished, and consumed all bytes
2531
self.assertEqual(0, decoder.next_read_size())
2532
self.assertEqual('', decoder.unused_data)
2533
# The message handler has been invoked with all the parts of the
2534
# trivial response: empty headers, status byte, no args, end.
2536
[('headers', {}), ('byte', 'S'), ('structure', []), ('end',)],
2537
response_handler.event_log)
2539
def test_incomplete_message(self):
2540
"""A decoder will keep signalling that it needs more bytes via
2541
next_read_size() != 0 until it has seen a complete message, regardless
2542
which state it is in.
2544
# Define a simple response that uses all possible message parts.
2545
headers = '\0\0\0\x02de' # length-prefixed, bencoded empty dict
2546
response_status = 'oS' # success
2547
args = 's\0\0\0\x02le' # length-prefixed, bencoded empty list
2548
body = 'b\0\0\0\x04BODY' # a body: 'BODY'
2549
end = 'e' # end marker
2550
simple_response = headers + response_status + args + body + end
2551
# Feed the request to the decoder one byte at a time.
2552
decoder, response_handler = self.make_logging_response_decoder()
2553
for byte in simple_response:
2554
self.assertNotEqual(0, decoder.next_read_size())
2555
decoder.accept_bytes(byte)
2556
# Now the response is complete
2557
self.assertEqual(0, decoder.next_read_size())
2559
def test_read_response_tuple_raises_UnknownSmartMethod(self):
2560
"""read_response_tuple raises UnknownSmartMethod if the server replied
2561
with 'UnknownMethod'.
2563
headers = '\0\0\0\x02de' # length-prefixed, bencoded empty dict
2564
response_status = 'oE' # error flag
2565
# args: ('UnknownMethod', 'method-name')
2566
args = 's\0\0\0\x20l13:UnknownMethod11:method-namee'
2567
end = 'e' # end marker
2568
message_bytes = headers + response_status + args + end
2569
decoder, response_handler = self.make_conventional_response_decoder()
2570
decoder.accept_bytes(message_bytes)
2571
error = self.assertRaises(
2572
errors.UnknownSmartMethod, response_handler.read_response_tuple)
2573
self.assertEqual('method-name', error.verb)
2575
def test_read_response_tuple_error(self):
2576
"""If the response has an error, it is raised as an exception."""
2577
headers = '\0\0\0\x02de' # length-prefixed, bencoded empty dict
2578
response_status = 'oE' # error
2579
args = 's\0\0\0\x1al9:first arg10:second arge' # two args
2580
end = 'e' # end marker
2581
message_bytes = headers + response_status + args + end
2582
decoder, response_handler = self.make_conventional_response_decoder()
2583
decoder.accept_bytes(message_bytes)
2584
error = self.assertRaises(
2585
errors.ErrorFromSmartServer, response_handler.read_response_tuple)
2586
self.assertEqual(('first arg', 'second arg'), error.error_tuple)
2589
class TestClientEncodingProtocolThree(TestSmartProtocol):
2591
request_encoder = protocol.ProtocolThreeRequester
2592
response_decoder = protocol.ProtocolThreeDecoder
2593
server_protocol_class = protocol.ProtocolThreeDecoder
2595
def make_client_encoder_and_output(self):
2596
result = self.make_client_protocol_and_output()
2597
requester, response_handler, output = result
2598
return requester, output
2600
def test_call_smoke_test(self):
2601
"""A smoke test for ProtocolThreeRequester.call.
2603
This test checks that a particular simple invocation of call emits the
2604
correct bytes for that invocation.
2606
requester, output = self.make_client_encoder_and_output()
2607
requester.set_headers({'header name': 'header value'})
2608
requester.call('one arg')
2610
'bzr message 3 (bzr 1.6)\n' # protocol version
2611
'\x00\x00\x00\x1fd11:header name12:header valuee' # headers
2612
's\x00\x00\x00\x0bl7:one arge' # args
2616
def test_call_with_body_bytes_smoke_test(self):
2617
"""A smoke test for ProtocolThreeRequester.call_with_body_bytes.
2619
This test checks that a particular simple invocation of
2620
call_with_body_bytes emits the correct bytes for that invocation.
2622
requester, output = self.make_client_encoder_and_output()
2623
requester.set_headers({'header name': 'header value'})
2624
requester.call_with_body_bytes(('one arg',), 'body bytes')
2626
'bzr message 3 (bzr 1.6)\n' # protocol version
2627
'\x00\x00\x00\x1fd11:header name12:header valuee' # headers
2628
's\x00\x00\x00\x0bl7:one arge' # args
2629
'b' # there is a prefixed body
2630
'\x00\x00\x00\nbody bytes' # the prefixed body
2634
def test_call_writes_just_once(self):
2635
"""A bodyless request is written to the medium all at once."""
2636
medium_request = StubMediumRequest()
2637
encoder = protocol.ProtocolThreeRequester(medium_request)
2638
encoder.call('arg1', 'arg2', 'arg3')
2640
['accept_bytes', 'finished_writing'], medium_request.calls)
2642
def test_call_with_body_bytes_writes_just_once(self):
2643
"""A request with body bytes is written to the medium all at once."""
2644
medium_request = StubMediumRequest()
2645
encoder = protocol.ProtocolThreeRequester(medium_request)
2646
encoder.call_with_body_bytes(('arg', 'arg'), 'body bytes')
2648
['accept_bytes', 'finished_writing'], medium_request.calls)
2651
class StubMediumRequest(object):
2652
"""A stub medium request that tracks the number of times accept_bytes is
2658
self._medium = 'dummy medium'
2660
def accept_bytes(self, bytes):
2661
self.calls.append('accept_bytes')
2663
def finished_writing(self):
2664
self.calls.append('finished_writing')
2667
class TestResponseEncodingProtocolThree(tests.TestCase):
2669
def make_response_encoder(self):
2670
out_stream = StringIO()
2671
response_encoder = protocol.ProtocolThreeResponder(out_stream.write)
2672
return response_encoder, out_stream
2674
def test_send_error_unknown_method(self):
2675
encoder, out_stream = self.make_response_encoder()
2676
encoder.send_error(errors.UnknownSmartMethod('method name'))
2677
# Use assertEndsWith so that we don't compare the header, which varies
2678
# by bzrlib.__version__.
2679
self.assertEndsWith(
2680
out_stream.getvalue(),
2683
# tuple: 'UnknownMethod', 'method name'
2684
's\x00\x00\x00\x20l13:UnknownMethod11:method namee'
2689
class TestResponseEncoderBufferingProtocolThree(tests.TestCase):
2690
"""Tests for buffering of responses.
2692
We want to avoid doing many small writes when one would do, to avoid
2693
unnecessary network overhead.
2698
self.responder = protocol.ProtocolThreeResponder(self.writes.append)
2700
def assertWriteCount(self, expected_count):
2702
expected_count, len(self.writes),
2703
"Too many writes: %r" % (self.writes,))
2705
def test_send_error_writes_just_once(self):
2706
"""An error response is written to the medium all at once."""
2707
self.responder.send_error(Exception('An exception string.'))
2708
self.assertWriteCount(1)
2710
def test_send_response_writes_just_once(self):
2711
"""A normal response with no body is written to the medium all at once.
2713
response = _mod_request.SuccessfulSmartServerResponse(('arg', 'arg'))
2714
self.responder.send_response(response)
2715
self.assertWriteCount(1)
2717
def test_send_response_with_body_writes_just_once(self):
2718
"""A normal response with a monolithic body is written to the medium
2721
response = _mod_request.SuccessfulSmartServerResponse(
2722
('arg', 'arg'), body='body bytes')
2723
self.responder.send_response(response)
2724
self.assertWriteCount(1)
2726
def test_send_response_with_body_stream_writes_once_per_chunk(self):
2727
"""A normal response with a stream body is written to the medium
2728
writes to the medium once per chunk.
2730
# Construct a response with stream with 2 chunks in it.
2731
response = _mod_request.SuccessfulSmartServerResponse(
2732
('arg', 'arg'), body_stream=['chunk1', 'chunk2'])
2733
self.responder.send_response(response)
2734
# We will write 3 times: exactly once for each chunk, plus a final
2735
# write to end the response.
2736
self.assertWriteCount(3)
2030
2739
class TestSmartClientUnicode(tests.TestCase):
2031
2740
"""_SmartClient tests for unicode arguments.
2064
2774
self.assertCallDoesNotBreakMedium('method', ('args',), u'body')
2777
class MockMedium(object):
2778
"""A mock medium that can be used to test _SmartClient.
2780
It can be given a series of requests to expect (and responses it should
2781
return for them). It can also be told when the client is expected to
2782
disconnect a medium. Expectations must be satisfied in the order they are
2783
given, or else an AssertionError will be raised.
2785
Typical use looks like::
2787
medium = MockMedium()
2788
medium.expect_request(...)
2789
medium.expect_request(...)
2790
medium.expect_request(...)
2794
self.base = 'dummy base'
2795
self._mock_request = _MockMediumRequest(self)
2796
self._expected_events = []
2797
self._protocol_version = None
2799
def expect_request(self, request_bytes, response_bytes,
2800
allow_partial_read=False):
2801
"""Expect 'request_bytes' to be sent, and reply with 'response_bytes'.
2803
No assumption is made about how many times accept_bytes should be
2804
called to send the request. Similarly, no assumption is made about how
2805
many times read_bytes/read_line are called by protocol code to read a
2808
request.accept_bytes('ab')
2809
request.accept_bytes('cd')
2810
request.finished_writing()
2814
request.accept_bytes('abcd')
2815
request.finished_writing()
2817
Will both satisfy ``medium.expect_request('abcd', ...)``. Thus tests
2818
using this should not break due to irrelevant changes in protocol
2821
:param allow_partial_read: if True, no assertion is raised if a
2822
response is not fully read. Setting this is useful when the client
2823
is expected to disconnect without needing to read the complete
2824
response. Default is False.
2826
self._expected_events.append(('send request', request_bytes))
2827
if allow_partial_read:
2828
self._expected_events.append(
2829
('read response (partial)', response_bytes))
2831
self._expected_events.append(('read response', response_bytes))
2833
def expect_disconnect(self):
2834
"""Expect the client to call ``medium.disconnect()``."""
2835
self._expected_events.append('disconnect')
2837
def _assertEvent(self, observed_event):
2838
"""Raise AssertionError unless observed_event matches the next expected
2841
:seealso: expect_request
2842
:seealso: expect_disconnect
2845
expected_event = self._expected_events.pop(0)
2847
raise AssertionError(
2848
'Mock medium observed event %r, but no more events expected'
2849
% (observed_event,))
2850
if expected_event[0] == 'read response (partial)':
2851
if observed_event[0] != 'read response':
2852
raise AssertionError(
2853
'Mock medium observed event %r, but expected event %r'
2854
% (observed_event, expected_event))
2855
elif observed_event != expected_event:
2856
raise AssertionError(
2857
'Mock medium observed event %r, but expected event %r'
2858
% (observed_event, expected_event))
2859
if self._expected_events:
2860
next_event = self._expected_events[0]
2861
if next_event[0].startswith('read response'):
2862
self._mock_request._response = next_event[1]
2864
def get_request(self):
2865
return self._mock_request
2867
def disconnect(self):
2868
if self._mock_request._read_bytes:
2869
self._assertEvent(('read response', self._mock_request._read_bytes))
2870
self._mock_request._read_bytes = ''
2871
self._assertEvent('disconnect')
2874
class _MockMediumRequest(object):
2875
"""A mock ClientMediumRequest used by MockMedium."""
2877
def __init__(self, mock_medium):
2878
self._medium = mock_medium
2879
self._written_bytes = ''
2880
self._read_bytes = ''
2881
self._response = None
2883
def accept_bytes(self, bytes):
2884
self._written_bytes += bytes
2886
def finished_writing(self):
2887
self._medium._assertEvent(('send request', self._written_bytes))
2888
self._written_bytes = ''
2890
def finished_reading(self):
2891
self._medium._assertEvent(('read response', self._read_bytes))
2892
self._read_bytes = ''
2894
def read_bytes(self, size):
2895
resp = self._response
2896
bytes, resp = resp[:size], resp[size:]
2897
self._response = resp
2898
self._read_bytes += bytes
2901
def read_line(self):
2902
resp = self._response
2904
line, resp = resp.split('\n', 1)
2907
line, resp = resp, ''
2908
self._response = resp
2909
self._read_bytes += line
2913
class Test_SmartClientVersionDetection(tests.TestCase):
2914
"""Tests for _SmartClient's automatic protocol version detection.
2916
On the first remote call, _SmartClient will keep retrying the request with
2917
different protocol versions until it finds one that works.
2920
def test_version_three_server(self):
2921
"""With a protocol 3 server, only one request is needed."""
2922
medium = MockMedium()
2923
smart_client = client._SmartClient(medium, headers={})
2924
message_start = protocol.MESSAGE_VERSION_THREE + '\x00\x00\x00\x02de'
2925
medium.expect_request(
2927
's\x00\x00\x00\x1el11:method-name5:arg 15:arg 2ee',
2928
message_start + 's\0\0\0\x13l14:response valueee')
2929
result = smart_client.call('method-name', 'arg 1', 'arg 2')
2930
# The call succeeded without raising any exceptions from the mock
2931
# medium, and the smart_client returns the response from the server.
2932
self.assertEqual(('response value',), result)
2933
self.assertEqual([], medium._expected_events)
2935
def test_version_two_server(self):
2936
"""If the server only speaks protocol 2, the client will first try
2937
version 3, then fallback to protocol 2.
2939
Further, _SmartClient caches the detection, so future requests will all
2940
use protocol 2 immediately.
2942
medium = MockMedium()
2943
smart_client = client._SmartClient(medium, headers={})
2944
# First the client should send a v3 request, but the server will reply
2946
medium.expect_request(
2947
'bzr message 3 (bzr 1.6)\n\x00\x00\x00\x02de' +
2948
's\x00\x00\x00\x1el11:method-name5:arg 15:arg 2ee',
2949
'bzr response 2\nfailed\n\n')
2950
# So then the client should disconnect to reset the connection, because
2951
# the client needs to assume the server cannot read any further
2952
# requests off the original connection.
2953
medium.expect_disconnect()
2954
# The client should then retry the original request in v2
2955
medium.expect_request(
2956
'bzr request 2\nmethod-name\x01arg 1\x01arg 2\n',
2957
'bzr response 2\nsuccess\nresponse value\n')
2958
result = smart_client.call('method-name', 'arg 1', 'arg 2')
2959
# The smart_client object will return the result of the successful
2961
self.assertEqual(('response value',), result)
2963
# Now try another request, and this time the client will just use
2964
# protocol 2. (i.e. the autodetection won't be repeated)
2965
medium.expect_request(
2966
'bzr request 2\nanother-method\n',
2967
'bzr response 2\nsuccess\nanother response\n')
2968
result = smart_client.call('another-method')
2969
self.assertEqual(('another response',), result)
2970
self.assertEqual([], medium._expected_events)
2972
def test_unknown_version(self):
2973
"""If the server does not use any known (or at least supported)
2974
protocol version, a SmartProtocolError is raised.
2976
medium = MockMedium()
2977
smart_client = client._SmartClient(medium, headers={})
2978
unknown_protocol_bytes = 'Unknown protocol!'
2979
# The client will try v3 and v2 before eventually giving up.
2980
medium.expect_request(
2981
'bzr message 3 (bzr 1.6)\n\x00\x00\x00\x02de' +
2982
's\x00\x00\x00\x1el11:method-name5:arg 15:arg 2ee',
2983
unknown_protocol_bytes)
2984
medium.expect_disconnect()
2985
medium.expect_request(
2986
'bzr request 2\nmethod-name\x01arg 1\x01arg 2\n',
2987
unknown_protocol_bytes)
2988
medium.expect_disconnect()
2990
errors.SmartProtocolError,
2991
smart_client.call, 'method-name', 'arg 1', 'arg 2')
2992
self.assertEqual([], medium._expected_events)
2994
def test_first_response_is_error(self):
2995
"""If the server replies with an error, then the version detection
2998
This test is very similar to test_version_two_server, but catches a bug
2999
we had in the case where the first reply was an error response.
3001
medium = MockMedium()
3002
smart_client = client._SmartClient(medium, headers={})
3003
message_start = protocol.MESSAGE_VERSION_THREE + '\x00\x00\x00\x02de'
3004
# Issue a request that gets an error reply in a non-default protocol
3006
medium.expect_request(
3008
's\x00\x00\x00\x10l11:method-nameee',
3009
'bzr response 2\nfailed\n\n')
3010
medium.expect_disconnect()
3011
medium.expect_request(
3012
'bzr request 2\nmethod-name\n',
3013
'bzr response 2\nfailed\nFooBarError\n')
3014
err = self.assertRaises(
3015
errors.ErrorFromSmartServer,
3016
smart_client.call, 'method-name')
3017
self.assertEqual(('FooBarError',), err.error_tuple)
3018
# Now the medium should have remembered the protocol version, so
3019
# subsequent requests will use the remembered version immediately.
3020
medium.expect_request(
3021
'bzr request 2\nmethod-name\n',
3022
'bzr response 2\nsuccess\nresponse value\n')
3023
result = smart_client.call('method-name')
3024
self.assertEqual(('response value',), result)
3025
self.assertEqual([], medium._expected_events)
3028
class Test_SmartClient(tests.TestCase):
3030
def test_call_default_headers(self):
3031
"""ProtocolThreeRequester.call by default sends a 'Software
3034
smart_client = client._SmartClient('dummy medium')
3036
bzrlib.__version__, smart_client._headers['Software version'])
3037
# XXX: need a test that smart_client._headers is passed to the request
2067
3041
class LengthPrefixedBodyDecoder(tests.TestCase):
2069
3043
# XXX: TODO: make accept_reading_trailer invoke translate_response or