/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
3407.2.2 by Martin Pool
Remove special case in RemoteBranchLockableFiles for branch.conf
1
# Copyright (C) 2006, 2007, 2008 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 (
3777.1.3 by Aaron Bentley
Use SSH default username from authentication.conf
30
    config,
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
31
    errors,
3184.1.9 by Robert Collins
* ``Repository.get_data_stream`` is now deprecated in favour of
32
    graph,
2535.3.39 by Andrew Bennetts
Tidy some XXXs.
33
    pack,
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
34
    remote,
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
35
    repository,
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
36
    tests,
3691.2.4 by Martin Pool
Add FakeRemoteTransport to clarify test_remote
37
    urlutils,
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
38
    )
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
39
from bzrlib.branch import Branch
40
from bzrlib.bzrdir import BzrDir, BzrDirFormat
41
from bzrlib.remote import (
42
    RemoteBranch,
43
    RemoteBzrDir,
44
    RemoteBzrDirFormat,
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
45
    RemoteRepository,
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
46
    )
47
from bzrlib.revision import NULL_REVISION
2432.3.2 by Andrew Bennetts
Add test, and tidy implementation.
48
from bzrlib.smart import server, medium
2018.5.159 by Andrew Bennetts
Rename SmartClient to _SmartClient.
49
from bzrlib.smart.client import _SmartClient
3287.6.4 by Robert Collins
Fix up deprecation warnings for get_revision_graph.
50
from bzrlib.symbol_versioning import one_four
3431.3.11 by Andrew Bennetts
Push remote_path_from_transport logic into SmartClientMedium, removing special-casing of bzr+http from _SmartClient.
51
from bzrlib.transport import get_transport, http
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
52
from bzrlib.transport.memory import MemoryTransport
3777.1.3 by Aaron Bentley
Use SSH default username from authentication.conf
53
from bzrlib.transport.remote import (
54
    RemoteTransport,
55
    RemoteSSHTransport,
56
    RemoteTCPTransport,
57
)
1752.2.30 by Martin Pool
Start adding a RemoteBzrDir, etc
58
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)
59
2018.5.42 by Robert Collins
Various hopefully improvements, but wsgi is broken, handing over to spiv :).
60
class BasicRemoteObjectTests(tests.TestCaseWithTransport):
1752.2.30 by Martin Pool
Start adding a RemoteBzrDir, etc
61
62
    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.
63
        self.transport_server = server.SmartTCPServer_for_testing
2018.5.42 by Robert Collins
Various hopefully improvements, but wsgi is broken, handing over to spiv :).
64
        super(BasicRemoteObjectTests, self).setUp()
65
        self.transport = self.get_transport()
1752.2.30 by Martin Pool
Start adding a RemoteBzrDir, etc
66
        # 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
67
        self.local_wt = BzrDir.create_standalone_workingtree('.')
1752.2.30 by Martin Pool
Start adding a RemoteBzrDir, etc
68
2018.5.171 by Andrew Bennetts
Disconnect RemoteTransports in some tests to avoid tripping up test_strace with leftover threads from previous tests.
69
    def tearDown(self):
70
        self.transport.disconnect()
71
        tests.TestCaseWithTransport.tearDown(self)
72
1752.2.30 by Martin Pool
Start adding a RemoteBzrDir, etc
73
    def test_create_remote_bzrdir(self):
74
        b = remote.RemoteBzrDir(self.transport)
75
        self.assertIsInstance(b, BzrDir)
76
77
    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.
78
        # open a standalone branch in the working directory
1752.2.30 by Martin Pool
Start adding a RemoteBzrDir, etc
79
        b = remote.RemoteBzrDir(self.transport)
80
        branch = b.open_branch()
2018.5.163 by Andrew Bennetts
Deal with various review comments from Robert.
81
        self.assertIsInstance(branch, Branch)
1752.2.30 by Martin Pool
Start adding a RemoteBzrDir, etc
82
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
83
    def test_remote_repository(self):
84
        b = BzrDir.open_from_transport(self.transport)
85
        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.
86
        revid = u'\xc823123123'.encode('utf8')
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
87
        self.assertFalse(repo.has_revision(revid))
88
        self.local_wt.commit(message='test commit', rev_id=revid)
89
        self.assertTrue(repo.has_revision(revid))
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
90
91
    def test_remote_branch_revision_history(self):
92
        b = BzrDir.open_from_transport(self.transport).open_branch()
2018.5.38 by Robert Collins
Implement RemoteBranch.revision_history().
93
        self.assertEqual([], b.revision_history())
94
        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.
95
        r2 = self.local_wt.commit('1st commit', rev_id=u'\xc8'.encode('utf8'))
2018.5.38 by Robert Collins
Implement RemoteBranch.revision_history().
96
        self.assertEqual([r1, r2], b.revision_history())
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
97
1752.2.30 by Martin Pool
Start adding a RemoteBzrDir, etc
98
    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)
99
        """Should open a RemoteBzrDir over a RemoteTransport"""
1752.2.30 by Martin Pool
Start adding a RemoteBzrDir, etc
100
        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.
101
        self.assertTrue(RemoteBzrDirFormat
102
                        in BzrDirFormat._control_server_formats)
1752.2.30 by Martin Pool
Start adding a RemoteBzrDir, etc
103
        self.assertIsInstance(fmt, remote.RemoteBzrDirFormat)
104
105
    def test_open_detected_smart_format(self):
106
        fmt = BzrDirFormat.find_format(self.transport)
107
        d = fmt.open(self.transport)
108
        self.assertIsInstance(d, BzrDir)
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
109
2477.1.1 by Martin Pool
Add RemoteBranch repr
110
    def test_remote_branch_repr(self):
111
        b = BzrDir.open_from_transport(self.transport).open_branch()
112
        self.assertStartsWith(str(b), 'RemoteBranch(')
113
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
114
3691.2.4 by Martin Pool
Add FakeRemoteTransport to clarify test_remote
115
class FakeRemoteTransport(object):
116
    """This class provides the minimum support for use in place of a RemoteTransport.
117
    
3691.2.5 by Martin Pool
Add Branch.get_stacked_on_url rpc and tests for same
118
    It doesn't actually transmit requests, but rather expects them to be
119
    handled by a FakeClient which holds canned responses.  It does not allow
120
    any vfs access, therefore is not suitable for testing any operation that
121
    will fallback to vfs access.  Backing the test by an instance of this
122
    class guarantees that it's - done using non-vfs operations.
3691.2.4 by Martin Pool
Add FakeRemoteTransport to clarify test_remote
123
    """
124
125
    _default_url = 'fakeremotetransport://host/path/'
126
127
    def __init__(self, url=None):
128
        if url is None:
129
            url = self._default_url
130
        self.base = url
131
132
    def __repr__(self):
133
        return "%r(%r)" % (self.__class__.__name__,
134
            self.base)
135
136
    def clone(self, relpath):
137
        return FakeRemoteTransport(urlutils.join(self.base, relpath))
138
139
    def get(self, relpath):
3691.2.5 by Martin Pool
Add Branch.get_stacked_on_url rpc and tests for same
140
        # only get is specifically stubbed out, because it's usually the first
141
        # thing we do.  anything else will fail with an AttributeError.
3691.2.4 by Martin Pool
Add FakeRemoteTransport to clarify test_remote
142
        raise AssertionError("%r doesn't support file access to %r"
143
            % (self, relpath))
144
145
146
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
147
class FakeProtocol(object):
148
    """Lookalike SmartClientRequestProtocolOne allowing body reading tests."""
149
2535.3.68 by Andrew Bennetts
Backwards compatibility for new smart method.
150
    def __init__(self, body, fake_client):
2535.4.2 by Andrew Bennetts
Nasty hackery to make stream_knit_data_for_revisions response use streaming.
151
        self.body = body
152
        self._body_buffer = None
2535.3.68 by Andrew Bennetts
Backwards compatibility for new smart method.
153
        self._fake_client = fake_client
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
154
155
    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.
156
        if self._body_buffer is None:
157
            self._body_buffer = StringIO(self.body)
2535.3.68 by Andrew Bennetts
Backwards compatibility for new smart method.
158
        bytes = self._body_buffer.read(count)
159
        if self._body_buffer.tell() == len(self._body_buffer.getvalue()):
160
            self._fake_client.expecting_body = False
161
        return bytes
162
163
    def cancel_read_body(self):
164
        self._fake_client.expecting_body = False
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
165
2535.4.2 by Andrew Bennetts
Nasty hackery to make stream_knit_data_for_revisions response use streaming.
166
    def read_streamed_body(self):
167
        return self.body
168
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
169
2018.5.159 by Andrew Bennetts
Rename SmartClient to _SmartClient.
170
class FakeClient(_SmartClient):
171
    """Lookalike for _SmartClient allowing testing."""
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
172
    
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
173
    def __init__(self, fake_medium_base='fake base'):
2535.3.68 by Andrew Bennetts
Backwards compatibility for new smart method.
174
        """Create a FakeClient.
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
175
3104.4.2 by Andrew Bennetts
All tests passing.
176
        :param responses: A list of response-tuple, body-data pairs to be sent
3297.3.3 by Andrew Bennetts
SmartClientRequestProtocol*.read_response_tuple can now raise UnknownSmartMethod. Callers no longer need to do their own ad hoc unknown smart method error detection.
177
            back to callers.  A special case is if the response-tuple is
178
            'unknown verb', then a UnknownSmartMethod will be raised for that
179
            call, using the second element of the tuple as the verb in the
180
            exception.
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
181
        """
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
182
        self.responses = []
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
183
        self._calls = []
2535.3.68 by Andrew Bennetts
Backwards compatibility for new smart method.
184
        self.expecting_body = False
3691.2.7 by Martin Pool
FakeClient can know what calls to expect
185
        # if non-None, this is the list of expected calls, with only the
186
        # method name and arguments included.  the body might be hard to
187
        # compute so is not included
188
        self._expected_calls = None
3431.3.2 by Andrew Bennetts
Remove 'base' from _SmartClient entirely, now that the medium has it.
189
        _SmartClient.__init__(self, FakeMedium(self._calls, fake_medium_base))
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
190
3691.2.7 by Martin Pool
FakeClient can know what calls to expect
191
    def add_expected_call(self, call_name, call_args, response_type,
192
        response_args, response_body=None):
193
        if self._expected_calls is None:
194
            self._expected_calls = []
195
        self._expected_calls.append((call_name, call_args))
3691.2.8 by Martin Pool
Update some test_remote tests for Branch.get_stacked_on_url and with clearer assertions
196
        self.responses.append((response_type, response_args, response_body))
3691.2.7 by Martin Pool
FakeClient can know what calls to expect
197
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
198
    def add_success_response(self, *args):
199
        self.responses.append(('success', args, None))
200
201
    def add_success_response_with_body(self, body, *args):
202
        self.responses.append(('success', args, body))
203
204
    def add_error_response(self, *args):
205
        self.responses.append(('error', args))
206
207
    def add_unknown_method_response(self, verb):
208
        self.responses.append(('unknown', verb))
209
3691.2.8 by Martin Pool
Update some test_remote tests for Branch.get_stacked_on_url and with clearer assertions
210
    def finished_test(self):
211
        if self._expected_calls:
212
            raise AssertionError("%r finished but was still expecting %r"
213
                % (self, self._expected_calls[0]))
214
3297.3.3 by Andrew Bennetts
SmartClientRequestProtocol*.read_response_tuple can now raise UnknownSmartMethod. Callers no longer need to do their own ad hoc unknown smart method error detection.
215
    def _get_next_response(self):
3691.2.7 by Martin Pool
FakeClient can know what calls to expect
216
        try:
217
            response_tuple = self.responses.pop(0)
218
        except IndexError, e:
219
            raise AssertionError("%r didn't expect any more calls"
220
                % (self,))
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
221
        if response_tuple[0] == 'unknown':
222
            raise errors.UnknownSmartMethod(response_tuple[1])
223
        elif response_tuple[0] == 'error':
224
            raise errors.ErrorFromSmartServer(response_tuple[1])
3297.3.3 by Andrew Bennetts
SmartClientRequestProtocol*.read_response_tuple can now raise UnknownSmartMethod. Callers no longer need to do their own ad hoc unknown smart method error detection.
225
        return response_tuple
226
3691.2.7 by Martin Pool
FakeClient can know what calls to expect
227
    def _check_call(self, method, args):
228
        if self._expected_calls is None:
229
            # the test should be updated to say what it expects
230
            return
231
        try:
232
            next_call = self._expected_calls.pop(0)
233
        except IndexError:
234
            raise AssertionError("%r didn't expect any more calls "
235
                "but got %r%r"
3691.2.11 by Martin Pool
More tests around RemoteBranch stacking.
236
                % (self, method, args,))
3691.2.7 by Martin Pool
FakeClient can know what calls to expect
237
        if method != next_call[0] or args != next_call[1]:
