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