/brz/remove-bazaar

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