238
            raise AssertionError("%r expected %r%r "
239
                "but got %r%r"
3691.2.8 by Martin Pool
Update some test_remote tests for Branch.get_stacked_on_url and with clearer assertions
240
                % (self, next_call[0], next_call[1], method, args,))
3691.2.7 by Martin Pool
FakeClient can know what calls to expect
241
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
242
    def call(self, method, *args):
3691.2.7 by Martin Pool
FakeClient can know what calls to expect
243
        self._check_call(method, args)
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
244
        self._calls.append(('call', method, args))
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
245
        return self._get_next_response()[1]
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
246
2018.5.153 by Andrew Bennetts
Rename call2 to call_expecting_body, and other small changes prompted by review.
247
    def call_expecting_body(self, method, *args):
3691.2.7 by Martin Pool
FakeClient can know what calls to expect
248
        self._check_call(method, args)
2018.5.153 by Andrew Bennetts
Rename call2 to call_expecting_body, and other small changes prompted by review.
249
        self._calls.append(('call_expecting_body', method, args))
3297.3.3 by Andrew Bennetts
SmartClientRequestProtocol*.read_response_tuple can now raise UnknownSmartMethod. Callers no longer need to do their own ad hoc unknown smart method error detection.
250
        result = self._get_next_response()
2535.3.68 by Andrew Bennetts
Backwards compatibility for new smart method.
251
        self.expecting_body = True
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
252
        return result[1], FakeProtocol(result[2], self)
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
253
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.
254
    def call_with_body_bytes_expecting_body(self, method, args, body):
3691.2.7 by Martin Pool
FakeClient can know what calls to expect
255
        self._check_call(method, args)
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.
256
        self._calls.append(('call_with_body_bytes_expecting_body', method,
257
            args, body))
3297.3.3 by Andrew Bennetts
SmartClientRequestProtocol*.read_response_tuple can now raise UnknownSmartMethod. Callers no longer need to do their own ad hoc unknown smart method error detection.
258
        result = self._get_next_response()
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.
259
        self.expecting_body = True
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
260
        return result[1], FakeProtocol(result[2], self)
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.
261
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
262
3431.3.11 by Andrew Bennetts
Push remote_path_from_transport logic into SmartClientMedium, removing special-casing of bzr+http from _SmartClient.
263
class FakeMedium(medium.SmartClientMedium):
3104.4.2 by Andrew Bennetts
All tests passing.
264
3431.3.1 by Andrew Bennetts
First rough cut of a fix for bug #230550, by adding .base to SmartClientMedia rather than relying on other objects to track this accurately while reusing client media.
265
    def __init__(self, client_calls, base):
3453.4.1 by Andrew Bennetts
Better infrastructure on SmartClientMedium for tracking the remote version.
266
        medium.SmartClientMedium.__init__(self, base)
3213.1.2 by Andrew Bennetts
Add test for reconnection if get_parent_map is unknown by the server.
267
        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.
268
269
    def disconnect(self):
3213.1.2 by Andrew Bennetts
Add test for reconnection if get_parent_map is unknown by the server.
270
        self._client_calls.append(('disconnect medium',))
3104.4.2 by Andrew Bennetts
All tests passing.
271
272
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.
273
class TestVfsHas(tests.TestCase):
274
275
    def test_unicode_path(self):
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
276
        client = FakeClient('/')
277
        client.add_success_response('yes',)
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.
278
        transport = RemoteTransport('bzr://localhost/', _client=client)
279
        filename = u'/hell\u00d8'.encode('utf8')
280
        result = transport.has(filename)
281
        self.assertEqual(
282
            [('call', 'has', (filename,))],
283
            client._calls)
284
        self.assertTrue(result)
285
286
3431.3.11 by Andrew Bennetts
Push remote_path_from_transport logic into SmartClientMedium, removing special-casing of bzr+http from _SmartClient.
287
class Test_ClientMedium_remote_path_from_transport(tests.TestCase):
288
    """Tests for the behaviour of client_medium.remote_path_from_transport."""
3313.3.3 by Andrew Bennetts
Add tests for _SmartClient.remote_path_for_transport.
289
290
    def assertRemotePath(self, expected, client_base, transport_base):
3431.3.11 by Andrew Bennetts
Push remote_path_from_transport logic into SmartClientMedium, removing special-casing of bzr+http from _SmartClient.
291
        """Assert that the result of
292
        SmartClientMedium.remote_path_from_transport is the expected value for
293
        a given client_base and transport_base.
3313.3.3 by Andrew Bennetts
Add tests for _SmartClient.remote_path_for_transport.
294
        """
3431.3.11 by Andrew Bennetts
Push remote_path_from_transport logic into SmartClientMedium, removing special-casing of bzr+http from _SmartClient.
295
        client_medium = medium.SmartClientMedium(client_base)
3313.3.3 by Andrew Bennetts
Add tests for _SmartClient.remote_path_for_transport.
296
        transport = get_transport(transport_base)
3431.3.11 by Andrew Bennetts
Push remote_path_from_transport logic into SmartClientMedium, removing special-casing of bzr+http from _SmartClient.
297
        result = client_medium.remote_path_from_transport(transport)
3313.3.3 by Andrew Bennetts
Add tests for _SmartClient.remote_path_for_transport.
298
        self.assertEqual(expected, result)
3431.3.11 by Andrew Bennetts
Push remote_path_from_transport logic into SmartClientMedium, removing special-casing of bzr+http from _SmartClient.
299
3313.3.3 by Andrew Bennetts
Add tests for _SmartClient.remote_path_for_transport.
300
    def test_remote_path_from_transport(self):
3431.3.11 by Andrew Bennetts
Push remote_path_from_transport logic into SmartClientMedium, removing special-casing of bzr+http from _SmartClient.
301
        """SmartClientMedium.remote_path_from_transport calculates a URL for
302
        the given transport relative to the root of the client base URL.
3313.3.3 by Andrew Bennetts
Add tests for _SmartClient.remote_path_for_transport.
303
        """
304
        self.assertRemotePath('xyz/', 'bzr://host/path', 'bzr://host/xyz')
305
        self.assertRemotePath(
306
            'path/xyz/', 'bzr://host/path', 'bzr://host/path/xyz')
307
3431.3.11 by Andrew Bennetts
Push remote_path_from_transport logic into SmartClientMedium, removing special-casing of bzr+http from _SmartClient.
308
    def assertRemotePathHTTP(self, expected, transport_base, relpath):
309
        """Assert that the result of
310
        HttpTransportBase.remote_path_from_transport is the expected value for
311
        a given transport_base and relpath of that transport.  (Note that
312
        HttpTransportBase is a subclass of SmartClientMedium)
313
        """
314
        base_transport = get_transport(transport_base)
315
        client_medium = base_transport.get_smart_medium()
316
        cloned_transport = base_transport.clone(relpath)
317
        result = client_medium.remote_path_from_transport(cloned_transport)
318
        self.assertEqual(expected, result)
319
        
3313.3.3 by Andrew Bennetts
Add tests for _SmartClient.remote_path_for_transport.
320
    def test_remote_path_from_transport_http(self):
321
        """Remote paths for HTTP transports are calculated differently to other
322
        transports.  They are just relative to the client base, not the root
323
        directory of the host.
324
        """
325
        for scheme in ['http:', 'https:', 'bzr+http:', 'bzr+https:']:
3431.3.11 by Andrew Bennetts
Push remote_path_from_transport logic into SmartClientMedium, removing special-casing of bzr+http from _SmartClient.
326
            self.assertRemotePathHTTP(
327
                '../xyz/', scheme + '//host/path', '../xyz/')
328
            self.assertRemotePathHTTP(
329
                'xyz/', scheme + '//host/path', 'xyz/')
3313.3.3 by Andrew Bennetts
Add tests for _SmartClient.remote_path_for_transport.
330
331
3453.4.1 by Andrew Bennetts
Better infrastructure on SmartClientMedium for tracking the remote version.
332
class Test_ClientMedium_remote_is_at_least(tests.TestCase):
333
    """Tests for the behaviour of client_medium.remote_is_at_least."""
334
335
    def test_initially_unlimited(self):
336
        """A fresh medium assumes that the remote side supports all
337
        versions.
338
        """
339
        client_medium = medium.SmartClientMedium('dummy base')
3453.4.10 by Andrew Bennetts
Change _is_remote_at_least to _is_remote_before.
340
        self.assertFalse(client_medium._is_remote_before((99, 99)))
3453.4.1 by Andrew Bennetts
Better infrastructure on SmartClientMedium for tracking the remote version.
341
    
3453.4.9 by Andrew Bennetts
Rename _remote_is_not to _remember_remote_is_before.
342
    def test__remember_remote_is_before(self):
343
        """Calling _remember_remote_is_before ratchets down the known remote
344
        version.
345
        """
3453.4.1 by Andrew Bennetts
Better infrastructure on SmartClientMedium for tracking the remote version.
346
        client_medium = medium.SmartClientMedium('dummy base')
347
        # Mark the remote side as being less than 1.6.  The remote side may
348
        # still be 1.5.
3453.4.9 by Andrew Bennetts
Rename _remote_is_not to _remember_remote_is_before.
349
        client_medium._remember_remote_is_before((1, 6))
3453.4.10 by Andrew Bennetts
Change _is_remote_at_least to _is_remote_before.
350
        self.assertTrue(client_medium._is_remote_before((1, 6)))
351
        self.assertFalse(client_medium._is_remote_before((1, 5)))
3453.4.9 by Andrew Bennetts
Rename _remote_is_not to _remember_remote_is_before.
352
        # Calling _remember_remote_is_before again with a lower value works.
353
        client_medium._remember_remote_is_before((1, 5))
3453.4.10 by Andrew Bennetts
Change _is_remote_at_least to _is_remote_before.
354
        self.assertTrue(client_medium._is_remote_before((1, 5)))
3453.4.9 by Andrew Bennetts
Rename _remote_is_not to _remember_remote_is_before.
355
        # You cannot call _remember_remote_is_before with a larger value.
3453.4.1 by Andrew Bennetts
Better infrastructure on SmartClientMedium for tracking the remote version.
356
        self.assertRaises(
3453.4.9 by Andrew Bennetts
Rename _remote_is_not to _remember_remote_is_before.
357
            AssertionError, client_medium._remember_remote_is_before, (1, 9))
3453.4.1 by Andrew Bennetts
Better infrastructure on SmartClientMedium for tracking the remote version.
358
359
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
360
class TestBzrDirOpenBranch(tests.TestCase):
361
362
    def test_branch_present(self):
363
        transport = MemoryTransport()
364
        transport.mkdir('quack')
365
        transport = transport.clone('quack')
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
366
        client = FakeClient(transport.base)
3691.2.10 by Martin Pool
Update more test_remote tests
367
        client.add_expected_call(
368
            'BzrDir.open_branch', ('quack/',),
369
            'success', ('ok', ''))
370
        client.add_expected_call(
371
            'BzrDir.find_repositoryV2', ('quack/',),
372
            'success', ('ok', '', 'no', 'no', 'no'))
373
        client.add_expected_call(
374
            'Branch.get_stacked_on_url', ('quack/',),
375
            'error', ('NotStacked',))
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
376
        bzrdir = RemoteBzrDir(transport, _client=client)
377
        result = bzrdir.open_branch()
378
        self.assertIsInstance(result, RemoteBranch)
379
        self.assertEqual(bzrdir, result.bzrdir)
3691.2.10 by Martin Pool
Update more test_remote tests
380
        client.finished_test()
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
381
382
    def test_branch_missing(self):
383
        transport = MemoryTransport()
384
        transport.mkdir('quack')
385
        transport = transport.clone('quack')
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
386
        client = FakeClient(transport.base)
387
        client.add_error_response('nobranch')
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
388
        bzrdir = RemoteBzrDir(transport, _client=client)
389
        self.assertRaises(errors.NotBranchError, bzrdir.open_branch)
390
        self.assertEqual(
3104.4.2 by Andrew Bennetts
All tests passing.
391
            [('call', 'BzrDir.open_branch', ('quack/',))],
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
392
            client._calls)
393
3211.4.1 by Robert Collins
* ``RemoteBzrDir._get_tree_branch`` no longer triggers ``_ensure_real``,
394
    def test__get_tree_branch(self):
395
        # _get_tree_branch is a form of open_branch, but it should only ask for
396
        # branch opening, not any other network requests.
397
        calls = []
398
        def open_branch():
399
            calls.append("Called")
400
            return "a-branch"
401
        transport = MemoryTransport()
402
        # no requests on the network - catches other api calls being made.
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
403
        client = FakeClient(transport.base)
3211.4.1 by Robert Collins
* ``RemoteBzrDir._get_tree_branch`` no longer triggers ``_ensure_real``,
404
        bzrdir = RemoteBzrDir(transport, _client=client)
405
        # patch the open_branch call to record that it was called.
