/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
1
# Copyright (C) 2006, 2007 Canonical Ltd
1752.2.30 by Martin Pool
Start adding a RemoteBzrDir, etc
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 remote bzrdir/branch/repo/etc
18
19
These are proxy objects which act on remote objects by sending messages
20
through a smart client.  The proxies are to be created when attempting to open
21
the object given a transport that supports smartserver rpc operations. 
2018.18.7 by Martin Pool
(broken) Start addng client proxy test for Repository.tarball
22
23
These tests correspond to tests.test_smart, which exercises the server side.
1752.2.30 by Martin Pool
Start adding a RemoteBzrDir, etc
24
"""
25
3211.5.2 by Robert Collins
Change RemoteRepository.get_parent_map to use bz2 not gzip for compression.
26
import bz2
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
27
from cStringIO import StringIO
28
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
29
from bzrlib import (
30
    errors,
3184.1.9 by Robert Collins
* ``Repository.get_data_stream`` is now deprecated in favour of
31
    graph,
2535.3.39 by Andrew Bennetts
Tidy some XXXs.
32
    pack,
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
33
    remote,
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
34
    repository,
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
35
    tests,
36
    )
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
37
from bzrlib.branch import Branch
38
from bzrlib.bzrdir import BzrDir, BzrDirFormat
39
from bzrlib.remote import (
40
    RemoteBranch,
41
    RemoteBzrDir,
42
    RemoteBzrDirFormat,
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
43
    RemoteRepository,
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
44
    )
45
from bzrlib.revision import NULL_REVISION
2432.3.2 by Andrew Bennetts
Add test, and tidy implementation.
46
from bzrlib.smart import server, medium
2018.5.159 by Andrew Bennetts
Rename SmartClient to _SmartClient.
47
from bzrlib.smart.client import _SmartClient
3287.6.4 by Robert Collins
Fix up deprecation warnings for get_revision_graph.
48
from bzrlib.symbol_versioning import one_four
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
49
from bzrlib.transport.memory import MemoryTransport
2466.2.2 by Andrew Bennetts
Add tests for RemoteTransport.is_readonly in the style of the other remote object tests.
50
from bzrlib.transport.remote import RemoteTransport
1752.2.30 by Martin Pool
Start adding a RemoteBzrDir, etc
51
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)
52
2018.5.42 by Robert Collins
Various hopefully improvements, but wsgi is broken, handing over to spiv :).
53
class BasicRemoteObjectTests(tests.TestCaseWithTransport):
1752.2.30 by Martin Pool
Start adding a RemoteBzrDir, etc
54
55
    def setUp(self):
2018.5.95 by Andrew Bennetts
Add a Transport.is_readonly remote call, let {Branch,Repository}.lock_write remote call return UnlockableTransport, and miscellaneous test fixes.
56
        self.transport_server = server.SmartTCPServer_for_testing
2018.5.42 by Robert Collins
Various hopefully improvements, but wsgi is broken, handing over to spiv :).
57
        super(BasicRemoteObjectTests, self).setUp()
58
        self.transport = self.get_transport()
1752.2.30 by Martin Pool
Start adding a RemoteBzrDir, etc
59
        # make a branch that can be opened over the smart transport
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
60
        self.local_wt = BzrDir.create_standalone_workingtree('.')
1752.2.30 by Martin Pool
Start adding a RemoteBzrDir, etc
61
2018.5.171 by Andrew Bennetts
Disconnect RemoteTransports in some tests to avoid tripping up test_strace with leftover threads from previous tests.
62
    def tearDown(self):
63
        self.transport.disconnect()
64
        tests.TestCaseWithTransport.tearDown(self)
65
1752.2.30 by Martin Pool
Start adding a RemoteBzrDir, etc
66
    def test_create_remote_bzrdir(self):
67
        b = remote.RemoteBzrDir(self.transport)
68
        self.assertIsInstance(b, BzrDir)
69
70
    def test_open_remote_branch(self):
2018.6.1 by Robert Collins
Implement a BzrDir.open_branch smart server method for opening a branch without VFS.
71
        # open a standalone branch in the working directory
1752.2.30 by Martin Pool
Start adding a RemoteBzrDir, etc
72
        b = remote.RemoteBzrDir(self.transport)
73
        branch = b.open_branch()
2018.5.163 by Andrew Bennetts
Deal with various review comments from Robert.
74
        self.assertIsInstance(branch, Branch)
1752.2.30 by Martin Pool
Start adding a RemoteBzrDir, etc
75
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
76
    def test_remote_repository(self):
77
        b = BzrDir.open_from_transport(self.transport)
78
        repo = b.open_repository()
2018.5.106 by Andrew Bennetts
Update tests in test_remote to use utf-8 byte strings for revision IDs, rather than unicode strings.
79
        revid = u'\xc823123123'.encode('utf8')
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
80
        self.assertFalse(repo.has_revision(revid))
81
        self.local_wt.commit(message='test commit', rev_id=revid)
82
        self.assertTrue(repo.has_revision(revid))
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
83
84
    def test_remote_branch_revision_history(self):
85
        b = BzrDir.open_from_transport(self.transport).open_branch()
2018.5.38 by Robert Collins
Implement RemoteBranch.revision_history().
86
        self.assertEqual([], b.revision_history())
87
        r1 = self.local_wt.commit('1st commit')
2018.5.106 by Andrew Bennetts
Update tests in test_remote to use utf-8 byte strings for revision IDs, rather than unicode strings.
88
        r2 = self.local_wt.commit('1st commit', rev_id=u'\xc8'.encode('utf8'))
2018.5.38 by Robert Collins
Implement RemoteBranch.revision_history().
89
        self.assertEqual([r1, r2], b.revision_history())
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
90
1752.2.30 by Martin Pool
Start adding a RemoteBzrDir, etc
91
    def test_find_correct_format(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)
92
        """Should open a RemoteBzrDir over a RemoteTransport"""
1752.2.30 by Martin Pool
Start adding a RemoteBzrDir, etc
93
        fmt = BzrDirFormat.find_format(self.transport)
2018.5.169 by Andrew Bennetts
Add a _server_formats flag to BzrDir.open_from_transport and BzrDirFormat.find_format, make RemoteBranch.control_files into a property.
94
        self.assertTrue(RemoteBzrDirFormat
95
                        in BzrDirFormat._control_server_formats)
1752.2.30 by Martin Pool
Start adding a RemoteBzrDir, etc
96
        self.assertIsInstance(fmt, remote.RemoteBzrDirFormat)
97
98
    def test_open_detected_smart_format(self):
99
        fmt = BzrDirFormat.find_format(self.transport)
100
        d = fmt.open(self.transport)
101
        self.assertIsInstance(d, BzrDir)
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
102
2477.1.1 by Martin Pool
Add RemoteBranch repr
103
    def test_remote_branch_repr(self):
104
        b = BzrDir.open_from_transport(self.transport).open_branch()
105
        self.assertStartsWith(str(b), 'RemoteBranch(')
106
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
107
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
108
class FakeProtocol(object):
109
    """Lookalike SmartClientRequestProtocolOne allowing body reading tests."""
110
2535.3.68 by Andrew Bennetts
Backwards compatibility for new smart method.
111
    def __init__(self, body, fake_client):
2535.4.2 by Andrew Bennetts
Nasty hackery to make stream_knit_data_for_revisions response use streaming.
112
        self.body = body
113
        self._body_buffer = None
2535.3.68 by Andrew Bennetts
Backwards compatibility for new smart method.
114
        self._fake_client = fake_client
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
115
116
    def read_body_bytes(self, count=-1):
2535.4.2 by Andrew Bennetts
Nasty hackery to make stream_knit_data_for_revisions response use streaming.
117
        if self._body_buffer is None:
118
            self._body_buffer = StringIO(self.body)
2535.3.68 by Andrew Bennetts
Backwards compatibility for new smart method.
119
        bytes = self._body_buffer.read(count)
120
        if self._body_buffer.tell() == len(self._body_buffer.getvalue()):
121
            self._fake_client.expecting_body = False
122
        return bytes
123
124
    def cancel_read_body(self):
125
        self._fake_client.expecting_body = False
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
126
2535.4.2 by Andrew Bennetts
Nasty hackery to make stream_knit_data_for_revisions response use streaming.
127
    def read_streamed_body(self):
128
        return self.body
129
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
130
2018.5.159 by Andrew Bennetts
Rename SmartClient to _SmartClient.
131
class FakeClient(_SmartClient):
132
    """Lookalike for _SmartClient allowing testing."""
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
133
    
3104.4.2 by Andrew Bennetts
All tests passing.
134
    def __init__(self, responses, fake_medium_base='fake base'):
2535.3.68 by Andrew Bennetts
Backwards compatibility for new smart method.
135
        """Create a FakeClient.
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
136
3104.4.2 by Andrew Bennetts
All tests passing.
137
        :param responses: A list of response-tuple, body-data pairs to be sent
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
138
            back to callers.
