/brz/remove-bazaar

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