406
        bzrdir.open_branch = open_branch
407
        self.assertEqual((None, "a-branch"), bzrdir._get_tree_branch())
408
        self.assertEqual(["Called"], calls)
409
        self.assertEqual([], client._calls)
410
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.
411
    def test_url_quoting_of_path(self):
412
        # Relpaths on the wire should not be URL-escaped.  So "~" should be
413
        # transmitted as "~", not "%7E".
3431.3.1 by Andrew Bennetts
First rough cut of a fix for bug #230550, by adding .base to SmartClientMedia rather than relying on other objects to track this accurately while reusing client media.
414
        transport = RemoteTCPTransport('bzr://localhost/~hello/')
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
415
        client = FakeClient(transport.base)
3691.2.10 by Martin Pool
Update more test_remote tests
416
        client.add_expected_call(
417
            'BzrDir.open_branch', ('~hello/',),
418
            'success', ('ok', ''))
419
        client.add_expected_call(
420
            'BzrDir.find_repositoryV2', ('~hello/',),
421
            'success', ('ok', '', 'no', 'no', 'no'))
422
        client.add_expected_call(
423
            'Branch.get_stacked_on_url', ('~hello/',),
424
            'error', ('NotStacked',))
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.
425
        bzrdir = RemoteBzrDir(transport, _client=client)
426
        result = bzrdir.open_branch()
3691.2.10 by Martin Pool
Update more test_remote tests
427
        client.finished_test()
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.
428
3221.3.3 by Robert Collins
* Hook up the new remote method ``RemoteBzrDir.find_repositoryV2`` so
429
    def check_open_repository(self, rich_root, subtrees, external_lookup='no'):
3104.4.2 by Andrew Bennetts
All tests passing.
430
        transport = MemoryTransport()
431
        transport.mkdir('quack')
432
        transport = transport.clone('quack')
2018.5.118 by Robert Collins
Fix RemoteRepositoryFormat to have appropriate rich_root_data and support_tree_reference.
433
        if rich_root:
2018.5.166 by Andrew Bennetts
Small changes in response to Aaron's review.
434
            rich_response = 'yes'
2018.5.118 by Robert Collins
Fix RemoteRepositoryFormat to have appropriate rich_root_data and support_tree_reference.
435
        else:
2018.5.166 by Andrew Bennetts
Small changes in response to Aaron's review.
436
            rich_response = 'no'
2018.5.118 by Robert Collins
Fix RemoteRepositoryFormat to have appropriate rich_root_data and support_tree_reference.
437
        if subtrees:
2018.5.166 by Andrew Bennetts
Small changes in response to Aaron's review.
438
            subtree_response = 'yes'
2018.5.118 by Robert Collins
Fix RemoteRepositoryFormat to have appropriate rich_root_data and support_tree_reference.
439
        else:
2018.5.166 by Andrew Bennetts
Small changes in response to Aaron's review.
440
            subtree_response = 'no'
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
441
        client = FakeClient(transport.base)
442
        client.add_success_response(
443
            'ok', '', rich_response, subtree_response, external_lookup)
2018.5.118 by Robert Collins
Fix RemoteRepositoryFormat to have appropriate rich_root_data and support_tree_reference.
444
        bzrdir = RemoteBzrDir(transport, _client=client)
445
        result = bzrdir.open_repository()
446
        self.assertEqual(
3221.3.3 by Robert Collins
* Hook up the new remote method ``RemoteBzrDir.find_repositoryV2`` so
447
            [('call', 'BzrDir.find_repositoryV2', ('quack/',))],
2018.5.118 by Robert Collins
Fix RemoteRepositoryFormat to have appropriate rich_root_data and support_tree_reference.
448
            client._calls)
449
        self.assertIsInstance(result, RemoteRepository)
450
        self.assertEqual(bzrdir, result.bzrdir)
451
        self.assertEqual(rich_root, result._format.rich_root_data)
2018.5.138 by Robert Collins
Merge bzr.dev.
452
        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.
453
454
    def test_open_repository_sets_format_attributes(self):
455
        self.check_open_repository(True, True)
456
        self.check_open_repository(False, True)
457
        self.check_open_repository(True, False)
458
        self.check_open_repository(False, False)
3221.3.3 by Robert Collins
* Hook up the new remote method ``RemoteBzrDir.find_repositoryV2`` so
459
        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.
460
2432.3.2 by Andrew Bennetts
Add test, and tidy implementation.
461
    def test_old_server(self):
462
        """RemoteBzrDirFormat should fail to probe if the server version is too
463
        old.
464
        """
465
        self.assertRaises(errors.NotBranchError,
466
            RemoteBzrDirFormat.probe_transport, OldServerTransport())
467
468
3297.3.3 by Andrew Bennetts
SmartClientRequestProtocol*.read_response_tuple can now raise UnknownSmartMethod. Callers no longer need to do their own ad hoc unknown smart method error detection.
469
class TestBzrDirOpenRepository(tests.TestCase):
470
471
    def test_backwards_compat_1_2(self):
472
        transport = MemoryTransport()
473
        transport.mkdir('quack')
474
        transport = transport.clone('quack')
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
475
        client = FakeClient(transport.base)
476
        client.add_unknown_method_response('RemoteRepository.find_repositoryV2')
477
        client.add_success_response('ok', '', 'no', 'no')
3297.3.3 by Andrew Bennetts
SmartClientRequestProtocol*.read_response_tuple can now raise UnknownSmartMethod. Callers no longer need to do their own ad hoc unknown smart method error detection.
478
        bzrdir = RemoteBzrDir(transport, _client=client)
479
        repo = bzrdir.open_repository()
480
        self.assertEqual(
481
            [('call', 'BzrDir.find_repositoryV2', ('quack/',)),
482
             ('call', 'BzrDir.find_repository', ('quack/',))],
483
            client._calls)
484
485
2432.3.2 by Andrew Bennetts
Add test, and tidy implementation.
486
class OldSmartClient(object):
487
    """A fake smart client for test_old_version that just returns a version one
488
    response to the 'hello' (query version) command.
489
    """
490
491
    def get_request(self):
492
        input_file = StringIO('ok\x011\n')
493
        output_file = StringIO()
494
        client_medium = medium.SmartSimplePipesClientMedium(
495
            input_file, output_file)
496
        return medium.SmartClientStreamMediumRequest(client_medium)
497
3241.1.1 by Andrew Bennetts
Shift protocol version querying from RemoteBzrDirFormat into SmartClientMedium.
498
    def protocol_version(self):
499
        return 1
500
2432.3.2 by Andrew Bennetts
Add test, and tidy implementation.
501
502
class OldServerTransport(object):
503
    """A fake transport for test_old_server that reports it's smart server
504
    protocol version as version one.
505
    """
506
507
    def __init__(self):
508
        self.base = 'fake:'
509
510
    def get_smart_client(self):
511
        return OldSmartClient()
512
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
513
3692.1.1 by Andrew Bennetts
Make RemoteBranch.lock_write lock the repository too.
514
class RemoteBranchTestCase(tests.TestCase):
515
516
    def make_remote_branch(self, transport, client):
517
        """Make a RemoteBranch using 'client' as its _SmartClient.
518
        
519
        A RemoteBzrDir and RemoteRepository will also be created to fill out
520
        the RemoteBranch, albeit with stub values for some of their attributes.
521
        """
522
        # we do not want bzrdir to make any remote calls, so use False as its
523
        # _client.  If it tries to make a remote call, this will fail
524
        # immediately.
525
        bzrdir = RemoteBzrDir(transport, _client=False)
526
        repo = RemoteRepository(bzrdir, None, _client=client)
527
        return RemoteBranch(bzrdir, repo, _client=client)
528
529
530
class TestBranchLastRevisionInfo(RemoteBranchTestCase):
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
531
532
    def test_empty_branch(self):
533
        # in an empty branch we decode the response properly
534
        transport = MemoryTransport()
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
535
        client = FakeClient(transport.base)
3691.2.8 by Martin Pool
Update some test_remote tests for Branch.get_stacked_on_url and with clearer assertions
536
        client.add_expected_call(
537
            'Branch.get_stacked_on_url', ('quack/',),
538
            'error', ('NotStacked',))
539
        client.add_expected_call(
540
            'Branch.last_revision_info', ('quack/',),
541
            'success', ('ok', '0', 'null:'))
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
542
        transport.mkdir('quack')
543
        transport = transport.clone('quack')
3692.1.1 by Andrew Bennetts
Make RemoteBranch.lock_write lock the repository too.
544
        branch = self.make_remote_branch(transport, client)
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
545
        result = branch.last_revision_info()
3691.2.8 by Martin Pool
Update some test_remote tests for Branch.get_stacked_on_url and with clearer assertions
546
        client.finished_test()
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
547
        self.assertEqual((0, NULL_REVISION), result)
548
549
    def test_non_empty_branch(self):
550
        # 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.
551
        revid = u'\xc8'.encode('utf8')
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
552
        transport = MemoryTransport()
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
553
        client = FakeClient(transport.base)
3691.2.8 by Martin Pool
Update some test_remote tests for Branch.get_stacked_on_url and with clearer assertions
554
        client.add_expected_call(
555
            'Branch.get_stacked_on_url', ('kwaak/',),
556
            'error', ('NotStacked',))
557
        client.add_expected_call(
558
            'Branch.last_revision_info', ('kwaak/',),
559
            'success', ('ok', '2', revid))
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
560
        transport.mkdir('kwaak')
561
        transport = transport.clone('kwaak')
3692.1.1 by Andrew Bennetts
Make RemoteBranch.lock_write lock the repository too.
562
        branch = self.make_remote_branch(transport, client)
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
563
        result = branch.last_revision_info()
2018.5.106 by Andrew Bennetts
Update tests in test_remote to use utf-8 byte strings for revision IDs, rather than unicode strings.
564
        self.assertEqual((2, revid), result)
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
565
566
3691.2.11 by Martin Pool
More tests around RemoteBranch stacking.
567
class TestBranch_get_stacked_on_url(tests.TestCaseWithMemoryTransport):
3691.2.5 by Martin Pool
Add Branch.get_stacked_on_url rpc and tests for same
568
    """Test Branch._get_stacked_on_url rpc"""
569
3691.2.10 by Martin Pool
Update more test_remote tests
570
    def test_get_stacked_on_invalid_url(self):
571
        raise tests.KnownFailure('opening a branch requires the server to open the fallback repository')
572
        transport = FakeRemoteTransport('fakeremotetransport:///')
3691.2.5 by Martin Pool
Add Branch.get_stacked_on_url rpc and tests for same
573
        client = FakeClient(transport.base)
3691.2.10 by Martin Pool
Update more test_remote tests
574
        client.add_expected_call(
575
            'Branch.get_stacked_on_url', ('.',),
576
            'success', ('ok', 'file:///stacked/on'))
3691.2.5 by Martin Pool
Add Branch.get_stacked_on_url rpc and tests for same
577
        bzrdir = RemoteBzrDir(transport, _client=client)
578
        branch = RemoteBranch(bzrdir, None, _client=client)
579
        result = branch.get_stacked_on_url()
580
        self.assertEqual(
581
            'file:///stacked/on', result)
582
3691.2.12 by Martin Pool
Add test for coping without Branch.get_stacked_on_url
583
    def test_backwards_compatible(self):
584
        # like with bzr1.6 with no Branch.get_stacked_on_url rpc
585
        base_branch = self.make_branch('base', format='1.6')
586
        stacked_branch = self.make_branch('stacked', format='1.6')
587
        stacked_branch.set_stacked_on_url('../base')
588
        client = FakeClient(self.get_url())
589
        client.add_expected_call(
590
            'BzrDir.open_branch', ('stacked/',),
591
            'success', ('ok', ''))
592
        client.add_expected_call(
593
            'BzrDir.find_repositoryV2', ('stacked/',),
594
            'success', ('ok', '', 'no', 'no', 'no'))
595
        # called twice, once from constructor and then again by us
596
        client.add_expected_call(
597
            'Branch.get_stacked_on_url', ('stacked/',),
598
            'unknown', ('Branch.get_stacked_on_url',))
599
        client.add_expected_call(
600
            'Branch.get_stacked_on_url', ('stacked/',),
601
            'unknown', ('Branch.get_stacked_on_url',))
602
        # this will also do vfs access, but that goes direct to the transport
603
        # and isn't seen by the FakeClient.
604
        bzrdir = RemoteBzrDir(self.get_transport('stacked'), _client=client)
605
        branch = bzrdir.open_branch()
606
        result = branch.get_stacked_on_url()
607
        self.assertEqual('../base', result)
608
        client.finished_test()
609
        # it's in the fallback list both for the RemoteRepository and its vfs
610
        # repository
611
        self.assertEqual(1, len(branch.repository._fallback_repositories))
612
        self.assertEqual(1,
613
            len(branch.repository._real_repository._fallback_repositories))
