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