139
        """
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
140
        self.responses = responses
141
        self._calls = []
2535.3.68 by Andrew Bennetts
Backwards compatibility for new smart method.
142
        self.expecting_body = False
3213.1.2 by Andrew Bennetts
Add test for reconnection if get_parent_map is unknown by the server.
143
        _SmartClient.__init__(self, FakeMedium(fake_medium_base, self._calls))
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
144
145
    def call(self, method, *args):
146
        self._calls.append(('call', method, args))
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
147
        return self.responses.pop(0)[0]
148
2018.5.153 by Andrew Bennetts
Rename call2 to call_expecting_body, and other small changes prompted by review.
149
    def call_expecting_body(self, method, *args):
150
        self._calls.append(('call_expecting_body', method, args))
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
151
        result = self.responses.pop(0)
2535.3.68 by Andrew Bennetts
Backwards compatibility for new smart method.
152
        self.expecting_body = True
153
        return result[0], FakeProtocol(result[1], self)
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
154
3184.1.10 by Robert Collins
Change the smart server verb for Repository.stream_revisions_chunked to use SearchResults as the request mechanism for downloads.
155
    def call_with_body_bytes_expecting_body(self, method, args, body):
156
        self._calls.append(('call_with_body_bytes_expecting_body', method,
157
            args, body))
158
        result = self.responses.pop(0)
159
        self.expecting_body = True
160
        return result[0], FakeProtocol(result[1], self)
161
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
162
3104.4.2 by Andrew Bennetts
All tests passing.
163
class FakeMedium(object):
164
3213.1.2 by Andrew Bennetts
Add test for reconnection if get_parent_map is unknown by the server.
165
    def __init__(self, base, client_calls):
3104.4.2 by Andrew Bennetts
All tests passing.
166
        self.base = base
3213.1.2 by Andrew Bennetts
Add test for reconnection if get_parent_map is unknown by the server.
167
        self.connection = FakeConnection(client_calls)
168
        self._client_calls = client_calls
3213.1.1 by Andrew Bennetts
Recover (by reconnecting) if the server turns out not to understand the new requests in 1.2 that send bodies.
169
170
171
class FakeConnection(object):
172
3213.1.2 by Andrew Bennetts
Add test for reconnection if get_parent_map is unknown by the server.
173
    def __init__(self, client_calls):
3213.1.1 by Andrew Bennetts
Recover (by reconnecting) if the server turns out not to understand the new requests in 1.2 that send bodies.
174
        self._remote_is_at_least_1_2 = True
3213.1.2 by Andrew Bennetts
Add test for reconnection if get_parent_map is unknown by the server.
175
        self._client_calls = client_calls
3213.1.1 by Andrew Bennetts
Recover (by reconnecting) if the server turns out not to understand the new requests in 1.2 that send bodies.
176
177
    def disconnect(self):
3213.1.2 by Andrew Bennetts
Add test for reconnection if get_parent_map is unknown by the server.
178
        self._client_calls.append(('disconnect medium',))
3104.4.2 by Andrew Bennetts
All tests passing.
179
180
3192.2.1 by Andrew Bennetts
Don't transmit URL-escaped relpaths in the smart protocol, which is back to how things worked in bzr 1.1 and earlier.
181
class TestVfsHas(tests.TestCase):
182
183
    def test_unicode_path(self):
184
        client = FakeClient([(('yes',), )], '/')
185
        transport = RemoteTransport('bzr://localhost/', _client=client)
186
        filename = u'/hell\u00d8'.encode('utf8')
187
        result = transport.has(filename)
188
        self.assertEqual(
189
            [('call', 'has', (filename,))],
190
            client._calls)
191
        self.assertTrue(result)
192
193
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
194
class TestBzrDirOpenBranch(tests.TestCase):
195
196
    def test_branch_present(self):
197
        transport = MemoryTransport()
198
        transport.mkdir('quack')
199
        transport = transport.clone('quack')
3221.3.3 by Robert Collins
* Hook up the new remote method ``RemoteBzrDir.find_repositoryV2`` so
200
        client = FakeClient([(('ok', ''), ), (('ok', '', 'no', 'no', 'no'), )],
3104.4.2 by Andrew Bennetts
All tests passing.
201
                            transport.base)
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
202
        bzrdir = RemoteBzrDir(transport, _client=client)
203
        result = bzrdir.open_branch()
204
        self.assertEqual(
3104.4.2 by Andrew Bennetts
All tests passing.
205
            [('call', 'BzrDir.open_branch', ('quack/',)),
3221.3.3 by Robert Collins
* Hook up the new remote method ``RemoteBzrDir.find_repositoryV2`` so
206
             ('call', 'BzrDir.find_repositoryV2', ('quack/',))],
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
207
            client._calls)
208
        self.assertIsInstance(result, RemoteBranch)
209
        self.assertEqual(bzrdir, result.bzrdir)
210
211
    def test_branch_missing(self):
212
        transport = MemoryTransport()
213
        transport.mkdir('quack')
214
        transport = transport.clone('quack')
3104.4.2 by Andrew Bennetts
All tests passing.
215
        client = FakeClient([(('nobranch',), )], transport.base)
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
216
        bzrdir = RemoteBzrDir(transport, _client=client)
217
        self.assertRaises(errors.NotBranchError, bzrdir.open_branch)
218
        self.assertEqual(
3104.4.2 by Andrew Bennetts
All tests passing.
219
            [('call', 'BzrDir.open_branch', ('quack/',))],
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
220
            client._calls)
221
3211.4.1 by Robert Collins
* ``RemoteBzrDir._get_tree_branch`` no longer triggers ``_ensure_real``,
222
    def test__get_tree_branch(self):
223
        # _get_tree_branch is a form of open_branch, but it should only ask for
224
        # branch opening, not any other network requests.
225
        calls = []
226
        def open_branch():
227
            calls.append("Called")
228
            return "a-branch"
229
        transport = MemoryTransport()
230
        # no requests on the network - catches other api calls being made.
231
        client = FakeClient([], transport.base)
232
        bzrdir = RemoteBzrDir(transport, _client=client)
233
        # patch the open_branch call to record that it was called.
234
        bzrdir.open_branch = open_branch
235
        self.assertEqual((None, "a-branch"), bzrdir._get_tree_branch())
236
        self.assertEqual(["Called"], calls)
237
        self.assertEqual([], client._calls)
238
3192.2.1 by Andrew Bennetts
Don't transmit URL-escaped relpaths in the smart protocol, which is back to how things worked in bzr 1.1 and earlier.
239
    def test_url_quoting_of_path(self):
240
        # Relpaths on the wire should not be URL-escaped.  So "~" should be
241
        # transmitted as "~", not "%7E".
242
        transport = RemoteTransport('bzr://localhost/~hello/')
3221.3.3 by Robert Collins
* Hook up the new remote method ``RemoteBzrDir.find_repositoryV2`` so
243
        client = FakeClient([(('ok', ''), ), (('ok', '', 'no', 'no', 'no'), )],
3192.2.1 by Andrew Bennetts
Don't transmit URL-escaped relpaths in the smart protocol, which is back to how things worked in bzr 1.1 and earlier.
244
                            transport.base)
245
        bzrdir = RemoteBzrDir(transport, _client=client)
246
        result = bzrdir.open_branch()
247
        self.assertEqual(
248
            [('call', 'BzrDir.open_branch', ('~hello/',)),
3221.3.3 by Robert Collins
* Hook up the new remote method ``RemoteBzrDir.find_repositoryV2`` so
249
             ('call', 'BzrDir.find_repositoryV2', ('~hello/',))],
3192.2.1 by Andrew Bennetts
Don't transmit URL-escaped relpaths in the smart protocol, which is back to how things worked in bzr 1.1 and earlier.
250
            client._calls)
251
3221.3.3 by Robert Collins
* Hook up the new remote method ``RemoteBzrDir.find_repositoryV2`` so
252
    def check_open_repository(self, rich_root, subtrees, external_lookup='no'):
3104.4.2 by Andrew Bennetts
All tests passing.
253
        transport = MemoryTransport()
254
        transport.mkdir('quack')
255
        transport = transport.clone('quack')
2018.5.118 by Robert Collins
Fix RemoteRepositoryFormat to have appropriate rich_root_data and support_tree_reference.
256
        if rich_root:
2018.5.166 by Andrew Bennetts
Small changes in response to Aaron's review.
257
            rich_response = 'yes'
2018.5.118 by Robert Collins
Fix RemoteRepositoryFormat to have appropriate rich_root_data and support_tree_reference.
258
        else:
2018.5.166 by Andrew Bennetts
Small changes in response to Aaron's review.
259
            rich_response = 'no'
2018.5.118 by Robert Collins
Fix RemoteRepositoryFormat to have appropriate rich_root_data and support_tree_reference.
260
        if subtrees:
2018.5.166 by Andrew Bennetts
Small changes in response to Aaron's review.
261
            subtree_response = 'yes'
2018.5.118 by Robert Collins
Fix RemoteRepositoryFormat to have appropriate rich_root_data and support_tree_reference.
262
        else:
2018.5.166 by Andrew Bennetts
Small changes in response to Aaron's review.
263
            subtree_response = 'no'
3221.3.3 by Robert Collins
* Hook up the new remote method ``RemoteBzrDir.find_repositoryV2`` so
264
        client = FakeClient(
265
            [(('ok', '', rich_response, subtree_response, external_lookup), ),],
266
            transport.base)
2018.5.118 by Robert Collins
Fix RemoteRepositoryFormat to have appropriate rich_root_data and support_tree_reference.
267
        bzrdir = RemoteBzrDir(transport, _client=client)
268
        result = bzrdir.open_repository()
269
        self.assertEqual(
3221.3.3 by Robert Collins
* Hook up the new remote method ``RemoteBzrDir.find_repositoryV2`` so
270
            [('call', 'BzrDir.find_repositoryV2', ('quack/',))],
2018.5.118 by Robert Collins
Fix RemoteRepositoryFormat to have appropriate rich_root_data and support_tree_reference.
271
            client._calls)
272
        self.assertIsInstance(result, RemoteRepository)
273
        self.assertEqual(bzrdir, result.bzrdir)
274
        self.assertEqual(rich_root, result._format.rich_root_data)
2018.5.138 by Robert Collins
Merge bzr.dev.
275
        self.assertEqual(subtrees, result._format.supports_tree_reference)
2018.5.118 by Robert Collins
Fix RemoteRepositoryFormat to have appropriate rich_root_data and support_tree_reference.
276
277
    def test_open_repository_sets_format_attributes(self):
278
        self.check_open_repository(True, True)
279
        self.check_open_repository(False, True)
280
        self.check_open_repository(True, False)
281
        self.check_open_repository(False, False)
3221.3.3 by Robert Collins
* Hook up the new remote method ``RemoteBzrDir.find_repositoryV2`` so
282
        self.check_open_repository(False, False, 'yes')
2018.5.118 by Robert Collins
Fix RemoteRepositoryFormat to have appropriate rich_root_data and support_tree_reference.
283
2432.3.2 by Andrew Bennetts
Add test, and tidy implementation.
284
    def test_old_server(self):
285
        """RemoteBzrDirFormat should fail to probe if the server version is too
