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