614
3691.2.11 by Martin Pool
More tests around RemoteBranch stacking.
615
    def test_get_stacked_on_real_branch(self):
616
        base_branch = self.make_branch('base', format='1.6')
617
        stacked_branch = self.make_branch('stacked', format='1.6')
618
        stacked_branch.set_stacked_on_url('../base')
619
        client = FakeClient(self.get_url())
620
        client.add_expected_call(
621
            'BzrDir.open_branch', ('stacked/',),
622
            'success', ('ok', ''))
623
        client.add_expected_call(
624
            'BzrDir.find_repositoryV2', ('stacked/',),
625
            'success', ('ok', '', 'no', 'no', 'no'))
626
        # called twice, once from constructor and then again by us
627
        client.add_expected_call(
628
            'Branch.get_stacked_on_url', ('stacked/',),
629
            'success', ('ok', '../base'))
630
        client.add_expected_call(
631
            'Branch.get_stacked_on_url', ('stacked/',),
632
            'success', ('ok', '../base'))
633
        bzrdir = RemoteBzrDir(self.get_transport('stacked'), _client=client)
634
        branch = bzrdir.open_branch()
635
        result = branch.get_stacked_on_url()
636
        self.assertEqual('../base', result)
637
        client.finished_test()
638
        # it's in the fallback list both for the RemoteRepository and its vfs
639
        # repository
640
        self.assertEqual(1, len(branch.repository._fallback_repositories))
641
        self.assertEqual(1,
642
            len(branch.repository._real_repository._fallback_repositories))
643
3691.2.5 by Martin Pool
Add Branch.get_stacked_on_url rpc and tests for same
644
3692.1.1 by Andrew Bennetts
Make RemoteBranch.lock_write lock the repository too.
645
class TestBranchSetLastRevision(RemoteBranchTestCase):
2018.12.3 by Andrew Bennetts
Add a Branch.set_last_revision smart method, and make RemoteBranch.set_revision_history use it.
646
647
    def test_set_empty(self):
648
        # set_revision_history([]) is translated to calling
649
        # Branch.set_last_revision(path, '') on the wire.
3104.4.2 by Andrew Bennetts
All tests passing.
650
        transport = MemoryTransport()
651
        transport.mkdir('branch')
652
        transport = transport.clone('branch')
653
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
654
        client = FakeClient(transport.base)
3691.2.10 by Martin Pool
Update more test_remote tests
655
        client.add_expected_call(
656
            'Branch.get_stacked_on_url', ('branch/',),
657
            'error', ('NotStacked',))
658
        client.add_expected_call(
659
            'Branch.lock_write', ('branch/', '', ''),
660
            'success', ('ok', 'branch token', 'repo token'))
661
        client.add_expected_call(
662
            'Branch.set_last_revision', ('branch/', 'branch token', 'repo token', 'null:',),
663
            'success', ('ok',))
664
        client.add_expected_call(
665
            'Branch.unlock', ('branch/', 'branch token', 'repo token'),
666
            'success', ('ok',))
3692.1.1 by Andrew Bennetts
Make RemoteBranch.lock_write lock the repository too.
667
        branch = self.make_remote_branch(transport, client)
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
668
        # This is a hack to work around the problem that RemoteBranch currently
669
        # unnecessarily invokes _ensure_real upon a call to lock_write.
670
        branch._ensure_real = lambda: None
671
        branch.lock_write()
2018.12.3 by Andrew Bennetts
Add a Branch.set_last_revision smart method, and make RemoteBranch.set_revision_history use it.
672
        result = branch.set_revision_history([])
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
673
        branch.unlock()
2018.12.3 by Andrew Bennetts
Add a Branch.set_last_revision smart method, and make RemoteBranch.set_revision_history use it.
674
        self.assertEqual(None, result)
3691.2.10 by Martin Pool
Update more test_remote tests
675
        client.finished_test()
2018.12.3 by Andrew Bennetts
Add a Branch.set_last_revision smart method, and make RemoteBranch.set_revision_history use it.
676
677
    def test_set_nonempty(self):
678
        # set_revision_history([rev-id1, ..., rev-idN]) is translated to calling
679
        # Branch.set_last_revision(path, rev-idN) on the wire.
3104.4.2 by Andrew Bennetts
All tests passing.
680
        transport = MemoryTransport()
681
        transport.mkdir('branch')
682
        transport = transport.clone('branch')
683
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
684
        client = FakeClient(transport.base)
3691.2.10 by Martin Pool
Update more test_remote tests
685
        client.add_expected_call(
686
            'Branch.get_stacked_on_url', ('branch/',),
687
            'error', ('NotStacked',))
688
        client.add_expected_call(
689
            'Branch.lock_write', ('branch/', '', ''),
690
            'success', ('ok', 'branch token', 'repo token'))
691
        client.add_expected_call(
692
            'Branch.set_last_revision', ('branch/', 'branch token', 'repo token', 'rev-id2',),
693
            'success', ('ok',))
694
        client.add_expected_call(
695
            'Branch.unlock', ('branch/', 'branch token', 'repo token'),
696
            'success', ('ok',))
3692.1.1 by Andrew Bennetts
Make RemoteBranch.lock_write lock the repository too.
697
        branch = self.make_remote_branch(transport, client)
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
698
        # This is a hack to work around the problem that RemoteBranch currently
699
        # unnecessarily invokes _ensure_real upon a call to lock_write.
700
        branch._ensure_real = lambda: None
701
        # Lock the branch, reset the record of remote calls.
702
        branch.lock_write()
2018.12.3 by Andrew Bennetts
Add a Branch.set_last_revision smart method, and make RemoteBranch.set_revision_history use it.
703
        result = branch.set_revision_history(['rev-id1', 'rev-id2'])
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
704
        branch.unlock()
2018.12.3 by Andrew Bennetts
Add a Branch.set_last_revision smart method, and make RemoteBranch.set_revision_history use it.
705
        self.assertEqual(None, result)
3691.2.10 by Martin Pool
Update more test_remote tests
706
        client.finished_test()
2018.12.3 by Andrew Bennetts
Add a Branch.set_last_revision smart method, and make RemoteBranch.set_revision_history use it.
707
708
    def test_no_such_revision(self):
709
        transport = MemoryTransport()
710
        transport.mkdir('branch')
711
        transport = transport.clone('branch')
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
712
        # A response of 'NoSuchRevision' is translated into an exception.
713
        client = FakeClient(transport.base)
3691.2.9 by Martin Pool
Convert and update more test_remote tests
714
        client.add_expected_call(
715
            'Branch.get_stacked_on_url', ('branch/',),
716
            'error', ('NotStacked',))
717
        client.add_expected_call(
718
            'Branch.lock_write', ('branch/', '', ''),
719
            'success', ('ok', 'branch token', 'repo token'))
720
        client.add_expected_call(
721
            'Branch.set_last_revision', ('branch/', 'branch token', 'repo token', 'rev-id',),
722
            'error', ('NoSuchRevision', 'rev-id'))
723
        client.add_expected_call(
724
            'Branch.unlock', ('branch/', 'branch token', 'repo token'),
725
            'success', ('ok',))
2018.12.3 by Andrew Bennetts
Add a Branch.set_last_revision smart method, and make RemoteBranch.set_revision_history use it.
726
3692.1.1 by Andrew Bennetts
Make RemoteBranch.lock_write lock the repository too.
727
        branch = self.make_remote_branch(transport, client)
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
728
        branch.lock_write()
2018.12.3 by Andrew Bennetts
Add a Branch.set_last_revision smart method, and make RemoteBranch.set_revision_history use it.
729
        self.assertRaises(
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
730
            errors.NoSuchRevision, branch.set_revision_history, ['rev-id'])
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
731
        branch.unlock()
3691.2.9 by Martin Pool
Convert and update more test_remote tests
732
        client.finished_test()
2018.12.3 by Andrew Bennetts
Add a Branch.set_last_revision smart method, and make RemoteBranch.set_revision_history use it.
733
3577.1.1 by Andrew Bennetts
Cherry-pick TipChangeRejected changes from pre-branch-tip-changed-hook loom.
734
    def test_tip_change_rejected(self):
735
        """TipChangeRejected responses cause a TipChangeRejected exception to
736
        be raised.
737
        """
738
        transport = MemoryTransport()
739
        transport.mkdir('branch')
740
        transport = transport.clone('branch')
741
        client = FakeClient(transport.base)
742
        rejection_msg_unicode = u'rejection message\N{INTERROBANG}'
743
        rejection_msg_utf8 = rejection_msg_unicode.encode('utf8')
3691.2.10 by Martin Pool
Update more test_remote tests
744
        client.add_expected_call(
745
            'Branch.get_stacked_on_url', ('branch/',),
746
            'error', ('NotStacked',))
747
        client.add_expected_call(
748
            'Branch.lock_write', ('branch/', '', ''),
749
            'success', ('ok', 'branch token', 'repo token'))
750
        client.add_expected_call(
751
            'Branch.set_last_revision', ('branch/', 'branch token', 'repo token', 'rev-id',),
752
            'error', ('TipChangeRejected', rejection_msg_utf8))
753
        client.add_expected_call(
754
            'Branch.unlock', ('branch/', 'branch token', 'repo token'),
755
            'success', ('ok',))
3692.1.1 by Andrew Bennetts
Make RemoteBranch.lock_write lock the repository too.
756
        branch = self.make_remote_branch(transport, client)
3577.1.1 by Andrew Bennetts
Cherry-pick TipChangeRejected changes from pre-branch-tip-changed-hook loom.
757
        branch._ensure_real = lambda: None
758
        branch.lock_write()
759
        self.addCleanup(branch.unlock)
760
        # The 'TipChangeRejected' error response triggered by calling
761
        # set_revision_history causes a TipChangeRejected exception.
762
        err = self.assertRaises(
763
            errors.TipChangeRejected, branch.set_revision_history, ['rev-id'])
764
        # The UTF-8 message from the response has been decoded into a unicode
765
        # object.
766
        self.assertIsInstance(err.msg, unicode)
767
        self.assertEqual(rejection_msg_unicode, err.msg)
3691.2.10 by Martin Pool
Update more test_remote tests
768
        branch.unlock()
769
        client.finished_test()
3577.1.1 by Andrew Bennetts
Cherry-pick TipChangeRejected changes from pre-branch-tip-changed-hook loom.
770
2018.12.3 by Andrew Bennetts
Add a Branch.set_last_revision smart method, and make RemoteBranch.set_revision_history use it.
771
3692.1.1 by Andrew Bennetts
Make RemoteBranch.lock_write lock the repository too.
772
class TestBranchSetLastRevisionInfo(RemoteBranchTestCase):
2892.2.1 by Andrew Bennetts
Add Branch.set_last_revision_info smart method, and make the RemoteBranch client use it.
773
3297.4.2 by Andrew Bennetts
Add backwards compatibility for servers older than 1.4.
774
    def test_set_last_revision_info(self):
2892.2.1 by Andrew Bennetts
Add Branch.set_last_revision_info smart method, and make the RemoteBranch client use it.
775
        # set_last_revision_info(num, 'rev-id') is translated to calling
776
        # Branch.set_last_revision_info(num, 'rev-id') on the wire.
3297.4.1 by Andrew Bennetts
Merge 'Add Branch.set_last_revision_info smart method'.
777
        transport = MemoryTransport()
778
        transport.mkdir('branch')
779
        transport = transport.clone('branch')
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
780
        client = FakeClient(transport.base)
3691.2.10 by Martin Pool
Update more test_remote tests
781
        # get_stacked_on_url
782
        client.add_error_response('NotStacked')
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
783
        # lock_write
784
        client.add_success_response('ok', 'branch token', 'repo token')
785
        # set_last_revision
786
        client.add_success_response('ok')
787
        # unlock
788
        client.add_success_response('ok')
2892.2.1 by Andrew Bennetts
Add Branch.set_last_revision_info smart method, and make the RemoteBranch client use it.
789
3692.1.1 by Andrew Bennetts
Make RemoteBranch.lock_write lock the repository too.
790
        branch = self.make_remote_branch(transport, client)
2892.2.1 by Andrew Bennetts
Add Branch.set_last_revision_info smart method, and make the RemoteBranch client use it.
791
        # Lock the branch, reset the record of remote calls.
792
        branch.lock_write()
793
        client._calls = []
794
        result = branch.set_last_revision_info(1234, 'a-revision-id')
795
        self.assertEqual(
796
            [('call', 'Branch.set_last_revision_info',
3297.4.1 by Andrew Bennetts
Merge 'Add Branch.set_last_revision_info smart method'.
797
                ('branch/', 'branch token', 'repo token',
2892.2.1 by Andrew Bennetts
Add Branch.set_last_revision_info smart method, and make the RemoteBranch client use it.
798
                 '1234', 'a-revision-id'))],
799
            client._calls)
800
        self.assertEqual(None, result)
