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