286
        old.
287
        """
288
        self.assertRaises(errors.NotBranchError,
289
            RemoteBzrDirFormat.probe_transport, OldServerTransport())
290
291
292
class OldSmartClient(object):
293
    """A fake smart client for test_old_version that just returns a version one
294
    response to the 'hello' (query version) command.
295
    """
296
297
    def get_request(self):
298
        input_file = StringIO('ok\x011\n')
299
        output_file = StringIO()
300
        client_medium = medium.SmartSimplePipesClientMedium(
301
            input_file, output_file)
302
        return medium.SmartClientStreamMediumRequest(client_medium)
303
3241.1.1 by Andrew Bennetts
Shift protocol version querying from RemoteBzrDirFormat into SmartClientMedium.
304
    def protocol_version(self):
305
        return 1
306
2432.3.2 by Andrew Bennetts
Add test, and tidy implementation.
307
308
class OldServerTransport(object):
309
    """A fake transport for test_old_server that reports it's smart server
310
    protocol version as version one.
311
    """
312
313
    def __init__(self):
314
        self.base = 'fake:'
315
316
    def get_smart_client(self):
317
        return OldSmartClient()
318
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
319
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
320
class TestBranchLastRevisionInfo(tests.TestCase):
321
322
    def test_empty_branch(self):
323
        # in an empty branch we decode the response properly
324
        transport = MemoryTransport()
3104.4.2 by Andrew Bennetts
All tests passing.
325
        client = FakeClient([(('ok', '0', 'null:'), )], transport.base)
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
326
        transport.mkdir('quack')
327
        transport = transport.clone('quack')
328
        # we do not want bzrdir to make any remote calls
329
        bzrdir = RemoteBzrDir(transport, _client=False)
330
        branch = RemoteBranch(bzrdir, None, _client=client)
331
        result = branch.last_revision_info()
332
333
        self.assertEqual(
3104.4.2 by Andrew Bennetts
All tests passing.
334
            [('call', 'Branch.last_revision_info', ('quack/',))],
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
335
            client._calls)
336
        self.assertEqual((0, NULL_REVISION), result)
337
338
    def test_non_empty_branch(self):
339
        # in a non-empty branch we also decode the response properly
2018.5.106 by Andrew Bennetts
Update tests in test_remote to use utf-8 byte strings for revision IDs, rather than unicode strings.
340
        revid = u'\xc8'.encode('utf8')
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
341
        transport = MemoryTransport()
3104.4.2 by Andrew Bennetts
All tests passing.
342
        client = FakeClient([(('ok', '2', revid), )], transport.base)
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
343
        transport.mkdir('kwaak')
344
        transport = transport.clone('kwaak')
345
        # we do not want bzrdir to make any remote calls
346
        bzrdir = RemoteBzrDir(transport, _client=False)
347
        branch = RemoteBranch(bzrdir, None, _client=client)
348
        result = branch.last_revision_info()
349
350
        self.assertEqual(
3104.4.2 by Andrew Bennetts
All tests passing.
351
            [('call', 'Branch.last_revision_info', ('kwaak/',))],
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
352
            client._calls)
2018.5.106 by Andrew Bennetts
Update tests in test_remote to use utf-8 byte strings for revision IDs, rather than unicode strings.
353
        self.assertEqual((2, revid), result)
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
354
355
2018.12.3 by Andrew Bennetts
Add a Branch.set_last_revision smart method, and make RemoteBranch.set_revision_history use it.
356
class TestBranchSetLastRevision(tests.TestCase):
357
358
    def test_set_empty(self):
359
        # set_revision_history([]) is translated to calling
360
        # Branch.set_last_revision(path, '') on the wire.
3104.4.2 by Andrew Bennetts
All tests passing.
361
        transport = MemoryTransport()
362
        transport.mkdir('branch')
363
        transport = transport.clone('branch')
364
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
365
        client = FakeClient([
366
            # lock_write
367
            (('ok', 'branch token', 'repo token'), ),
368
            # set_last_revision
369
            (('ok',), ),
370
            # unlock
3104.4.2 by Andrew Bennetts
All tests passing.
371
            (('ok',), )],
372
            transport.base)
2018.12.3 by Andrew Bennetts
Add a Branch.set_last_revision smart method, and make RemoteBranch.set_revision_history use it.
373
        bzrdir = RemoteBzrDir(transport, _client=False)
374
        branch = RemoteBranch(bzrdir, None, _client=client)
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
375
        # This is a hack to work around the problem that RemoteBranch currently
376
        # unnecessarily invokes _ensure_real upon a call to lock_write.
377
        branch._ensure_real = lambda: None
378
        branch.lock_write()
379
        client._calls = []
2018.12.3 by Andrew Bennetts
Add a Branch.set_last_revision smart method, and make RemoteBranch.set_revision_history use it.
380
        result = branch.set_revision_history([])
381
        self.assertEqual(
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
382
            [('call', 'Branch.set_last_revision',
3104.4.2 by Andrew Bennetts
All tests passing.
383
                ('branch/', 'branch token', 'repo token', 'null:'))],
2018.12.3 by Andrew Bennetts
Add a Branch.set_last_revision smart method, and make RemoteBranch.set_revision_history use it.
384
            client._calls)
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
385
        branch.unlock()
2018.12.3 by Andrew Bennetts
Add a Branch.set_last_revision smart method, and make RemoteBranch.set_revision_history use it.
386
        self.assertEqual(None, result)
387
388
    def test_set_nonempty(self):
389
        # set_revision_history([rev-id1, ..., rev-idN]) is translated to calling
390
        # Branch.set_last_revision(path, rev-idN) on the wire.
3104.4.2 by Andrew Bennetts
All tests passing.
391
        transport = MemoryTransport()
392
        transport.mkdir('branch')
393
        transport = transport.clone('branch')
394
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
395
        client = FakeClient([
396
            # lock_write
397
            (('ok', 'branch token', 'repo token'), ),
398
            # set_last_revision
399
            (('ok',), ),
400
            # unlock
3104.4.2 by Andrew Bennetts
All tests passing.
401
            (('ok',), )],
402
            transport.base)
2018.12.3 by Andrew Bennetts
Add a Branch.set_last_revision smart method, and make RemoteBranch.set_revision_history use it.
403
        bzrdir = RemoteBzrDir(transport, _client=False)
404
        branch = RemoteBranch(bzrdir, None, _client=client)
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
405
        # This is a hack to work around the problem that RemoteBranch currently
406
        # unnecessarily invokes _ensure_real upon a call to lock_write.
407
        branch._ensure_real = lambda: None
408
        # Lock the branch, reset the record of remote calls.
409
        branch.lock_write()
410
        client._calls = []
2018.12.3 by Andrew Bennetts
Add a Branch.set_last_revision smart method, and make RemoteBranch.set_revision_history use it.
411
412
        result = branch.set_revision_history(['rev-id1', 'rev-id2'])
413
        self.assertEqual(
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
414
            [('call', 'Branch.set_last_revision',
3104.4.2 by Andrew Bennetts
All tests passing.
415
                ('branch/', 'branch token', 'repo token', 'rev-id2'))],
2018.12.3 by Andrew Bennetts
Add a Branch.set_last_revision smart method, and make RemoteBranch.set_revision_history use it.
416
            client._calls)
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
417
        branch.unlock()
2018.12.3 by Andrew Bennetts
Add a Branch.set_last_revision smart method, and make RemoteBranch.set_revision_history use it.
418
        self.assertEqual(None, result)
419
420
    def test_no_such_revision(self):
421
        # A response of 'NoSuchRevision' is translated into an exception.
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
422
        client = FakeClient([
423
            # lock_write
424
            (('ok', 'branch token', 'repo token'), ),
425
            # set_last_revision
426
            (('NoSuchRevision', 'rev-id'), ),
427
            # unlock
428
            (('ok',), )])
2018.12.3 by Andrew Bennetts
Add a Branch.set_last_revision smart method, and make RemoteBranch.set_revision_history use it.
429
        transport = MemoryTransport()
430
        transport.mkdir('branch')
431
        transport = transport.clone('branch')
432
433
        bzrdir = RemoteBzrDir(transport, _client=False)
434
        branch = RemoteBranch(bzrdir, None, _client=client)
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
435
        branch._ensure_real = lambda: None
436
        branch.lock_write()
437
        client._calls = []
2018.12.3 by Andrew Bennetts
Add a Branch.set_last_revision smart method, and make RemoteBranch.set_revision_history use it.
438
439
        self.assertRaises(
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
440
            errors.NoSuchRevision, branch.set_revision_history, ['rev-id'])
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
441
        branch.unlock()
2018.12.3 by Andrew Bennetts
Add a Branch.set_last_revision smart method, and make RemoteBranch.set_revision_history use it.
442
443
2018.5.169 by Andrew Bennetts
Add a _server_formats flag to BzrDir.open_from_transport and BzrDirFormat.find_format, make RemoteBranch.control_files into a property.
444
class TestBranchControlGetBranchConf(tests.TestCaseWithMemoryTransport):
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
445
    """Test branch.control_files api munging...