801
802
    def test_no_such_revision(self):
803
        # A response of 'NoSuchRevision' is translated into an exception.
804
        transport = MemoryTransport()
805
        transport.mkdir('branch')
806
        transport = transport.clone('branch')
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
807
        client = FakeClient(transport.base)
3691.2.10 by Martin Pool
Update more test_remote tests
808
        # get_stacked_on_url
809
        client.add_error_response('NotStacked')
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
810
        # lock_write
811
        client.add_success_response('ok', 'branch token', 'repo token')
812
        # set_last_revision
813
        client.add_error_response('NoSuchRevision', 'revid')
814
        # unlock
815
        client.add_success_response('ok')
2892.2.1 by Andrew Bennetts
Add Branch.set_last_revision_info smart method, and make the RemoteBranch client use it.
816
3692.1.1 by Andrew Bennetts
Make RemoteBranch.lock_write lock the repository too.
817
        branch = self.make_remote_branch(transport, client)
2892.2.1 by Andrew Bennetts
Add Branch.set_last_revision_info smart method, and make the RemoteBranch client use it.
818
        # Lock the branch, reset the record of remote calls.
819
        branch.lock_write()
820
        client._calls = []
821
822
        self.assertRaises(
823
            errors.NoSuchRevision, branch.set_last_revision_info, 123, 'revid')
824
        branch.unlock()
825
3297.4.2 by Andrew Bennetts
Add backwards compatibility for servers older than 1.4.
826
    def lock_remote_branch(self, branch):
827
        """Trick a RemoteBranch into thinking it is locked."""
828
        branch._lock_mode = 'w'
829
        branch._lock_count = 2
830
        branch._lock_token = 'branch token'
831
        branch._repo_lock_token = 'repo token'
3692.1.1 by Andrew Bennetts
Make RemoteBranch.lock_write lock the repository too.
832
        branch.repository._lock_mode = 'w'
833
        branch.repository._lock_count = 2
834
        branch.repository._lock_token = 'repo token'
3297.4.2 by Andrew Bennetts
Add backwards compatibility for servers older than 1.4.
835
836
    def test_backwards_compatibility(self):
837
        """If the server does not support the Branch.set_last_revision_info
838
        verb (which is new in 1.4), then the client falls back to VFS methods.
839
        """
840
        # This test is a little messy.  Unlike most tests in this file, it
841
        # doesn't purely test what a Remote* object sends over the wire, and
842
        # how it reacts to responses from the wire.  It instead relies partly
843
        # on asserting that the RemoteBranch will call
844
        # self._real_branch.set_last_revision_info(...).
845
846
        # First, set up our RemoteBranch with a FakeClient that raises
847
        # UnknownSmartMethod, and a StubRealBranch that logs how it is called.
848
        transport = MemoryTransport()
849
        transport.mkdir('branch')
850
        transport = transport.clone('branch')
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
851
        client = FakeClient(transport.base)
3691.2.10 by Martin Pool
Update more test_remote tests
852
        client.add_expected_call(
853
            'Branch.get_stacked_on_url', ('branch/',),
854
            'error', ('NotStacked',))
855
        client.add_expected_call(
856
            'Branch.set_last_revision_info',
857
            ('branch/', 'branch token', 'repo token', '1234', 'a-revision-id',),
858
            'unknown', 'Branch.set_last_revision_info')
859
3692.1.1 by Andrew Bennetts
Make RemoteBranch.lock_write lock the repository too.
860
        branch = self.make_remote_branch(transport, client)
3297.4.2 by Andrew Bennetts
Add backwards compatibility for servers older than 1.4.
861
        class StubRealBranch(object):
862
            def __init__(self):
863
                self.calls = []
864
            def set_last_revision_info(self, revno, revision_id):
865
                self.calls.append(
866
                    ('set_last_revision_info', revno, revision_id))
3441.5.5 by Andrew Bennetts
Some small tweaks and comments.
867
            def _clear_cached_state(self):
868
                pass
3297.4.2 by Andrew Bennetts
Add backwards compatibility for servers older than 1.4.
869
        real_branch = StubRealBranch()
870
        branch._real_branch = real_branch
871
        self.lock_remote_branch(branch)
872
873
        # Call set_last_revision_info, and verify it behaved as expected.
874
        result = branch.set_last_revision_info(1234, 'a-revision-id')
875
        self.assertEqual(
876
            [('set_last_revision_info', 1234, 'a-revision-id')],
877
            real_branch.calls)
3691.2.10 by Martin Pool
Update more test_remote tests
878
        client.finished_test()
3297.4.2 by Andrew Bennetts
Add backwards compatibility for servers older than 1.4.
879
3245.4.53 by Andrew Bennetts
Add some missing 'raise' statements to test_remote.
880
    def test_unexpected_error(self):
3697.2.6 by Martin Pool
Merge 261315 fix into 1.7 branch
881
        # If the server sends an error the client doesn't understand, it gets
882
        # turned into an UnknownErrorFromSmartServer, which is presented as a
883
        # non-internal error to the user.
3245.4.53 by Andrew Bennetts
Add some missing 'raise' statements to test_remote.
884
        transport = MemoryTransport()
885
        transport.mkdir('branch')
886
        transport = transport.clone('branch')
887
        client = FakeClient(transport.base)
3691.2.10 by Martin Pool
Update more test_remote tests
888
        # get_stacked_on_url
889
        client.add_error_response('NotStacked')
3245.4.53 by Andrew Bennetts
Add some missing 'raise' statements to test_remote.
890
        # lock_write
891
        client.add_success_response('ok', 'branch token', 'repo token')
892
        # set_last_revision
893
        client.add_error_response('UnexpectedError')
894
        # unlock
895
        client.add_success_response('ok')
896
3692.1.1 by Andrew Bennetts
Make RemoteBranch.lock_write lock the repository too.
897
        branch = self.make_remote_branch(transport, client)
3245.4.53 by Andrew Bennetts
Add some missing 'raise' statements to test_remote.
898
        # Lock the branch, reset the record of remote calls.
899
        branch.lock_write()
900
        client._calls = []
901
902
        err = self.assertRaises(
3690.1.2 by Andrew Bennetts
Rename UntranslateableErrorFromSmartServer -> UnknownErrorFromSmartServer.
903
            errors.UnknownErrorFromSmartServer,
3245.4.53 by Andrew Bennetts
Add some missing 'raise' statements to test_remote.
904
            branch.set_last_revision_info, 123, 'revid')
905
        self.assertEqual(('UnexpectedError',), err.error_tuple)
906
        branch.unlock()
907
3577.1.1 by Andrew Bennetts
Cherry-pick TipChangeRejected changes from pre-branch-tip-changed-hook loom.
908
    def test_tip_change_rejected(self):
909
        """TipChangeRejected responses cause a TipChangeRejected exception to
910
        be raised.
911
        """
912
        transport = MemoryTransport()
913
        transport.mkdir('branch')
914
        transport = transport.clone('branch')
915
        client = FakeClient(transport.base)
3691.2.10 by Martin Pool
Update more test_remote tests
916
        # get_stacked_on_url
917
        client.add_error_response('NotStacked')
3577.1.1 by Andrew Bennetts
Cherry-pick TipChangeRejected changes from pre-branch-tip-changed-hook loom.
918
        # lock_write
919
        client.add_success_response('ok', 'branch token', 'repo token')
920
        # set_last_revision
921
        client.add_error_response('TipChangeRejected', 'rejection message')
922
        # unlock
923
        client.add_success_response('ok')
924
3692.1.1 by Andrew Bennetts
Make RemoteBranch.lock_write lock the repository too.
925
        branch = self.make_remote_branch(transport, client)
3577.1.1 by Andrew Bennetts
Cherry-pick TipChangeRejected changes from pre-branch-tip-changed-hook loom.
926
        # Lock the branch, reset the record of remote calls.
927
        branch.lock_write()
928
        self.addCleanup(branch.unlock)
929
        client._calls = []
930
931
        # The 'TipChangeRejected' error response triggered by calling
932
        # set_last_revision_info causes a TipChangeRejected exception.
933
        err = self.assertRaises(
934
            errors.TipChangeRejected,
935
            branch.set_last_revision_info, 123, 'revid')
936
        self.assertEqual('rejection message', err.msg)
937
2892.2.1 by Andrew Bennetts
Add Branch.set_last_revision_info smart method, and make the RemoteBranch client use it.
938
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.
939
class TestBranchControlGetBranchConf(tests.TestCaseWithMemoryTransport):
3408.3.1 by Martin Pool
Remove erroneous handling of branch.conf for RemoteBranch
940
    """Getting the branch configuration should use an abstract method not vfs.
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
941
    """
942
943
    def test_get_branch_conf(self):
3408.3.1 by Martin Pool
Remove erroneous handling of branch.conf for RemoteBranch
944
        raise tests.KnownFailure('branch.conf is not retrieved by get_config_file')
3407.2.10 by Martin Pool
Merge trunk
945
        ## # We should see that branch.get_config() does a single rpc to get the
946
        ## # remote configuration file, abstracting away where that is stored on
947
        ## # the server.  However at the moment it always falls back to using the
948
        ## # vfs, and this would need some changes in config.py.
3408.3.1 by Martin Pool
Remove erroneous handling of branch.conf for RemoteBranch
949
3407.2.10 by Martin Pool
Merge trunk
950
        ## # in an empty branch we decode the response properly
951
        ## client = FakeClient([(('ok', ), '# config file body')], self.get_url())
952
        ## # we need to make a real branch because the remote_branch.control_files
953
        ## # will trigger _ensure_real.
954
        ## branch = self.make_branch('quack')
955
        ## transport = branch.bzrdir.root_transport
956
        ## # we do not want bzrdir to make any remote calls
957
        ## bzrdir = RemoteBzrDir(transport, _client=False)
958
        ## branch = RemoteBranch(bzrdir, None, _client=client)
959
        ## config = branch.get_config()
960
        ## self.assertEqual(
961
        ##     [('call_expecting_body', 'Branch.get_config_file', ('quack/',))],
962
        ##     client._calls)
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
963
964
3692.1.1 by Andrew Bennetts
Make RemoteBranch.lock_write lock the repository too.
965
class TestBranchLockWrite(RemoteBranchTestCase):
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.
966
967
    def test_lock_write_unlockable(self):
968
        transport = MemoryTransport()
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
969
        client = FakeClient(transport.base)
3691.2.9 by Martin Pool
Convert and update more test_remote tests
970
        client.add_expected_call(
971
            'Branch.get_stacked_on_url', ('quack/',),
972
            'error', ('NotStacked',),)
973
        client.add_expected_call(
974
            'Branch.lock_write', ('quack/', '', ''),
975
            'error', ('UnlockableTransport',))
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.
976
        transport.mkdir('quack')
977
        transport = transport.clone('quack')
3692.1.1 by Andrew Bennetts
Make RemoteBranch.lock_write lock the repository too.
978
        branch = self.make_remote_branch(transport, client)
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.
979
        self.assertRaises(errors.UnlockableTransport, branch.lock_write)
3691.2.9 by Martin Pool
Convert and update more test_remote tests
980
        client.finished_test()
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.
981
982
2466.2.2 by Andrew Bennetts
Add tests for RemoteTransport.is_readonly in the style of the other remote object tests.
983
class TestTransportIsReadonly(tests.TestCase):
984
985
    def test_true(self):
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
986
        client = FakeClient()
987
        client.add_success_response('yes')
2466.2.2 by Andrew Bennetts
Add tests for RemoteTransport.is_readonly in the style of the other remote object tests.
988
        transport = RemoteTransport('bzr://example.com/', medium=False,
989
                                    _client=client)
990
        self.assertEqual(True, transport.is_readonly())
991
        self.assertEqual(
992
            [('call', 'Transport.is_readonly', ())],
993
            client._calls)
994
995
    def test_false(self):
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
996
        client = FakeClient()
997
        client.add_success_response('no')
2466.2.2 by Andrew Bennetts
Add tests for RemoteTransport.is_readonly in the style of the other remote object tests.
998
        transport = RemoteTransport('bzr://example.com/', medium=False,
999
                                    _client=client)
1000
        self.assertEqual(False, transport.is_readonly())
1001
        self.assertEqual(
1002
            [('call', 'Transport.is_readonly', ())],
1003
            client._calls)
1004
1005
    def test_error_from_old_server(self):
1006
        """bzr 0.15 and earlier servers don't recognise the is_readonly verb.
1007
        
1008
        Clients should treat it as a "no" response, because is_readonly is only
1009
        advisory anyway (a transport could be read-write, but then the
1010
        underlying filesystem could be readonly anyway).
1011
        """
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
1012
        client = FakeClient()
1013
        client.add_unknown_method_response('Transport.is_readonly')
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.
1014
        transport = RemoteTransport('bzr://example.com/', medium=False,
1015
                                    _client=client)
1016
        self.assertEqual(False, transport.is_readonly())
