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