446
2018.5.169 by Andrew Bennetts
Add a _server_formats flag to BzrDir.open_from_transport and BzrDirFormat.find_format, make RemoteBranch.control_files into a property.
447
    We special case RemoteBranch.control_files.get('branch.conf') to
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
448
    call a specific API so that RemoteBranch's can intercept configuration
449
    file reading, allowing them to signal to the client about things like
450
    'email is configured for commits'.
451
    """
452
453
    def test_get_branch_conf(self):
454
        # in an empty branch we decode the response properly
3104.4.2 by Andrew Bennetts
All tests passing.
455
        client = FakeClient([(('ok', ), 'config file body')], self.get_url())
2018.5.169 by Andrew Bennetts
Add a _server_formats flag to BzrDir.open_from_transport and BzrDirFormat.find_format, make RemoteBranch.control_files into a property.
456
        # we need to make a real branch because the remote_branch.control_files
457
        # will trigger _ensure_real.
458
        branch = self.make_branch('quack')
459
        transport = branch.bzrdir.root_transport
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
460
        # we do not want bzrdir to make any remote calls
461
        bzrdir = RemoteBzrDir(transport, _client=False)
462
        branch = RemoteBranch(bzrdir, None, _client=client)
463
        result = branch.control_files.get('branch.conf')
464
        self.assertEqual(
3104.4.2 by Andrew Bennetts
All tests passing.
465
            [('call_expecting_body', 'Branch.get_config_file', ('quack/',))],
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
466
            client._calls)
467
        self.assertEqual('config file body', result.read())
468
469
2018.5.95 by Andrew Bennetts
Add a Transport.is_readonly remote call, let {Branch,Repository}.lock_write remote call return UnlockableTransport, and miscellaneous test fixes.
470
class TestBranchLockWrite(tests.TestCase):
471
472
    def test_lock_write_unlockable(self):
473
        transport = MemoryTransport()
3104.4.2 by Andrew Bennetts
All tests passing.
474
        client = FakeClient([(('UnlockableTransport', ), '')], transport.base)
2018.5.95 by Andrew Bennetts
Add a Transport.is_readonly remote call, let {Branch,Repository}.lock_write remote call return UnlockableTransport, and miscellaneous test fixes.
475
        transport.mkdir('quack')
476
        transport = transport.clone('quack')
477
        # we do not want bzrdir to make any remote calls
478
        bzrdir = RemoteBzrDir(transport, _client=False)
479
        branch = RemoteBranch(bzrdir, None, _client=client)
480
        self.assertRaises(errors.UnlockableTransport, branch.lock_write)
481
        self.assertEqual(
3104.4.2 by Andrew Bennetts
All tests passing.
482
            [('call', 'Branch.lock_write', ('quack/', '', ''))],
2018.5.95 by Andrew Bennetts
Add a Transport.is_readonly remote call, let {Branch,Repository}.lock_write remote call return UnlockableTransport, and miscellaneous test fixes.
483
            client._calls)
484
485
2466.2.2 by Andrew Bennetts
Add tests for RemoteTransport.is_readonly in the style of the other remote object tests.
486
class TestTransportIsReadonly(tests.TestCase):
487
488
    def test_true(self):
489
        client = FakeClient([(('yes',), '')])
490
        transport = RemoteTransport('bzr://example.com/', medium=False,
491
                                    _client=client)
492
        self.assertEqual(True, transport.is_readonly())
493
        self.assertEqual(
494
            [('call', 'Transport.is_readonly', ())],
495
            client._calls)
496
497
    def test_false(self):
498
        client = FakeClient([(('no',), '')])
499
        transport = RemoteTransport('bzr://example.com/', medium=False,
500
                                    _client=client)
501
        self.assertEqual(False, transport.is_readonly())
502
        self.assertEqual(
503
            [('call', 'Transport.is_readonly', ())],
504
            client._calls)
505
506
    def test_error_from_old_server(self):
507
        """bzr 0.15 and earlier servers don't recognise the is_readonly verb.