1017
        self.assertEqual(
1018
            [('call', 'Transport.is_readonly', ())],
1019
            client._calls)
1020
2466.2.2 by Andrew Bennetts
Add tests for RemoteTransport.is_readonly in the style of the other remote object tests.
1021
3777.1.3 by Aaron Bentley
Use SSH default username from authentication.conf
1022
class TestRemoteSSHTransportAuthentication(tests.TestCaseInTempDir):
1023
1024
    def test_defaults_to_none(self):
1025
        t = RemoteSSHTransport('bzr+ssh://example.com')
1026
        self.assertIs(None, t._get_credentials()[0])
1027
1028
    def test_uses_authentication_config(self):
1029
        conf = config.AuthenticationConfig()
1030
        conf._get_config().update(
1031
            {'bzr+sshtest': {'scheme': 'ssh', 'user': 'bar', 'host':
1032
            'example.com'}})
1033
        conf._save()
1034
        t = RemoteSSHTransport('bzr+ssh://example.com')
1035
        self.assertEqual('bar', t._get_credentials()[0])
1036
1037
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
1038
class TestRemoteRepository(tests.TestCase):
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
1039
    """Base for testing RemoteRepository protocol usage.
1040
    
1041
    These tests contain frozen requests and responses.  We want any changes to 
1042
    what is sent or expected to be require a thoughtful update to these tests
1043
    because they might break compatibility with different-versioned servers.
1044
    """
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
1045
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
1046
    def setup_fake_client_and_repository(self, transport_path):
2018.18.7 by Martin Pool
(broken) Start addng client proxy test for Repository.tarball
1047
        """Create the fake client and repository for testing with.
1048
        
1049
        There's no real server here; we just have canned responses sent
1050
        back one by one.
1051
        
1052
        :param transport_path: Path below the root of the MemoryTransport
1053
            where the repository will be created.
1054
        """
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
1055
        transport = MemoryTransport()
1056
        transport.mkdir(transport_path)
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
1057
        client = FakeClient(transport.base)
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
1058
        transport = transport.clone(transport_path)
1059
        # we do not want bzrdir to make any remote calls
1060
        bzrdir = RemoteBzrDir(transport, _client=False)
1061
        repo = RemoteRepository(bzrdir, None, _client=client)
1062
        return repo, client
1063
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
1064
2018.12.2 by Andrew Bennetts
Remove some duplicate code in test_remote
1065
class TestRepositoryGatherStats(TestRemoteRepository):
2018.10.3 by v.ladeuil+lp at free
more tests for gather_stats
1066
1067
    def test_revid_none(self):
1068
        # ('ok',), body with revisions and size
1069
        transport_path = 'quack'
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
1070
        repo, client = self.setup_fake_client_and_repository(transport_path)
1071
        client.add_success_response_with_body(
1072
            'revisions: 2\nsize: 18\n', 'ok')
2018.10.3 by v.ladeuil+lp at free
more tests for gather_stats
1073
        result = repo.gather_stats(None)
1074
        self.assertEqual(
2018.5.153 by Andrew Bennetts
Rename call2 to call_expecting_body, and other small changes prompted by review.
1075
            [('call_expecting_body', 'Repository.gather_stats',
3104.4.2 by Andrew Bennetts
All tests passing.
1076
             ('quack/','','no'))],
2018.10.3 by v.ladeuil+lp at free
more tests for gather_stats
1077
            client._calls)
1078
        self.assertEqual({'revisions': 2, 'size': 18}, result)
1079
1080
    def test_revid_no_committers(self):
1081
        # ('ok',), body without committers
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
1082
        body = ('firstrev: 123456.300 3600\n'
1083
                'latestrev: 654231.400 0\n'
1084
                'revisions: 2\n'
1085
                'size: 18\n')
2018.10.3 by v.ladeuil+lp at free
more tests for gather_stats
1086
        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.
1087
        revid = u'\xc8'.encode('utf8')
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
1088
        repo, client = self.setup_fake_client_and_repository(transport_path)
1089
        client.add_success_response_with_body(body, 'ok')
2018.10.3 by v.ladeuil+lp at free
more tests for gather_stats
1090
        result = repo.gather_stats(revid)
1091
        self.assertEqual(
2018.5.153 by Andrew Bennetts
Rename call2 to call_expecting_body, and other small changes prompted by review.
1092
            [('call_expecting_body', 'Repository.gather_stats',
3104.4.2 by Andrew Bennetts
All tests passing.
1093
              ('quick/', revid, 'no'))],
2018.10.3 by v.ladeuil+lp at free
more tests for gather_stats
1094
            client._calls)
1095
        self.assertEqual({'revisions': 2, 'size': 18,
1096
                          'firstrev': (123456.300, 3600),
1097
                          'latestrev': (654231.400, 0),},
1098
                         result)
1099
1100
    def test_revid_with_committers(self):
1101
        # ('ok',), body with committers
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
1102
        body = ('committers: 128\n'
1103
                'firstrev: 123456.300 3600\n'
1104
                'latestrev: 654231.400 0\n'
1105
                'revisions: 2\n'
1106
                'size: 18\n')
2018.10.3 by v.ladeuil+lp at free
more tests for gather_stats
1107
        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.
1108
        revid = u'\xc8'.encode('utf8')
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
1109
        repo, client = self.setup_fake_client_and_repository(transport_path)
1110
        client.add_success_response_with_body(body, 'ok')
2018.10.3 by v.ladeuil+lp at free
more tests for gather_stats
1111
        result = repo.gather_stats(revid, True)
1112
        self.assertEqual(
2018.5.153 by Andrew Bennetts
Rename call2 to call_expecting_body, and other small changes prompted by review.
1113
            [('call_expecting_body', 'Repository.gather_stats',
3104.4.2 by Andrew Bennetts
All tests passing.
1114
              ('buick/', revid, 'yes'))],
2018.10.3 by v.ladeuil+lp at free
more tests for gather_stats
1115
            client._calls)
1116
        self.assertEqual({'revisions': 2, 'size': 18,
1117
                          'committers': 128,
1118
                          'firstrev': (123456.300, 3600),
1119
                          'latestrev': (654231.400, 0),},
1120
                         result)
1121
1122
3172.5.4 by Robert Collins
Implement get_parent_map for RemoteRepository with caching, based on get_revision_graph.
1123
class TestRepositoryGetGraph(TestRemoteRepository):
1124
1125
    def test_get_graph(self):
3172.5.8 by Robert Collins
Review feedback.
1126
        # get_graph returns a graph with the repository as the
1127
        # parents_provider.
3172.5.4 by Robert Collins
Implement get_parent_map for RemoteRepository with caching, based on get_revision_graph.
1128
        transport_path = 'quack'
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
1129
        repo, client = self.setup_fake_client_and_repository(transport_path)
3172.5.4 by Robert Collins
Implement get_parent_map for RemoteRepository with caching, based on get_revision_graph.
1130
        graph = repo.get_graph()
3441.5.24 by Andrew Bennetts
Remove RemoteGraph experiment.
1131
        self.assertEqual(graph._parents_provider, repo)
3172.5.4 by Robert Collins
Implement get_parent_map for RemoteRepository with caching, based on get_revision_graph.
1132
1133
1134
class TestRepositoryGetParentMap(TestRemoteRepository):
1135
1136
    def test_get_parent_map_caching(self):
1137
        # get_parent_map returns from cache until unlock()
1138
        # setup a reponse with two revisions
1139
        r1 = u'\u0e33'.encode('utf8')
1140
        r2 = u'\u0dab'.encode('utf8')
1141
        lines = [' '.join([r2, r1]), r1]
3211.5.2 by Robert Collins
Change RemoteRepository.get_parent_map to use bz2 not gzip for compression.
1142
        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.
1143
1144
        transport_path = 'quack'
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
1145
        repo, client = self.setup_fake_client_and_repository(transport_path)
1146
        client.add_success_response_with_body(encoded_body, 'ok')
1147
        client.add_success_response_with_body(encoded_body, 'ok')
3172.5.4 by Robert Collins
Implement get_parent_map for RemoteRepository with caching, based on get_revision_graph.
1148
        repo.lock_read()
1149
        graph = repo.get_graph()
1150
        parents = graph.get_parent_map([r2])
1151
        self.assertEqual({r2: (r1,)}, parents)
1152
        # locking and unlocking deeper should not reset
1153
        repo.lock_read()
1154
        repo.unlock()
1155
        parents = graph.get_parent_map([r1])
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
1156
        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.
1157
        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.
1158
            [('call_with_body_bytes_expecting_body',
1159
              '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.
1160
            client._calls)
1161
        repo.unlock()
1162
        # now we call again, and it should use the second response.
1163
        repo.lock_read()
1164
        graph = repo.get_graph()
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
1165
        parents = graph.get_parent_map([r1])
1166
        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.
1167
        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.
1168
            [('call_with_body_bytes_expecting_body',
1169
              'Repository.get_parent_map', ('quack/', r2), '\n\n0'),
1170
             ('call_with_body_bytes_expecting_body',
1171
              '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.
1172
            ],
1173
            client._calls)
1174
        repo.unlock()
1175
3213.1.2 by Andrew Bennetts
Add test for reconnection if get_parent_map is unknown by the server.
1176
    def test_get_parent_map_reconnects_if_unknown_method(self):
1177
        transport_path = 'quack'
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
1178
        repo, client = self.setup_fake_client_and_repository(transport_path)
1179
        client.add_unknown_method_response('Repository,get_parent_map')
1180
        client.add_success_response_with_body('', 'ok')
3453.4.10 by Andrew Bennetts
Change _is_remote_at_least to _is_remote_before.
1181
        self.assertFalse(client._medium._is_remote_before((1, 2)))
3213.1.2 by Andrew Bennetts
Add test for reconnection if get_parent_map is unknown by the server.
1182
        rev_id = 'revision-id'
3297.3.5 by Andrew Bennetts
Suppress a deprecation warning.
1183
        expected_deprecations = [
1184
            'bzrlib.remote.RemoteRepository.get_revision_graph was deprecated '
1185
            'in version 1.4.']
1186
        parents = self.callDeprecated(
1187
            expected_deprecations, repo.get_parent_map, [rev_id])
3213.1.2 by Andrew Bennetts
Add test for reconnection if get_parent_map is unknown by the server.
1188
        self.assertEqual(
3213.1.8 by Andrew Bennetts
Merge from bzr.dev.
1189
            [('call_with_body_bytes_expecting_body',
1190
              '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.
1191
             ('disconnect medium',),
1192
             ('call_expecting_body', 'Repository.get_revision_graph',
1193
              ('quack/', ''))],
1194
            client._calls)
3389.1.2 by Andrew Bennetts
Add test for the bug John found.
1195
        # The medium is now marked as being connected to an older server
3453.4.10 by Andrew Bennetts
Change _is_remote_at_least to _is_remote_before.
1196
        self.assertTrue(client._medium._is_remote_before((1, 2)))
3389.1.2 by Andrew Bennetts
Add test for the bug John found.
1197
1198
    def test_get_parent_map_fallback_parentless_node(self):
1199
        """get_parent_map falls back to get_revision_graph on old servers.  The
1200
        results from get_revision_graph are tweaked to match the get_parent_map
1201
        API.
1202
3389.1.3 by Andrew Bennetts
Remove XXX from test description.
1203
        Specifically, a {key: ()} result from get_revision_graph means "no
3389.1.2 by Andrew Bennetts
Add test for the bug John found.
1204
        parents" for that key, which in get_parent_map results should be
3389.1.3 by Andrew Bennetts
Remove XXX from test description.
1205
        represented as {key: ('null:',)}.
3389.1.2 by Andrew Bennetts
Add test for the bug John found.
1206
1207
        This is the test for https://bugs.launchpad.net/bzr/+bug/214894
1208
        """
1209
        rev_id = 'revision-id'
1210
        transport_path = 'quack'
3245.4.40 by Andrew Bennetts
Merge from bzr.dev.
1211
        repo, client = self.setup_fake_client_and_repository(transport_path)
1212
        client.add_success_response_with_body(rev_id, 'ok')
3453.4.9 by Andrew Bennetts
Rename _remote_is_not to _remember_remote_is_before.
1213
        client._medium._remember_remote_is_before((1, 2))
3389.1.2 by Andrew Bennetts
Add test for the bug John found.
1214
        expected_deprecations = [
1215
            'bzrlib.remote.RemoteRepository.get_revision_graph was deprecated '
1216
            'in version 1.4.']
1217
        parents = self.callDeprecated(
1218
            expected_deprecations, repo.get_parent_map, [rev_id])
1219
        self.assertEqual(
1220
            [('call_expecting_body', 'Repository.get_revision_graph',
1221
             ('quack/', ''))],
1222
            client._calls)
1223
        self.assertEqual({rev_id: ('null:',)}, parents)
3213.1.2 by Andrew Bennetts
Add test for reconnection if get_parent_map is unknown by the server.
1224
3297.2.3 by Andrew Bennetts
Test the code path that the typo is on.
1225
    def test_get_parent_map_unexpected_response(self):
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
1226
        repo, client = self.setup_fake_client_and_repository('path')
1227
        client.add_success_response('something unexpected!')
3297.2.3 by Andrew Bennetts
Test the code path that the typo is on.
1228
        self.assertRaises(
1229
            errors.UnexpectedSmartServerResponse,
1230
            repo.get_parent_map, ['a-revision-id'])
3213.1.2 by Andrew Bennetts
Add test for reconnection if get_parent_map is unknown by the server.
1231
3172.5.4 by Robert Collins
Implement get_parent_map for RemoteRepository with caching, based on get_revision_graph.
1232
2018.5.68 by Wouter van Heyst
Merge RemoteRepository.gather_stats.
1233
class TestRepositoryGetRevisionGraph(TestRemoteRepository):
1234
    
1235
    def test_null_revision(self):
1236
        # a null revision has the predictable result {}, we should have no wire
1237
        # traffic when calling it with this argument
1238
        transport_path = 'empty'
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
1239
        repo, client = self.setup_fake_client_and_repository(transport_path)
1240
        client.add_success_response('notused')
3287.6.4 by Robert Collins
Fix up deprecation warnings for get_revision_graph.
1241
        result = self.applyDeprecated(one_four, repo.get_revision_graph,
1242
            NULL_REVISION)
2018.5.68 by Wouter van Heyst
Merge RemoteRepository.gather_stats.
1243
        self.assertEqual([], client._calls)
1244
        self.assertEqual({}, result)
1245
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
1246
    def test_none_revision(self):
1247
        # 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.
1248
        r1 = u'\u0e33'.encode('utf8')
1249
        r2 = u'\u0dab'.encode('utf8')
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
1250
        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.
1251
        encoded_body = '\n'.join(lines)
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
1252
1253
        transport_path = 'sinhala'
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
1254
        repo, client = self.setup_fake_client_and_repository(transport_path)
1255
        client.add_success_response_with_body(encoded_body, 'ok')
3287.6.4 by Robert Collins
Fix up deprecation warnings for get_revision_graph.
1256
        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)
1257
        self.assertEqual(
2018.5.153 by Andrew Bennetts
Rename call2 to call_expecting_body, and other small changes prompted by review.
1258
            [('call_expecting_body', 'Repository.get_revision_graph',
3104.4.2 by Andrew Bennetts
All tests passing.
1259
             ('sinhala/', ''))],
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
1260
            client._calls)
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
1261
        self.assertEqual({r1: (), r2: (r1, )}, result)
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
1262
1263
    def test_specific_revision(self):
1264
        # with a specific revision we want the graph for that
1265
        # 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.
1266
        r11 = u'\u0e33'.encode('utf8')
1267
        r12 = u'\xc9'.encode('utf8')
1268
        r2 = u'\u0dab'.encode('utf8')
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
1269
        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.
1270
        encoded_body = '\n'.join(lines)
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
1271
1272
        transport_path = 'sinhala'
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
1273
        repo, client = self.setup_fake_client_and_repository(transport_path)
1274
        client.add_success_response_with_body(encoded_body, 'ok')
3287.6.4 by Robert Collins
Fix up deprecation warnings for get_revision_graph.
1275
        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)