508
        
509
        Clients should treat it as a "no" response, because is_readonly is only
510
        advisory anyway (a transport could be read-write, but then the
511
        underlying filesystem could be readonly anyway).
512
        """
513
        client = FakeClient([(
514
            ('error', "Generic bzr smart protocol error: "
515
                      "bad request 'Transport.is_readonly'"), '')])
516
        transport = RemoteTransport('bzr://example.com/', medium=False,
517
                                    _client=client)
518
        self.assertEqual(False, transport.is_readonly())
519
        self.assertEqual(
520
            [('call', 'Transport.is_readonly', ())],
521
            client._calls)
522
2471.2.1 by Andrew Bennetts
Fix trivial incompatibility with bzr 0.11 servers, which give a slightly different error to bzr 0.15 servers.
523
    def test_error_from_old_0_11_server(self):
524
        """Same as test_error_from_old_server, but with the slightly different
525
        error message from bzr 0.11 servers.
526
        """
527
        client = FakeClient([(
528
            ('error', "Generic bzr smart protocol error: "
529
                      "bad request u'Transport.is_readonly'"), '')])
530
        transport = RemoteTransport('bzr://example.com/', medium=False,
531
                                    _client=client)
532
        self.assertEqual(False, transport.is_readonly())
533
        self.assertEqual(
534
            [('call', 'Transport.is_readonly', ())],
535
            client._calls)
536
2466.2.2 by Andrew Bennetts
Add tests for RemoteTransport.is_readonly in the style of the other remote object tests.
537
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
538
class TestRemoteRepository(tests.TestCase):
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
539
    """Base for testing RemoteRepository protocol usage.
540
    
541
    These tests contain frozen requests and responses.  We want any changes to 
542
    what is sent or expected to be require a thoughtful update to these tests
543
    because they might break compatibility with different-versioned servers.
544
    """
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
545
546
    def setup_fake_client_and_repository(self, responses, transport_path):
2018.18.7 by Martin Pool
(broken) Start addng client proxy test for Repository.tarball
547
        """Create the fake client and repository for testing with.
548
        
549
        There's no real server here; we just have canned responses sent
550
        back one by one.
551
        
552
        :param transport_path: Path below the root of the MemoryTransport
553
            where the repository will be created.
554
        """
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
555
        transport = MemoryTransport()
556
        transport.mkdir(transport_path)
3104.4.2 by Andrew Bennetts
All tests passing.
557
        client = FakeClient(responses, transport.base)
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
558
        transport = transport.clone(transport_path)
559
        # we do not want bzrdir to make any remote calls
560
        bzrdir = RemoteBzrDir(transport, _client=False)
561
        repo = RemoteRepository(bzrdir, None, _client=client)
562
        return repo, client
563
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
564
2018.12.2 by Andrew Bennetts
Remove some duplicate code in test_remote
565
class TestRepositoryGatherStats(TestRemoteRepository):
2018.10.3 by v.ladeuil+lp at free
more tests for gather_stats
566
567
    def test_revid_none(self):
568
        # ('ok',), body with revisions and size
569
        responses = [(('ok', ), 'revisions: 2\nsize: 18\n')]
570
        transport_path = 'quack'
571
        repo, client = self.setup_fake_client_and_repository(
572
            responses, transport_path)
573
        result = repo.gather_stats(None)
574
        self.assertEqual(
2018.5.153 by Andrew Bennetts
Rename call2 to call_expecting_body, and other small changes prompted by review.
575
            [('call_expecting_body', 'Repository.gather_stats',
3104.4.2 by Andrew Bennetts
All tests passing.
576
             ('quack/','','no'))],
2018.10.3 by v.ladeuil+lp at free
more tests for gather_stats
577
            client._calls)
578
        self.assertEqual({'revisions': 2, 'size': 18}, result)
579
580
    def test_revid_no_committers(self):
581
        # ('ok',), body without committers
582
        responses = [(('ok', ),
583
                      'firstrev: 123456.300 3600\n'
584
                      'latestrev: 654231.400 0\n'
585
                      'revisions: 2\n'
586
                      'size: 18\n')]
587
        transport_path = 'quick'
2018.5.106 by Andrew Bennetts
Update tests in test_remote to use utf-8 byte strings for revision IDs, rather than unicode strings.
588
        revid = u'\xc8'.encode('utf8')
2018.10.3 by v.ladeuil+lp at free
more tests for gather_stats
589
        repo, client = self.setup_fake_client_and_repository(
590
            responses, transport_path)
591
        result = repo.gather_stats(revid)
592
        self.assertEqual(
2018.5.153 by Andrew Bennetts
Rename call2 to call_expecting_body, and other small changes prompted by review.
593
            [('call_expecting_body', 'Repository.gather_stats',
3104.4.2 by Andrew Bennetts
All tests passing.
594
              ('quick/', revid, 'no'))],
2018.10.3 by v.ladeuil+lp at free
more tests for gather_stats
595
            client._calls)
596
        self.assertEqual({'revisions': 2, 'size': 18,
597
                          'firstrev': (123456.300, 3600),
598
                          'latestrev': (654231.400, 0),},
599
                         result)
600
601
    def test_revid_with_committers(self):
602
        # ('ok',), body with committers
603
        responses = [(('ok', ),
604
                      'committers: 128\n'
605
                      'firstrev: 123456.300 3600\n'
606
                      'latestrev: 654231.400 0\n'
607
                      'revisions: 2\n'
608
                      'size: 18\n')]
609
        transport_path = 'buick'
2018.5.106 by Andrew Bennetts
Update tests in test_remote to use utf-8 byte strings for revision IDs, rather than unicode strings.
610
        revid = u'\xc8'.encode('utf8')
2018.10.3 by v.ladeuil+lp at free
more tests for gather_stats
611
        repo, client = self.setup_fake_client_and_repository(
612
            responses, transport_path)
613
        result = repo.gather_stats(revid, True)
614
        self.assertEqual(
2018.5.153 by Andrew Bennetts
Rename call2 to call_expecting_body, and other small changes prompted by review.
615
            [('call_expecting_body', 'Repository.gather_stats',
3104.4.2 by Andrew Bennetts
All tests passing.
616
              ('buick/', revid, 'yes'))],
2018.10.3 by v.ladeuil+lp at free
more tests for gather_stats
617
            client._calls)
618
        self.assertEqual({'revisions': 2, 'size': 18,
619
                          'committers': 128,
620
                          'firstrev': (123456.300, 3600),
621
                          'latestrev': (654231.400, 0),},
622
                         result)
623
624
3172.5.4 by Robert Collins
Implement get_parent_map for RemoteRepository with caching, based on get_revision_graph.
625
class TestRepositoryGetGraph(TestRemoteRepository):
626
627
    def test_get_graph(self):
3172.5.8 by Robert Collins
Review feedback.
628
        # get_graph returns a graph with the repository as the
629
        # parents_provider.
3172.5.4 by Robert Collins
Implement get_parent_map for RemoteRepository with caching, based on get_revision_graph.
630
        responses = []
631
        transport_path = 'quack'
632
        repo, client = self.setup_fake_client_and_repository(
633
            responses, transport_path)
634
        graph = repo.get_graph()
635
        self.assertEqual(graph._parents_provider, repo)
636
637
638
class TestRepositoryGetParentMap(TestRemoteRepository):
639
640
    def test_get_parent_map_caching(self):
641
        # get_parent_map returns from cache until unlock()
642
        # setup a reponse with two revisions
643
        r1 = u'\u0e33'.encode('utf8')
644
        r2 = u'\u0dab'.encode('utf8')
645
        lines = [' '.join([r2, r1]), r1]
3211.5.2 by Robert Collins
Change RemoteRepository.get_parent_map to use bz2 not gzip for compression.
646
        encoded_body = bz2.compress('\n'.join(lines))
3172.5.4 by Robert Collins
Implement get_parent_map for RemoteRepository with caching, based on get_revision_graph.
647
        responses = [(('ok', ), encoded_body), (('ok', ), encoded_body)]
648
649
        transport_path = 'quack'
650
        repo, client = self.setup_fake_client_and_repository(
651
            responses, transport_path)
652
        repo.lock_read()
653
        graph = repo.get_graph()
654
        parents = graph.get_parent_map([r2])
655
        self.assertEqual({r2: (r1,)}, parents)
656
        # locking and unlocking deeper should not reset
657
        repo.lock_read()
658
        repo.unlock()
659
        parents = graph.get_parent_map([r1])
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
660
        self.assertEqual({r1: (NULL_REVISION,)}, parents)
3172.5.4 by Robert Collins
Implement get_parent_map for RemoteRepository with caching, based on get_revision_graph.
661
        self.assertEqual(
3211.5.1 by Robert Collins
Change the smart server get_parents method to take a graph search to exclude already recieved parents from. This prevents history shortcuts causing huge numbers of duplicates.
662
            [('call_with_body_bytes_expecting_body',
663
              'Repository.get_parent_map', ('quack/', r2), '\n\n0')],
3172.5.4 by Robert Collins
Implement get_parent_map for RemoteRepository with caching, based on get_revision_graph.
664
            client._calls)
665
        repo.unlock()
666
        # now we call again, and it should use the second response.
667
        repo.lock_read()
668
        graph = repo.get_graph()
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
669
        parents = graph.get_parent_map([r1])
670
        self.assertEqual({r1: (NULL_REVISION,)}, parents)
3172.5.4 by Robert Collins
Implement get_parent_map for RemoteRepository with caching, based on get_revision_graph.
671
        self.assertEqual(
3211.5.1 by Robert Collins
Change the smart server get_parents method to take a graph search to exclude already recieved parents from. This prevents history shortcuts causing huge numbers of duplicates.
672
            [('call_with_body_bytes_expecting_body',
673
              'Repository.get_parent_map', ('quack/', r2), '\n\n0'),
674
             ('call_with_body_bytes_expecting_body',
675
              'Repository.get_parent_map', ('quack/', r1), '\n\n0'),
3172.5.4 by Robert Collins
Implement get_parent_map for RemoteRepository with caching, based on get_revision_graph.
676
            ],
677
            client._calls)
678
        repo.unlock()
679
3213.1.2 by Andrew Bennetts
Add test for reconnection if get_parent_map is unknown by the server.
680
    def test_get_parent_map_reconnects_if_unknown_method(self):
681
        error_msg = (
682
            "Generic bzr smart protocol error: "
683
            "bad request 'Repository.get_parent_map'")
684
        responses = [
685
            (('error', error_msg), ''),
686
            (('ok',), '')]
687
        transport_path = 'quack'
688
        repo, client = self.setup_fake_client_and_repository(
689
            responses, transport_path)
690
        rev_id = 'revision-id'
691
        parents = repo.get_parent_map([rev_id])
692
        self.assertEqual(
3213.1.8 by Andrew Bennetts
Merge from bzr.dev.
693
            [('call_with_body_bytes_expecting_body',
694
              'Repository.get_parent_map', ('quack/', rev_id), '\n\n0'),
3213.1.2 by Andrew Bennetts
Add test for reconnection if get_parent_map is unknown by the server.
695
             ('disconnect medium',),
696
             ('call_expecting_body', 'Repository.get_revision_graph',
697
              ('quack/', ''))],
698
            client._calls)
699
700
3172.5.4 by Robert Collins
Implement get_parent_map for RemoteRepository with caching, based on get_revision_graph.
701
2018.5.68 by Wouter van Heyst
Merge RemoteRepository.gather_stats.
702
class TestRepositoryGetRevisionGraph(TestRemoteRepository):
703
    
704
    def test_null_revision(self):
705
        # a null revision has the predictable result {}, we should have no wire
706
        # traffic when calling it with this argument
707
        responses = [(('notused', ), '')]
708
        transport_path = 'empty'
709
        repo, client = self.setup_fake_client_and_repository(
710
            responses, transport_path)
3287.6.4 by Robert Collins
Fix up deprecation warnings for get_revision_graph.
711
        result = self.applyDeprecated(one_four, repo.get_revision_graph,
712
            NULL_REVISION)
2018.5.68 by Wouter van Heyst
Merge RemoteRepository.gather_stats.
713
        self.assertEqual([], client._calls)
714
        self.assertEqual({}, result)
715
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
716
    def test_none_revision(self):
717
        # with none we want the entire graph
2018.5.106 by Andrew Bennetts
Update tests in test_remote to use utf-8 byte strings for revision IDs, rather than unicode strings.
718
        r1 = u'\u0e33'.encode('utf8')
719
        r2 = u'\u0dab'.encode('utf8')
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
720
        lines = [' '.join([r2, r1]), r1]
2018.5.106 by Andrew Bennetts
Update tests in test_remote to use utf-8 byte strings for revision IDs, rather than unicode strings.
721
        encoded_body = '\n'.join(lines)
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
722
723
        responses = [(('ok', ), encoded_body)]
724
        transport_path = 'sinhala'
725
        repo, client = self.setup_fake_client_and_repository(
726
            responses, transport_path)
3287.6.4 by Robert Collins
Fix up deprecation warnings for get_revision_graph.
727
        result = self.applyDeprecated(one_four, repo.get_revision_graph)
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
728
        self.assertEqual(
2018.5.153 by Andrew Bennetts
Rename call2 to call_expecting_body, and other small changes prompted by review.
729
            [('call_expecting_body', 'Repository.get_revision_graph',
3104.4.2 by Andrew Bennetts
All tests passing.
730
             ('sinhala/', ''))],
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
731
            client._calls)
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
732
        self.assertEqual({r1: (), r2: (r1, )}, result)
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
733
734
    def test_specific_revision(self):
735
        # with a specific revision we want the graph for that
736
        # with none we want the entire graph
2018.5.106 by Andrew Bennetts
Update tests in test_remote to use utf-8 byte strings for revision IDs, rather than unicode strings.
737
        r11 = u'\u0e33'.encode('utf8')
738
        r12 = u'\xc9'.encode('utf8')
739
        r2 = u'\u0dab'.encode('utf8')
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
740
        lines = [' '.join([r2, r11, r12]), r11, r12]
2018.5.106 by Andrew Bennetts
Update tests in test_remote to use utf-8 byte strings for revision IDs, rather than unicode strings.
741
        encoded_body = '\n'.join(lines)
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
742
743
        responses = [(('ok', ), encoded_body)]
744
        transport_path = 'sinhala'
745
        repo, client = self.setup_fake_client_and_repository(
746
            responses, transport_path)
3287.6.4 by Robert Collins
Fix up deprecation warnings for get_revision_graph.
747
        result = self.applyDeprecated(one_four, repo.get_revision_graph, r2)
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
748
        self.assertEqual(
2018.5.153 by Andrew Bennetts
Rename call2 to call_expecting_body, and other small changes prompted by review.
749
            [('call_expecting_body', 'Repository.get_revision_graph',
3104.4.2 by Andrew Bennetts
All tests passing.
750
             ('sinhala/', r2))],
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
751
            client._calls)
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
752
        self.assertEqual({r11: (), r12: (), r2: (r11, r12), }, result)
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
753
754
    def test_no_such_revision(self):
755
        revid = '123'
756
        responses = [(('nosuchrevision', revid), '')]
757
        transport_path = 'sinhala'
758
        repo, client = self.setup_fake_client_and_repository(
759
            responses, transport_path)
760
        # also check that the right revision is reported in the error
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
761
        self.assertRaises(errors.NoSuchRevision,
3287.6.4 by Robert Collins
Fix up deprecation warnings for get_revision_graph.
762
            self.applyDeprecated, one_four, repo.get_revision_graph, revid)
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
763
        self.assertEqual(
2018.5.153 by Andrew Bennetts
Rename call2 to call_expecting_body, and other small changes prompted by review.
764
            [('call_expecting_body', 'Repository.get_revision_graph',
3104.4.2 by Andrew Bennetts
All tests passing.
765
             ('sinhala/', revid))],
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
766
            client._calls)
767
768
        
769
class TestRepositoryIsShared(TestRemoteRepository):
770
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
771
    def test_is_shared(self):
772
        # ('yes', ) for Repository.is_shared -> 'True'.
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
773
        responses = [(('yes', ), )]
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
774
        transport_path = 'quack'
775
        repo, client = self.setup_fake_client_and_repository(
776
            responses, transport_path)
777
        result = repo.is_shared()
778
        self.assertEqual(
3104.4.2 by Andrew Bennetts
All tests passing.
779
            [('call', 'Repository.is_shared', ('quack/',))],
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
780
            client._calls)
781
        self.assertEqual(True, result)
782
783
    def test_is_not_shared(self):
784
        # ('no', ) for Repository.is_shared -> 'False'.
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
785
        responses = [(('no', ), )]
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
786
        transport_path = 'qwack'
787
        repo, client = self.setup_fake_client_and_repository(
788
            responses, transport_path)
789
        result = repo.is_shared()
790
        self.assertEqual(
3104.4.2 by Andrew Bennetts
All tests passing.
791
            [('call', 'Repository.is_shared', ('qwack/',))],
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
792
            client._calls)
793
        self.assertEqual(False, result)
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
794
795
796
class TestRepositoryLockWrite(TestRemoteRepository):
797
798
    def test_lock_write(self):
799
        responses = [(('ok', 'a token'), '')]
800
        transport_path = 'quack'
801
        repo, client = self.setup_fake_client_and_repository(
802
            responses, transport_path)
803
        result = repo.lock_write()
804
        self.assertEqual(
3104.4.2 by Andrew Bennetts
All tests passing.
805
            [('call', 'Repository.lock_write', ('quack/', ''))],
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
806
            client._calls)
807
        self.assertEqual('a token', result)
808
809
    def test_lock_write_already_locked(self):
810
        responses = [(('LockContention', ), '')]
811
        transport_path = 'quack'
812
        repo, client = self.setup_fake_client_and_repository(
813
            responses, transport_path)
814
        self.assertRaises(errors.LockContention, repo.lock_write)
815
        self.assertEqual(
3104.4.2 by Andrew Bennetts
All tests passing.
816
            [('call', 'Repository.lock_write', ('quack/', ''))],
2018.5.95 by Andrew Bennetts
Add a Transport.is_readonly remote call, let {Branch,Repository}.lock_write remote call return UnlockableTransport, and miscellaneous test fixes.
817
            client._calls)
818
819
    def test_lock_write_unlockable(self):
820
        responses = [(('UnlockableTransport', ), '')]
821
        transport_path = 'quack'
822
        repo, client = self.setup_fake_client_and_repository(
823
            responses, transport_path)
824
        self.assertRaises(errors.UnlockableTransport, repo.lock_write)
825
        self.assertEqual(
3104.4.2 by Andrew Bennetts
All tests passing.
826
            [('call', 'Repository.lock_write', ('quack/', ''))],
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
827
            client._calls)
828
829
830
class TestRepositoryUnlock(TestRemoteRepository):
831
832
    def test_unlock(self):
833
        responses = [(('ok', 'a token'), ''),
834
                     (('ok',), '')]
835
        transport_path = 'quack'
836
        repo, client = self.setup_fake_client_and_repository(
837
            responses, transport_path)
838
        repo.lock_write()
839
        repo.unlock()
840
        self.assertEqual(
3104.4.2 by Andrew Bennetts
All tests passing.
841
            [('call', 'Repository.lock_write', ('quack/', '')),
842
             ('call', 'Repository.unlock', ('quack/', 'a token'))],
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
843
            client._calls)
844
845
    def test_unlock_wrong_token(self):
846
        # If somehow the token is wrong, unlock will raise TokenMismatch.
847
        responses = [(('ok', 'a token'), ''),
848
                     (('TokenMismatch',), '')]
849
        transport_path = 'quack'
850
        repo, client = self.setup_fake_client_and_repository(
851
            responses, transport_path)
852
        repo.lock_write()
853
        self.assertRaises(errors.TokenMismatch, repo.unlock)
854
855
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
856
class TestRepositoryHasRevision(TestRemoteRepository):
857
858
    def test_none(self):
859
        # repo.has_revision(None) should not cause any traffic.
860
        transport_path = 'quack'
861
        responses = None
862
        repo, client = self.setup_fake_client_and_repository(
863
            responses, transport_path)
864
865
        # The null revision is always there, so has_revision(None) == True.
3172.3.3 by Robert Collins
Missed one occurence of None -> NULL_REVISION.
866
        self.assertEqual(True, repo.has_revision(NULL_REVISION))
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
867
868
        # The remote repo shouldn't be accessed.
869
        self.assertEqual([], client._calls)
2018.18.7 by Martin Pool
(broken) Start addng client proxy test for Repository.tarball
870
871
872
class TestRepositoryTarball(TestRemoteRepository):
873
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
874
    # This is a canned tarball reponse we can validate against
2018.18.18 by Martin Pool
reformat
875
    tarball_content = (
2018.18.23 by Martin Pool
review cleanups
876
        'QlpoOTFBWSZTWdGkj3wAAWF/k8aQACBIB//A9+8cIX/v33AACEAYABAECEACNz'
877
        'JqsgJJFPTSnk1A3qh6mTQAAAANPUHkagkSTEkaA09QaNAAAGgAAAcwCYCZGAEY'
878
        'mJhMJghpiaYBUkKammSHqNMZQ0NABkNAeo0AGneAevnlwQoGzEzNVzaYxp/1Uk'
879
        'xXzA1CQX0BJMZZLcPBrluJir5SQyijWHYZ6ZUtVqqlYDdB2QoCwa9GyWwGYDMA'
880
        'OQYhkpLt/OKFnnlT8E0PmO8+ZNSo2WWqeCzGB5fBXZ3IvV7uNJVE7DYnWj6qwB'
881
        'k5DJDIrQ5OQHHIjkS9KqwG3mc3t+F1+iujb89ufyBNIKCgeZBWrl5cXxbMGoMs'
882
        'c9JuUkg5YsiVcaZJurc6KLi6yKOkgCUOlIlOpOoXyrTJjK8ZgbklReDdwGmFgt'
883
        'dkVsAIslSVCd4AtACSLbyhLHryfb14PKegrVDba+U8OL6KQtzdM5HLjAc8/p6n'
884
        '0lgaWU8skgO7xupPTkyuwheSckejFLK5T4ZOo0Gda9viaIhpD1Qn7JqqlKAJqC'
885
        'QplPKp2nqBWAfwBGaOwVrz3y1T+UZZNismXHsb2Jq18T+VaD9k4P8DqE3g70qV'
886
        'JLurpnDI6VS5oqDDPVbtVjMxMxMg4rzQVipn2Bv1fVNK0iq3Gl0hhnnHKm/egy'
887
        'nWQ7QH/F3JFOFCQ0aSPfA='
888
        ).decode('base64')
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
889
2018.18.7 by Martin Pool
(broken) Start addng client proxy test for Repository.tarball
890
    def test_repository_tarball(self):
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
891
        # Test that Repository.tarball generates the right operations
2018.18.7 by Martin Pool
(broken) Start addng client proxy test for Repository.tarball
892
        transport_path = 'repo'
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
893
        expected_responses = [(('ok',), self.tarball_content),
2018.18.7 by Martin Pool
(broken) Start addng client proxy test for Repository.tarball
894
            ]
2018.18.14 by Martin Pool
merge hpss again; restore incorrectly removed RemoteRepository.break_lock
895
        expected_calls = [('call_expecting_body', 'Repository.tarball',
3104.4.2 by Andrew Bennetts
All tests passing.
896
                           ('repo/', 'bz2',),),
2018.18.7 by Martin Pool
(broken) Start addng client proxy test for Repository.tarball
897
            ]
898
        remote_repo, client = self.setup_fake_client_and_repository(
899
            expected_responses, transport_path)
900
        # Now actually ask for the tarball
2018.18.25 by Martin Pool
Repository.tarball fixes for python2.4
901
        tarball_file = remote_repo._get_tarball('bz2')
902
        try:
903
            self.assertEqual(expected_calls, client._calls)
904
            self.assertEqual(self.tarball_content, tarball_file.read())
905
        finally:
906
            tarball_file.close()
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
907
908
909
class TestRemoteRepositoryCopyContent(tests.TestCaseWithTransport):
910
    """RemoteRepository.copy_content_into optimizations"""
911
2018.18.10 by Martin Pool
copy_content_into from Remote repositories by using temporary directories on both ends.
912
    def test_copy_content_remote_to_local(self):
913
        self.transport_server = server.SmartTCPServer_for_testing
914
        src_repo = self.make_repository('repo1')
915
        src_repo = repository.Repository.open(self.get_url('repo1'))
916
        # At the moment the tarball-based copy_content_into can't write back
917
        # into a smart server.  It would be good if it could upload the
918
        # tarball; once that works we'd have to create repositories of
919
        # different formats. -- mbp 20070410
920
        dest_url = self.get_vfs_only_url('repo2')
921
        dest_bzrdir = BzrDir.create(dest_url)
922
        dest_repo = dest_bzrdir.create_repository()
923
        self.assertFalse(isinstance(dest_repo, RemoteRepository))
924
        self.assertTrue(isinstance(src_repo, RemoteRepository))
925
        src_repo.copy_content_into(dest_repo)
2535.3.39 by Andrew Bennetts
Tidy some XXXs.
926
927
2535.3.49 by Andrew Bennetts
Rename 'Repository.fetch_revisions' smart request to 'Repository.stream_knit_data_for_revisions'.
928
class TestRepositoryStreamKnitData(TestRemoteRepository):
2535.3.39 by Andrew Bennetts
Tidy some XXXs.
929
930
    def make_pack_file(self, records):
931
        pack_file = StringIO()
932
        pack_writer = pack.ContainerWriter(pack_file.write)
933
        pack_writer.begin()
934
        for bytes, names in records:
935
            pack_writer.add_bytes_record(bytes, names)
936
        pack_writer.end()
937
        pack_file.seek(0)
938
        return pack_file
939
2535.4.2 by Andrew Bennetts
Nasty hackery to make stream_knit_data_for_revisions response use streaming.
940
    def make_pack_stream(self, records):
2535.4.18 by Andrew Bennetts
Use pack.ContainerSerialiser to remove some nasty cruft.
941
        pack_serialiser = pack.ContainerSerialiser()
942
        yield pack_serialiser.begin()
2535.4.2 by Andrew Bennetts
Nasty hackery to make stream_knit_data_for_revisions response use streaming.
943
        for bytes, names in records:
2535.4.18 by Andrew Bennetts
Use pack.ContainerSerialiser to remove some nasty cruft.
944
            yield pack_serialiser.bytes_record(bytes, names)
945
        yield pack_serialiser.end()
2535.4.2 by Andrew Bennetts
Nasty hackery to make stream_knit_data_for_revisions response use streaming.
946
2535.3.39 by Andrew Bennetts
Tidy some XXXs.
947
    def test_bad_pack_from_server(self):
2535.3.50 by Andrew Bennetts
Use tuple names in data streams rather than concatenated strings.
948
        """A response with invalid data (e.g. it has a record with multiple
949
        names) triggers an exception.