1276
        self.assertEqual(
2018.5.153 by Andrew Bennetts
Rename call2 to call_expecting_body, and other small changes prompted by review.
1277
            [('call_expecting_body', 'Repository.get_revision_graph',
3104.4.2 by Andrew Bennetts
All tests passing.
1278
             ('sinhala/', r2))],
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
1279
            client._calls)
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
1280
        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)
1281
1282
    def test_no_such_revision(self):
1283
        revid = '123'
1284
        transport_path = 'sinhala'
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
1285
        repo, client = self.setup_fake_client_and_repository(transport_path)
1286
        client.add_error_response('nosuchrevision', revid)
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
1287
        # 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
1288
        self.assertRaises(errors.NoSuchRevision,
3287.6.4 by Robert Collins
Fix up deprecation warnings for get_revision_graph.
1289
            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)
1290
        self.assertEqual(
2018.5.153 by Andrew Bennetts
Rename call2 to call_expecting_body, and other small changes prompted by review.
1291
            [('call_expecting_body', 'Repository.get_revision_graph',
3104.4.2 by Andrew Bennetts
All tests passing.
1292
             ('sinhala/', revid))],
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
1293
            client._calls)
1294
3245.4.53 by Andrew Bennetts
Add some missing 'raise' statements to test_remote.
1295
    def test_unexpected_error(self):
1296
        revid = '123'
1297
        transport_path = 'sinhala'
1298
        repo, client = self.setup_fake_client_and_repository(transport_path)
1299
        client.add_error_response('AnUnexpectedError')
3690.1.2 by Andrew Bennetts
Rename UntranslateableErrorFromSmartServer -> UnknownErrorFromSmartServer.
1300
        e = self.assertRaises(errors.UnknownErrorFromSmartServer,
3245.4.53 by Andrew Bennetts
Add some missing 'raise' statements to test_remote.
1301
            self.applyDeprecated, one_four, repo.get_revision_graph, revid)
1302
        self.assertEqual(('AnUnexpectedError',), e.error_tuple)
1303
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
1304
        
1305
class TestRepositoryIsShared(TestRemoteRepository):
1306
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
1307
    def test_is_shared(self):
1308
        # ('yes', ) for Repository.is_shared -> 'True'.
1309
        transport_path = 'quack'
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
1310
        repo, client = self.setup_fake_client_and_repository(transport_path)
1311
        client.add_success_response('yes')
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
1312
        result = repo.is_shared()
1313
        self.assertEqual(
3104.4.2 by Andrew Bennetts
All tests passing.
1314
            [('call', 'Repository.is_shared', ('quack/',))],
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
1315
            client._calls)
1316
        self.assertEqual(True, result)
1317
1318
    def test_is_not_shared(self):
1319
        # ('no', ) for Repository.is_shared -> 'False'.
1320
        transport_path = 'qwack'
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
1321
        repo, client = self.setup_fake_client_and_repository(transport_path)
1322
        client.add_success_response('no')
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
1323
        result = repo.is_shared()
1324
        self.assertEqual(
3104.4.2 by Andrew Bennetts
All tests passing.
1325
            [('call', 'Repository.is_shared', ('qwack/',))],
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
1326
            client._calls)
1327
        self.assertEqual(False, result)
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
1328
1329
1330
class TestRepositoryLockWrite(TestRemoteRepository):
1331
1332
    def test_lock_write(self):
1333
        transport_path = 'quack'
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
1334
        repo, client = self.setup_fake_client_and_repository(transport_path)
1335
        client.add_success_response('ok', 'a token')
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
1336
        result = repo.lock_write()
1337
        self.assertEqual(
3104.4.2 by Andrew Bennetts
All tests passing.
1338
            [('call', 'Repository.lock_write', ('quack/', ''))],
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
1339
            client._calls)
1340
        self.assertEqual('a token', result)
1341
1342
    def test_lock_write_already_locked(self):
1343
        transport_path = 'quack'
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
1344
        repo, client = self.setup_fake_client_and_repository(transport_path)
1345
        client.add_error_response('LockContention')
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
1346
        self.assertRaises(errors.LockContention, repo.lock_write)