950
        
951
        Not all possible errors will be caught at this stage, but obviously
952
        malformed data should be.
953
        """
954
        record = ('bytes', [('name1',), ('name2',)])
2535.4.2 by Andrew Bennetts
Nasty hackery to make stream_knit_data_for_revisions response use streaming.
955
        pack_stream = self.make_pack_stream([record])
956
        responses = [(('ok',), pack_stream), ]
2535.3.39 by Andrew Bennetts
Tidy some XXXs.
957
        transport_path = 'quack'
958
        repo, client = self.setup_fake_client_and_repository(
959
            responses, transport_path)
3184.1.9 by Robert Collins
* ``Repository.get_data_stream`` is now deprecated in favour of
960
        search = graph.SearchResult(set(['revid']), set(), 1, set(['revid']))
961
        stream = repo.get_data_stream_for_search(search)
2535.3.39 by Andrew Bennetts
Tidy some XXXs.
962
        self.assertRaises(errors.SmartProtocolError, list, stream)
963
    
2535.3.68 by Andrew Bennetts
Backwards compatibility for new smart method.
964
    def test_backwards_compatibility(self):
965
        """If the server doesn't recognise this request, fallback to VFS."""
966
        error_msg = (
967
            "Generic bzr smart protocol error: "
2535.4.29 by Andrew Bennetts
Add a new smart method, Repository.stream_revisions_chunked, rather than changing the behaviour of an existing method.
968
            "bad request 'Repository.stream_revisions_chunked'")
2535.3.68 by Andrew Bennetts
Backwards compatibility for new smart method.
969
        responses = [
970
            (('error', error_msg), '')]
971
        repo, client = self.setup_fake_client_and_repository(
972
            responses, 'path')
973
        self.mock_called = False
974
        repo._real_repository = MockRealRepository(self)
3184.1.9 by Robert Collins
* ``Repository.get_data_stream`` is now deprecated in favour of
975
        search = graph.SearchResult(set(['revid']), set(), 1, set(['revid']))
976
        repo.get_data_stream_for_search(search)
2535.3.68 by Andrew Bennetts
Backwards compatibility for new smart method.
977
        self.assertTrue(self.mock_called)
978
        self.failIf(client.expecting_body,
979
            "The protocol has been left in an unclean state that will cause "
980
            "TooManyConcurrentRequests errors.")
981
982
983
class MockRealRepository(object):
984
    """Helper class for TestRepositoryStreamKnitData.test_unknown_method."""
985
986
    def __init__(self, test):
987
        self.test = test
988
3184.1.9 by Robert Collins
* ``Repository.get_data_stream`` is now deprecated in favour of
989
    def get_data_stream_for_search(self, search):
990
        self.test.assertEqual(set(['revid']), search.get_keys())
2535.3.68 by Andrew Bennetts
Backwards compatibility for new smart method.
991
        self.test.mock_called = True
992
993