1347
        self.assertEqual(
3104.4.2 by Andrew Bennetts
All tests passing.
1348
            [('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.
1349
            client._calls)
1350
1351
    def test_lock_write_unlockable(self):
1352
        transport_path = 'quack'
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
1353
        repo, client = self.setup_fake_client_and_repository(transport_path)
1354
        client.add_error_response('UnlockableTransport')
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.
1355
        self.assertRaises(errors.UnlockableTransport, repo.lock_write)
1356
        self.assertEqual(
3104.4.2 by Andrew Bennetts
All tests passing.
1357
            [('call', 'Repository.lock_write', ('quack/', ''))],
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
1358
            client._calls)
1359
1360
1361
class TestRepositoryUnlock(TestRemoteRepository):
1362
1363
    def test_unlock(self):
1364
        transport_path = 'quack'
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
1365
        repo, client = self.setup_fake_client_and_repository(transport_path)
1366
        client.add_success_response('ok', 'a token')
1367
        client.add_success_response('ok')
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
1368
        repo.lock_write()
1369
        repo.unlock()
1370
        self.assertEqual(
3104.4.2 by Andrew Bennetts
All tests passing.
1371
            [('call', 'Repository.lock_write', ('quack/', '')),
1372
             ('call', 'Repository.unlock', ('quack/', 'a token'))],
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
1373
            client._calls)
1374
1375
    def test_unlock_wrong_token(self):
1376
        # If somehow the token is wrong, unlock will raise TokenMismatch.
1377
        transport_path = 'quack'
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
1378
        repo, client = self.setup_fake_client_and_repository(transport_path)
1379
        client.add_success_response('ok', 'a token')
1380
        client.add_error_response('TokenMismatch')
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
1381
        repo.lock_write()
1382
        self.assertRaises(errors.TokenMismatch, repo.unlock)
1383
1384
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
1385
class TestRepositoryHasRevision(TestRemoteRepository):
1386
1387
    def test_none(self):
1388
        # repo.has_revision(None) should not cause any traffic.
1389
        transport_path = 'quack'
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
1390
        repo, client = self.setup_fake_client_and_repository(transport_path)
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
1391
1392
        # The null revision is always there, so has_revision(None) == True.
3172.3.3 by Robert Collins
Missed one occurence of None -> NULL_REVISION.
1393
        self.assertEqual(True, repo.has_revision(NULL_REVISION))
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
1394
1395
        # The remote repo shouldn't be accessed.
1396
        self.assertEqual([], client._calls)
2018.18.7 by Martin Pool
(broken) Start addng client proxy test for Repository.tarball
1397
1398
1399
class TestRepositoryTarball(TestRemoteRepository):
1400
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
1401
    # This is a canned tarball reponse we can validate against
2018.18.18 by Martin Pool
reformat
1402
    tarball_content = (
2018.18.23 by Martin Pool
review cleanups
1403
        'QlpoOTFBWSZTWdGkj3wAAWF/k8aQACBIB//A9+8cIX/v33AACEAYABAECEACNz'
1404
        'JqsgJJFPTSnk1A3qh6mTQAAAANPUHkagkSTEkaA09QaNAAAGgAAAcwCYCZGAEY'
1405
        'mJhMJghpiaYBUkKammSHqNMZQ0NABkNAeo0AGneAevnlwQoGzEzNVzaYxp/1Uk'
1406
        'xXzA1CQX0BJMZZLcPBrluJir5SQyijWHYZ6ZUtVqqlYDdB2QoCwa9GyWwGYDMA'
1407
        'OQYhkpLt/OKFnnlT8E0PmO8+ZNSo2WWqeCzGB5fBXZ3IvV7uNJVE7DYnWj6qwB'
1408
        'k5DJDIrQ5OQHHIjkS9KqwG3mc3t+F1+iujb89ufyBNIKCgeZBWrl5cXxbMGoMs'
1409
        'c9JuUkg5YsiVcaZJurc6KLi6yKOkgCUOlIlOpOoXyrTJjK8ZgbklReDdwGmFgt'
1410
        'dkVsAIslSVCd4AtACSLbyhLHryfb14PKegrVDba+U8OL6KQtzdM5HLjAc8/p6n'
1411
        '0lgaWU8skgO7xupPTkyuwheSckejFLK5T4ZOo0Gda9viaIhpD1Qn7JqqlKAJqC'
1412
        'QplPKp2nqBWAfwBGaOwVrz3y1T+UZZNismXHsb2Jq18T+VaD9k4P8DqE3g70qV'
1413
        'JLurpnDI6VS5oqDDPVbtVjMxMxMg4rzQVipn2Bv1fVNK0iq3Gl0hhnnHKm/egy'
1414
        'nWQ7QH/F3JFOFCQ0aSPfA='
1415
        ).decode('base64')
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
1416
2018.18.7 by Martin Pool
(broken) Start addng client proxy test for Repository.tarball
1417
    def test_repository_tarball(self):
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
1418
        # Test that Repository.tarball generates the right operations
2018.18.7 by Martin Pool
(broken) Start addng client proxy test for Repository.tarball
1419
        transport_path = 'repo'
2018.18.14 by Martin Pool
merge hpss again; restore incorrectly removed RemoteRepository.break_lock
1420
        expected_calls = [('call_expecting_body', 'Repository.tarball',
3104.4.2 by Andrew Bennetts
All tests passing.
1421
                           ('repo/', 'bz2',),),
2018.18.7 by Martin Pool
(broken) Start addng client proxy test for Repository.tarball
1422
            ]
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
1423
        repo, client = self.setup_fake_client_and_repository(transport_path)
1424
        client.add_success_response_with_body(self.tarball_content, 'ok')
2018.18.7 by Martin Pool
(broken) Start addng client proxy test for Repository.tarball
1425
        # Now actually ask for the tarball
3245.4.24 by Andrew Bennetts
Consistently raise errors from the server as ErrorFromSmartServer exceptions.
1426
        tarball_file = repo._get_tarball('bz2')
2018.18.25 by Martin Pool
Repository.tarball fixes for python2.4
1427
        try:
1428
            self.assertEqual(expected_calls, client._calls)
1429
            self.assertEqual(self.tarball_content, tarball_file.read())
1430
        finally:
1431
            tarball_file.close()
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
1432
1433
1434
class TestRemoteRepositoryCopyContent(tests.TestCaseWithTransport):
1435
    """RemoteRepository.copy_content_into optimizations"""
1436
2018.18.10 by Martin Pool
copy_content_into from Remote repositories by using temporary directories on both ends.
1437
    def test_copy_content_remote_to_local(self):
1438
        self.transport_server = server.SmartTCPServer_for_testing
1439
        src_repo = self.make_repository('repo1')
1440
        src_repo = repository.Repository.open(self.get_url('repo1'))
1441
        # At the moment the tarball-based copy_content_into can't write back
1442
        # into a smart server.  It would be good if it could upload the
1443
        # tarball; once that works we'd have to create repositories of
1444
        # different formats. -- mbp 20070410
1445
        dest_url = self.get_vfs_only_url('repo2')
1446
        dest_bzrdir = BzrDir.create(dest_url)
1447
        dest_repo = dest_bzrdir.create_repository()
1448
        self.assertFalse(isinstance(dest_repo, RemoteRepository))
1449
        self.assertTrue(isinstance(src_repo, RemoteRepository))
1450
        src_repo.copy_content_into(dest_repo)
3533.3.3 by Andrew Bennetts
Add unit tests for bzrlib.remote._translate_error.
1451
1452
3533.3.4 by Andrew Bennetts
Add tests for _translate_error's robustness.
1453
class TestErrorTranslationBase(tests.TestCaseWithMemoryTransport):
1454
    """Base class for unit tests for bzrlib.remote._translate_error."""
1455
1456
    def translateTuple(self, error_tuple, **context):
1457
        """Call _translate_error with an ErrorFromSmartServer built from the
1458
        given error_tuple.
1459
1460
        :param error_tuple: A tuple of a smart server response, as would be
1461
            passed to an ErrorFromSmartServer.
1462
        :kwargs context: context items to call _translate_error with.
1463
1464
        :returns: The error raised by _translate_error.
1465
        """
1466
        # Raise the ErrorFromSmartServer before passing it as an argument,
1467
        # because _translate_error may need to re-raise it with a bare 'raise'
1468
        # statement.
1469
        server_error = errors.ErrorFromSmartServer(error_tuple)
1470
        translated_error = self.translateErrorFromSmartServer(
1471
            server_error, **context)
1472
        return translated_error
1473
1474
    def translateErrorFromSmartServer(self, error_object, **context):
1475
        """Like translateTuple, but takes an already constructed
1476
        ErrorFromSmartServer rather than a tuple.
1477
        """
1478
        try:
1479
            raise error_object
1480
        except errors.ErrorFromSmartServer, server_error:
1481
            translated_error = self.assertRaises(
1482
                errors.BzrError, remote._translate_error, server_error,
1483
                **context)
1484
        return translated_error
1485
1486
    
1487
class TestErrorTranslationSuccess(TestErrorTranslationBase):
3533.3.3 by Andrew Bennetts
Add unit tests for bzrlib.remote._translate_error.
1488
    """Unit tests for bzrlib.remote._translate_error.
1489
    
1490
    Given an ErrorFromSmartServer (which has an error tuple from a smart
1491
    server) and some context, _translate_error raises more specific errors from
1492
    bzrlib.errors.
3533.3.4 by Andrew Bennetts
Add tests for _translate_error's robustness.
1493
1494
    This test case covers the cases where _translate_error succeeds in
1495
    translating an ErrorFromSmartServer to something better.  See
1496
    TestErrorTranslationRobustness for other cases.
3533.3.3 by Andrew Bennetts
Add unit tests for bzrlib.remote._translate_error.
1497
    """
1498
1499
    def test_NoSuchRevision(self):
1500
        branch = self.make_branch('')
1501
        revid = 'revid'
3533.3.4 by Andrew Bennetts
Add tests for _translate_error's robustness.
1502
        translated_error = self.translateTuple(
3533.3.3 by Andrew Bennetts
Add unit tests for bzrlib.remote._translate_error.
1503
            ('NoSuchRevision', revid), branch=branch)
1504
        expected_error = errors.NoSuchRevision(branch, revid)
1505
        self.assertEqual(expected_error, translated_error)
1506
1507
    def test_nosuchrevision(self):
1508
        repository = self.make_repository('')
1509
        revid = 'revid'
3533.3.4 by Andrew Bennetts
Add tests for _translate_error's robustness.
1510
        translated_error = self.translateTuple(
3533.3.3 by Andrew Bennetts
Add unit tests for bzrlib.remote._translate_error.
1511
            ('nosuchrevision', revid), repository=repository)
1512
        expected_error = errors.NoSuchRevision(repository, revid)
1513
        self.assertEqual(expected_error, translated_error)
1514
1515
    def test_nobranch(self):
1516
        bzrdir = self.make_bzrdir('')
3533.3.4 by Andrew Bennetts
Add tests for _translate_error's robustness.
1517
        translated_error = self.translateTuple(('nobranch',), bzrdir=bzrdir)
3533.3.3 by Andrew Bennetts
Add unit tests for bzrlib.remote._translate_error.
1518
        expected_error = errors.NotBranchError(path=bzrdir.root_transport.base)
1519
        self.assertEqual(expected_error, translated_error)
1520
1521
    def test_LockContention(self):
3533.3.4 by Andrew Bennetts
Add tests for _translate_error's robustness.
1522
        translated_error = self.translateTuple(('LockContention',))
3533.3.3 by Andrew Bennetts
Add unit tests for bzrlib.remote._translate_error.
1523
        expected_error = errors.LockContention('(remote lock)')
1524
        self.assertEqual(expected_error, translated_error)
1525
1526
    def test_UnlockableTransport(self):
1527
        bzrdir = self.make_bzrdir('')
3533.3.4 by Andrew Bennetts
Add tests for _translate_error's robustness.
1528
        translated_error = self.translateTuple(
3533.3.3 by Andrew Bennetts
Add unit tests for bzrlib.remote._translate_error.
1529
            ('UnlockableTransport',), bzrdir=bzrdir)
1530
        expected_error = errors.UnlockableTransport(bzrdir.root_transport)
1531
        self.assertEqual(expected_error, translated_error)
1532
1533
    def test_LockFailed(self):
1534
        lock = 'str() of a server lock'
1535
        why = 'str() of why'
3533.3.4 by Andrew Bennetts
Add tests for _translate_error's robustness.
1536
        translated_error = self.translateTuple(('LockFailed', lock, why))
3533.3.3 by Andrew Bennetts
Add unit tests for bzrlib.remote._translate_error.
1537
        expected_error = errors.LockFailed(lock, why)
1538
        self.assertEqual(expected_error, translated_error)
1539
1540
    def test_TokenMismatch(self):
1541
        token = 'a lock token'
3533.3.4 by Andrew Bennetts
Add tests for _translate_error's robustness.
1542
        translated_error = self.translateTuple(('TokenMismatch',), token=token)
3533.3.3 by Andrew Bennetts
Add unit tests for bzrlib.remote._translate_error.
1543
        expected_error = errors.TokenMismatch(token, '(remote token)')
1544
        self.assertEqual(expected_error, translated_error)
1545
1546
    def test_Diverged(self):
1547
        branch = self.make_branch('a')
1548
        other_branch = self.make_branch('b')
3533.3.4 by Andrew Bennetts
Add tests for _translate_error's robustness.
1549
        translated_error = self.translateTuple(
3533.3.3 by Andrew Bennetts
Add unit tests for bzrlib.remote._translate_error.
1550
            ('Diverged',), branch=branch, other_branch=other_branch)
1551
        expected_error = errors.DivergedBranches(branch, other_branch)
1552
        self.assertEqual(expected_error, translated_error)
1553
1554
3533.3.4 by Andrew Bennetts
Add tests for _translate_error's robustness.
1555
class TestErrorTranslationRobustness(TestErrorTranslationBase):
1556
    """Unit tests for bzrlib.remote._translate_error's robustness.
1557
    
1558
    TestErrorTranslationSuccess is for cases where _translate_error can
1559
    translate successfully.  This class about how _translate_err behaves when
1560
    it fails to translate: it re-raises the original error.
1561
    """
1562
1563
    def test_unrecognised_server_error(self):
1564
        """If the error code from the server is not recognised, the original
1565
        ErrorFromSmartServer is propagated unmodified.
1566
        """
1567
        error_tuple = ('An unknown error tuple',)
3690.1.2 by Andrew Bennetts
Rename UntranslateableErrorFromSmartServer -> UnknownErrorFromSmartServer.
1568
        server_error = errors.ErrorFromSmartServer(error_tuple)
1569
        translated_error = self.translateErrorFromSmartServer(server_error)
1570
        expected_error = errors.UnknownErrorFromSmartServer(server_error)
3690.1.1 by Andrew Bennetts
Unexpected error responses from a smart server no longer cause the client to traceback.
1571
        self.assertEqual(expected_error, translated_error)
3533.3.4 by Andrew Bennetts
Add tests for _translate_error's robustness.
1572
1573
    def test_context_missing_a_key(self):
1574
        """In case of a bug in the client, or perhaps an unexpected response
1575
        from a server, _translate_error returns the original error tuple from
1576
        the server and mutters a warning.
1577
        """
1578
        # To translate a NoSuchRevision error _translate_error needs a 'branch'
1579
        # in the context dict.  So let's give it an empty context dict instead
1580
        # to exercise its error recovery.
1581
        empty_context = {}
1582
        error_tuple = ('NoSuchRevision', 'revid')
1583
        server_error = errors.ErrorFromSmartServer(error_tuple)
1584
        translated_error = self.translateErrorFromSmartServer(server_error)
1585
        self.assertEqual(server_error, translated_error)
1586
        # In addition to re-raising ErrorFromSmartServer, some debug info has
1587
        # been muttered to the log file for developer to look at.
1588
        self.assertContainsRe(
1589
            self._get_log(keep_log_file=True),
1590
            "Missing key 'branch' in context")
1591
        
3691.2.2 by Martin Pool
Fix some problems in access to stacked repositories over hpss (#261315)
1592
1593
class TestStacking(tests.TestCaseWithTransport):
1594
    """Tests for operations on stacked remote repositories.
1595
    
1596
    The underlying format type must support stacking.
1597
    """
1598
1599
    def test_access_stacked_remote(self):
1600
        # based on <http://launchpad.net/bugs/261315>
1601
        # make a branch stacked on another repository containing an empty
1602
        # revision, then open it over hpss - we should be able to see that
1603
        # revision.
1604
        base_transport = self.get_transport()
1605
        base_builder = self.make_branch_builder('base', format='1.6')
1606
        base_builder.start_series()
1607
        base_revid = base_builder.build_snapshot('rev-id', None,
1608
            [('add', ('', None, 'directory', None))],
1609
            'message')
1610
        base_builder.finish_series()
1611
        stacked_branch = self.make_branch('stacked', format='1.6')
1612
        stacked_branch.set_stacked_on_url('../base')
1613
        # start a server looking at this
1614
        smart_server = server.SmartTCPServer_for_testing()
1615
        smart_server.setUp()
1616
        self.addCleanup(smart_server.tearDown)
1617
        remote_bzrdir = BzrDir.open(smart_server.get_url() + '/stacked')
1618
        # can get its branch and repository
1619
        remote_branch = remote_bzrdir.open_branch()
1620
        remote_repo = remote_branch.repository
3691.2.6 by Martin Pool
Disable RemoteBranch stacking, but get get_stacked_on_url working, and passing back exceptions
1621
        remote_repo.lock_read()
1622
        try:
1623
            # it should have an appropriate fallback repository, which should also
1624
            # be a RemoteRepository
1625
            self.assertEquals(len(remote_repo._fallback_repositories), 1)
1626
            self.assertIsInstance(remote_repo._fallback_repositories[0],
1627
                RemoteRepository)
1628
            # and it has the revision committed to the underlying repository;
1629
            # these have varying implementations so we try several of them
1630
            self.assertTrue(remote_repo.has_revisions([base_revid]))
1631
            self.assertTrue(remote_repo.has_revision(base_revid))
1632
            self.assertEqual(remote_repo.get_revision(base_revid).message,
1633
                'message')
1634
        finally:
1635
            remote_repo.unlock()