/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
    bzrdir,
30
    errors,
31
    remote,
32
    tests,
33
    )
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
34
from bzrlib.branch import Branch
35
from bzrlib.bzrdir import BzrDir, BzrDirFormat
36
from bzrlib.remote import (
37
    RemoteBranch,
38
    RemoteBzrDir,
39
    RemoteBzrDirFormat,
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
40
    RemoteRepository,
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
41
    )
42
from bzrlib.revision import NULL_REVISION
43
from bzrlib.smart import server
44
from bzrlib.smart.client import SmartClient
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)
45
from bzrlib.transport import remote as remote_transport
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
46
from bzrlib.transport.memory import MemoryTransport
1752.2.30 by Martin Pool
Start adding a RemoteBzrDir, etc
47
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)
48
2018.5.42 by Robert Collins
Various hopefully improvements, but wsgi is broken, handing over to spiv :).
49
class BasicRemoteObjectTests(tests.TestCaseWithTransport):
1752.2.30 by Martin Pool
Start adding a RemoteBzrDir, etc
50
51
    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.
52
        self.transport_server = server.SmartTCPServer_for_testing
2018.5.42 by Robert Collins
Various hopefully improvements, but wsgi is broken, handing over to spiv :).
53
        super(BasicRemoteObjectTests, self).setUp()
54
        self.transport = self.get_transport()
1752.2.30 by Martin Pool
Start adding a RemoteBzrDir, etc
55
        self.client = self.transport.get_smart_client()
56
        # 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
57
        self.local_wt = BzrDir.create_standalone_workingtree('.')
1752.2.30 by Martin Pool
Start adding a RemoteBzrDir, etc
58
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.
59
    def test_is_readonly(self):
60
        # XXX: this is a poor way to test RemoteTransport, but currently there's
61
        # no easy way to substitute in a fake client on a transport like we can
62
        # with RemoteBzrDir/Branch/Repository.
63
        self.assertEqual(self.transport.is_readonly(), False)
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()
73
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
74
    def test_remote_repository(self):
75
        b = BzrDir.open_from_transport(self.transport)
76
        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.
77
        revid = u'\xc823123123'.encode('utf8')
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
78
        self.assertFalse(repo.has_revision(revid))
79
        self.local_wt.commit(message='test commit', rev_id=revid)
80
        self.assertTrue(repo.has_revision(revid))
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
81
82
    def test_remote_branch_revision_history(self):
83
        b = BzrDir.open_from_transport(self.transport).open_branch()
2018.5.38 by Robert Collins
Implement RemoteBranch.revision_history().
84
        self.assertEqual([], b.revision_history())
85
        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.
86
        r2 = self.local_wt.commit('1st commit', rev_id=u'\xc8'.encode('utf8'))
2018.5.38 by Robert Collins
Implement RemoteBranch.revision_history().
87
        self.assertEqual([r1, r2], b.revision_history())
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
88
1752.2.30 by Martin Pool
Start adding a RemoteBzrDir, etc
89
    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)
90
        """Should open a RemoteBzrDir over a RemoteTransport"""
1752.2.30 by Martin Pool
Start adding a RemoteBzrDir, etc
91
        fmt = BzrDirFormat.find_format(self.transport)
2018.5.25 by Andrew Bennetts
Make sure RemoteBzrDirFormat is always registered (John Arbash Meinel, Robert Collins, Andrew Bennetts).
92
        self.assertTrue(RemoteBzrDirFormat in BzrDirFormat._control_formats)
1752.2.30 by Martin Pool
Start adding a RemoteBzrDir, etc
93
        self.assertIsInstance(fmt, remote.RemoteBzrDirFormat)
94
95
    def test_open_detected_smart_format(self):
96
        fmt = BzrDirFormat.find_format(self.transport)
97
        d = fmt.open(self.transport)
98
        self.assertIsInstance(d, BzrDir)
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
99
100
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.
101
class ReadonlyRemoteTransportTests(tests.TestCaseWithTransport):
102
103
    def setUp(self):
104
        self.transport_server = server.ReadonlySmartTCPServer_for_testing
105
        super(ReadonlyRemoteTransportTests, self).setUp()
106
107
    def test_is_readonly_yes(self):
108
        # XXX: this is a poor way to test RemoteTransport, but currently there's
109
        # no easy way to substitute in a fake client on a transport like we can
110
        # with RemoteBzrDir/Branch/Repository.
111
        transport = self.get_readonly_transport()
112
        self.assertEqual(transport.is_readonly(), True)
113
114
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
115
class FakeProtocol(object):
116
    """Lookalike SmartClientRequestProtocolOne allowing body reading tests."""
117
118
    def __init__(self, body):
119
        self._body_buffer = StringIO(body)
120
121
    def read_body_bytes(self, count=-1):
122
        return self._body_buffer.read(count)
123
124
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
125
class FakeClient(SmartClient):
126
    """Lookalike for SmartClient allowing testing."""
127
    
128
    def __init__(self, responses):
129
        # We don't call the super init because there is no medium.
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
130
        """create a FakeClient.
131
132
        :param respones: A list of response-tuple, body-data pairs to be sent
133
            back to callers.
134
        """
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
135
        self.responses = responses
136
        self._calls = []
137
138
    def call(self, method, *args):
139
        self._calls.append(('call', method, args))
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
140
        return self.responses.pop(0)[0]
141
142
    def call2(self, method, *args):
143
        self._calls.append(('call2', method, args))
144
        result = self.responses.pop(0)
145
        return result[0], FakeProtocol(result[1])
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
146
147
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
148
class TestBzrDirOpenBranch(tests.TestCase):
149
150
    def test_branch_present(self):
2018.5.118 by Robert Collins
Fix RemoteRepositoryFormat to have appropriate rich_root_data and support_tree_reference.
151
        client = FakeClient([(('ok', ''), ), (('ok', '', 'False', 'False'), )])
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
152
        transport = MemoryTransport()
153
        transport.mkdir('quack')
154
        transport = transport.clone('quack')
155
        bzrdir = RemoteBzrDir(transport, _client=client)
156
        result = bzrdir.open_branch()
157
        self.assertEqual(
158
            [('call', 'BzrDir.open_branch', ('///quack/',)),
159
             ('call', 'BzrDir.find_repository', ('///quack/',))],
160
            client._calls)
161
        self.assertIsInstance(result, RemoteBranch)
162
        self.assertEqual(bzrdir, result.bzrdir)
163
164
    def test_branch_missing(self):
165
        client = FakeClient([(('nobranch',), )])
166
        transport = MemoryTransport()
167
        transport.mkdir('quack')
168
        transport = transport.clone('quack')
169
        bzrdir = RemoteBzrDir(transport, _client=client)
170
        self.assertRaises(errors.NotBranchError, bzrdir.open_branch)
171
        self.assertEqual(
172
            [('call', 'BzrDir.open_branch', ('///quack/',))],
173
            client._calls)
174
2018.5.118 by Robert Collins
Fix RemoteRepositoryFormat to have appropriate rich_root_data and support_tree_reference.
175
    def check_open_repository(self, rich_root, subtrees):
176
        if rich_root:
177
            rich_response = 'True'
178
        else:
179
            rich_response = 'False'
180
        if subtrees:
181
            subtree_response = 'True'
182
        else:
183
            subtree_response = 'False'
184
        client = FakeClient([(('ok', '', rich_response, subtree_response), ),])
185
        transport = MemoryTransport()
186
        transport.mkdir('quack')
187
        transport = transport.clone('quack')
188
        bzrdir = RemoteBzrDir(transport, _client=client)
189
        result = bzrdir.open_repository()
190
        self.assertEqual(
191
            [('call', 'BzrDir.find_repository', ('///quack/',))],
192
            client._calls)
193
        self.assertIsInstance(result, RemoteRepository)
194
        self.assertEqual(bzrdir, result.bzrdir)
195
        self.assertEqual(rich_root, result._format.rich_root_data)
196
        self.assertEqual(subtrees, result._format.support_tree_reference)
197
198
    def test_open_repository_sets_format_attributes(self):
199
        self.check_open_repository(True, True)
200
        self.check_open_repository(False, True)
201
        self.check_open_repository(True, False)
202
        self.check_open_repository(False, False)
203
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
204
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
205
class TestBranchLastRevisionInfo(tests.TestCase):
206
207
    def test_empty_branch(self):
208
        # in an empty branch we decode the response properly
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
209
        client = FakeClient([(('ok', '0', ''), )])
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
210
        transport = MemoryTransport()
211
        transport.mkdir('quack')
212
        transport = transport.clone('quack')
213
        # we do not want bzrdir to make any remote calls
214
        bzrdir = RemoteBzrDir(transport, _client=False)
215
        branch = RemoteBranch(bzrdir, None, _client=client)
216
        result = branch.last_revision_info()
217
218
        self.assertEqual(
219
            [('call', 'Branch.last_revision_info', ('///quack/',))],
220
            client._calls)
221
        self.assertEqual((0, NULL_REVISION), result)
222
223
    def test_non_empty_branch(self):
224
        # 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.
225
        revid = u'\xc8'.encode('utf8')
226
        client = FakeClient([(('ok', '2', revid), )])
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
227
        transport = MemoryTransport()
228
        transport.mkdir('kwaak')
229
        transport = transport.clone('kwaak')
230
        # we do not want bzrdir to make any remote calls
231
        bzrdir = RemoteBzrDir(transport, _client=False)
232
        branch = RemoteBranch(bzrdir, None, _client=client)
233
        result = branch.last_revision_info()
234
235
        self.assertEqual(
236
            [('call', 'Branch.last_revision_info', ('///kwaak/',))],
237
            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.
238
        self.assertEqual((2, revid), result)
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
239
240
2018.12.3 by Andrew Bennetts
Add a Branch.set_last_revision smart method, and make RemoteBranch.set_revision_history use it.
241
class TestBranchSetLastRevision(tests.TestCase):
242
243
    def test_set_empty(self):
244
        # set_revision_history([]) is translated to calling
245
        # Branch.set_last_revision(path, '') on the wire.
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
246
        client = FakeClient([
247
            # lock_write
248
            (('ok', 'branch token', 'repo token'), ),
249
            # set_last_revision
250
            (('ok',), ),
251
            # unlock
252
            (('ok',), )])
2018.12.3 by Andrew Bennetts
Add a Branch.set_last_revision smart method, and make RemoteBranch.set_revision_history use it.
253
        transport = MemoryTransport()
254
        transport.mkdir('branch')
255
        transport = transport.clone('branch')
256
257
        bzrdir = RemoteBzrDir(transport, _client=False)
258
        branch = RemoteBranch(bzrdir, None, _client=client)
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
259
        # This is a hack to work around the problem that RemoteBranch currently
260
        # unnecessarily invokes _ensure_real upon a call to lock_write.
261
        branch._ensure_real = lambda: None
262
        branch.lock_write()
263
        client._calls = []
2018.12.3 by Andrew Bennetts
Add a Branch.set_last_revision smart method, and make RemoteBranch.set_revision_history use it.
264
        result = branch.set_revision_history([])
265
        self.assertEqual(
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
266
            [('call', 'Branch.set_last_revision',
267
                ('///branch/', 'branch token', 'repo token', ''))],
2018.12.3 by Andrew Bennetts
Add a Branch.set_last_revision smart method, and make RemoteBranch.set_revision_history use it.
268
            client._calls)
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
269
        branch.unlock()
2018.12.3 by Andrew Bennetts
Add a Branch.set_last_revision smart method, and make RemoteBranch.set_revision_history use it.
270
        self.assertEqual(None, result)
271
272
    def test_set_nonempty(self):
273
        # set_revision_history([rev-id1, ..., rev-idN]) is translated to calling
274
        # Branch.set_last_revision(path, rev-idN) on the wire.
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
275
        client = FakeClient([
276
            # lock_write
277
            (('ok', 'branch token', 'repo token'), ),
278
            # set_last_revision
279
            (('ok',), ),
280
            # unlock
281
            (('ok',), )])
2018.12.3 by Andrew Bennetts
Add a Branch.set_last_revision smart method, and make RemoteBranch.set_revision_history use it.
282
        transport = MemoryTransport()
283
        transport.mkdir('branch')
284
        transport = transport.clone('branch')
285
286
        bzrdir = RemoteBzrDir(transport, _client=False)
287
        branch = RemoteBranch(bzrdir, None, _client=client)
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
288
        # This is a hack to work around the problem that RemoteBranch currently
289
        # unnecessarily invokes _ensure_real upon a call to lock_write.
290
        branch._ensure_real = lambda: None
291
        # Lock the branch, reset the record of remote calls.
292
        branch.lock_write()
293
        client._calls = []
2018.12.3 by Andrew Bennetts
Add a Branch.set_last_revision smart method, and make RemoteBranch.set_revision_history use it.
294
295
        result = branch.set_revision_history(['rev-id1', 'rev-id2'])
296
        self.assertEqual(
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
297
            [('call', 'Branch.set_last_revision',
298
                ('///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.
299
            client._calls)
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
300
        branch.unlock()
2018.12.3 by Andrew Bennetts
Add a Branch.set_last_revision smart method, and make RemoteBranch.set_revision_history use it.
301
        self.assertEqual(None, result)
302
303
    def test_no_such_revision(self):
304
        # 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.
305
        client = FakeClient([
306
            # lock_write
307
            (('ok', 'branch token', 'repo token'), ),
308
            # set_last_revision
309
            (('NoSuchRevision', 'rev-id'), ),
310
            # unlock
311
            (('ok',), )])
2018.12.3 by Andrew Bennetts
Add a Branch.set_last_revision smart method, and make RemoteBranch.set_revision_history use it.
312
        transport = MemoryTransport()
313
        transport.mkdir('branch')
314
        transport = transport.clone('branch')
315
316
        bzrdir = RemoteBzrDir(transport, _client=False)
317
        branch = RemoteBranch(bzrdir, None, _client=client)
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
318
        branch._ensure_real = lambda: None
319
        branch.lock_write()
320
        client._calls = []
2018.12.3 by Andrew Bennetts
Add a Branch.set_last_revision smart method, and make RemoteBranch.set_revision_history use it.
321
322
        self.assertRaises(
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
323
            errors.NoSuchRevision, branch.set_revision_history, ['rev-id'])
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
324
        branch.unlock()
2018.12.3 by Andrew Bennetts
Add a Branch.set_last_revision smart method, and make RemoteBranch.set_revision_history use it.
325
326
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
327
class TestBranchControlGetBranchConf(tests.TestCase):
328
    """Test branch.control_files api munging...
329
2018.12.3 by Andrew Bennetts
Add a Branch.set_last_revision smart method, and make RemoteBranch.set_revision_history use it.
330
    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).
331
    call a specific API so that RemoteBranch's can intercept configuration
332
    file reading, allowing them to signal to the client about things like
333
    'email is configured for commits'.
334
    """
335
336
    def test_get_branch_conf(self):
337
        # in an empty branch we decode the response properly
338
        client = FakeClient([(('ok', ), 'config file body')])
339
        transport = MemoryTransport()
340
        transport.mkdir('quack')
341
        transport = transport.clone('quack')
342
        # we do not want bzrdir to make any remote calls
343
        bzrdir = RemoteBzrDir(transport, _client=False)
344
        branch = RemoteBranch(bzrdir, None, _client=client)
345
        result = branch.control_files.get('branch.conf')
346
        self.assertEqual(
347
            [('call2', 'Branch.get_config_file', ('///quack/',))],
348
            client._calls)
349
        self.assertEqual('config file body', result.read())
350
351
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.
352
class TestBranchLockWrite(tests.TestCase):
353
354
    def test_lock_write_unlockable(self):
355
        client = FakeClient([(('UnlockableTransport', ), '')])
356
        transport = MemoryTransport()
357
        transport.mkdir('quack')
358
        transport = transport.clone('quack')
359
        # we do not want bzrdir to make any remote calls
360
        bzrdir = RemoteBzrDir(transport, _client=False)
361
        branch = RemoteBranch(bzrdir, None, _client=client)
362
        self.assertRaises(errors.UnlockableTransport, branch.lock_write)
363
        self.assertEqual(
364
            [('call', 'Branch.lock_write', ('///quack/', '', ''))],
365
            client._calls)
366
367
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
368
class TestRemoteRepository(tests.TestCase):
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
369
370
    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
371
        """Create the fake client and repository for testing with.
372
        
373
        There's no real server here; we just have canned responses sent
374
        back one by one.
375
        
376
        :param transport_path: Path below the root of the MemoryTransport
377
            where the repository will be created.
378
        """
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
379
        client = FakeClient(responses)
380
        transport = MemoryTransport()
381
        transport.mkdir(transport_path)
382
        transport = transport.clone(transport_path)
383
        # we do not want bzrdir to make any remote calls
384
        bzrdir = RemoteBzrDir(transport, _client=False)
385
        repo = RemoteRepository(bzrdir, None, _client=client)
386
        return repo, client
387
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
388
2018.12.2 by Andrew Bennetts
Remove some duplicate code in test_remote
389
class TestRepositoryGatherStats(TestRemoteRepository):
2018.10.3 by v.ladeuil+lp at free
more tests for gather_stats
390
391
    def test_revid_none(self):
392
        # ('ok',), body with revisions and size
393
        responses = [(('ok', ), 'revisions: 2\nsize: 18\n')]
394
        transport_path = 'quack'
395
        repo, client = self.setup_fake_client_and_repository(
396
            responses, transport_path)
397
        result = repo.gather_stats(None)
398
        self.assertEqual(
399
            [('call2', 'Repository.gather_stats', ('///quack/','','no'))],
400
            client._calls)
401
        self.assertEqual({'revisions': 2, 'size': 18}, result)
402
403
    def test_revid_no_committers(self):
404
        # ('ok',), body without committers
405
        responses = [(('ok', ),
406
                      'firstrev: 123456.300 3600\n'
407
                      'latestrev: 654231.400 0\n'
408
                      'revisions: 2\n'
409
                      'size: 18\n')]
410
        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.
411
        revid = u'\xc8'.encode('utf8')
2018.10.3 by v.ladeuil+lp at free
more tests for gather_stats
412
        repo, client = self.setup_fake_client_and_repository(
413
            responses, transport_path)
414
        result = repo.gather_stats(revid)
415
        self.assertEqual(
416
            [('call2', 'Repository.gather_stats',
2018.5.106 by Andrew Bennetts
Update tests in test_remote to use utf-8 byte strings for revision IDs, rather than unicode strings.
417
              ('///quick/', revid, 'no'))],
2018.10.3 by v.ladeuil+lp at free
more tests for gather_stats
418
            client._calls)
419
        self.assertEqual({'revisions': 2, 'size': 18,
420
                          'firstrev': (123456.300, 3600),
421
                          'latestrev': (654231.400, 0),},
422
                         result)
423
424
    def test_revid_with_committers(self):
425
        # ('ok',), body with committers
426
        responses = [(('ok', ),
427
                      'committers: 128\n'
428
                      'firstrev: 123456.300 3600\n'
429
                      'latestrev: 654231.400 0\n'
430
                      'revisions: 2\n'
431
                      'size: 18\n')]
432
        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.
433
        revid = u'\xc8'.encode('utf8')
2018.10.3 by v.ladeuil+lp at free
more tests for gather_stats
434
        repo, client = self.setup_fake_client_and_repository(
435
            responses, transport_path)
436
        result = repo.gather_stats(revid, True)
437
        self.assertEqual(
438
            [('call2', 'Repository.gather_stats',
2018.5.106 by Andrew Bennetts
Update tests in test_remote to use utf-8 byte strings for revision IDs, rather than unicode strings.
439
              ('///buick/', revid, 'yes'))],
2018.10.3 by v.ladeuil+lp at free
more tests for gather_stats
440
            client._calls)
441
        self.assertEqual({'revisions': 2, 'size': 18,
442
                          'committers': 128,
443
                          'firstrev': (123456.300, 3600),
444
                          'latestrev': (654231.400, 0),},
445
                         result)
446
447
2018.5.68 by Wouter van Heyst
Merge RemoteRepository.gather_stats.
448
class TestRepositoryGetRevisionGraph(TestRemoteRepository):
449
    
450
    def test_null_revision(self):
451
        # a null revision has the predictable result {}, we should have no wire
452
        # traffic when calling it with this argument
453
        responses = [(('notused', ), '')]
454
        transport_path = 'empty'
455
        repo, client = self.setup_fake_client_and_repository(
456
            responses, transport_path)
457
        result = repo.get_revision_graph(NULL_REVISION)
458
        self.assertEqual([], client._calls)
459
        self.assertEqual({}, result)
460
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
461
    def test_none_revision(self):
462
        # 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.
463
        r1 = u'\u0e33'.encode('utf8')
464
        r2 = u'\u0dab'.encode('utf8')
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
465
        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.
466
        encoded_body = '\n'.join(lines)
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
467
468
        responses = [(('ok', ), encoded_body)]
469
        transport_path = 'sinhala'
470
        repo, client = self.setup_fake_client_and_repository(
471
            responses, transport_path)
472
        result = repo.get_revision_graph()
473
        self.assertEqual(
474
            [('call2', 'Repository.get_revision_graph', ('///sinhala/', ''))],
475
            client._calls)
476
        self.assertEqual({r1: [], r2: [r1]}, result)
477
478
    def test_specific_revision(self):
479
        # with a specific revision we want the graph for that
480
        # 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.
481
        r11 = u'\u0e33'.encode('utf8')
482
        r12 = u'\xc9'.encode('utf8')
483
        r2 = u'\u0dab'.encode('utf8')
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
484
        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.
485
        encoded_body = '\n'.join(lines)
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
486
487
        responses = [(('ok', ), encoded_body)]
488
        transport_path = 'sinhala'
489
        repo, client = self.setup_fake_client_and_repository(
490
            responses, transport_path)
491
        result = repo.get_revision_graph(r2)
492
        self.assertEqual(
2018.5.106 by Andrew Bennetts
Update tests in test_remote to use utf-8 byte strings for revision IDs, rather than unicode strings.
493
            [('call2', 'Repository.get_revision_graph', ('///sinhala/', r2))],
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
494
            client._calls)
495
        self.assertEqual({r11: [], r12: [], r2: [r11, r12], }, result)
496
497
    def test_no_such_revision(self):
498
        revid = '123'
499
        responses = [(('nosuchrevision', revid), '')]
500
        transport_path = 'sinhala'
501
        repo, client = self.setup_fake_client_and_repository(
502
            responses, transport_path)
503
        # 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
504
        self.assertRaises(errors.NoSuchRevision,
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
505
            repo.get_revision_graph, revid)
506
        self.assertEqual(
507
            [('call2', 'Repository.get_revision_graph', ('///sinhala/', revid))],
508
            client._calls)
509
510
        
511
class TestRepositoryIsShared(TestRemoteRepository):
512
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
513
    def test_is_shared(self):
514
        # ('yes', ) for Repository.is_shared -> 'True'.
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
515
        responses = [(('yes', ), )]
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
516
        transport_path = 'quack'
517
        repo, client = self.setup_fake_client_and_repository(
518
            responses, transport_path)
519
        result = repo.is_shared()
520
        self.assertEqual(
521
            [('call', 'Repository.is_shared', ('///quack/',))],
522
            client._calls)
523
        self.assertEqual(True, result)
524
525
    def test_is_not_shared(self):
526
        # ('no', ) for Repository.is_shared -> 'False'.
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
527
        responses = [(('no', ), )]
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
528
        transport_path = 'qwack'
529
        repo, client = self.setup_fake_client_and_repository(
530
            responses, transport_path)
531
        result = repo.is_shared()
532
        self.assertEqual(
533
            [('call', 'Repository.is_shared', ('///qwack/',))],
534
            client._calls)
535
        self.assertEqual(False, result)
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
536
537
538
class TestRepositoryLockWrite(TestRemoteRepository):
539
540
    def test_lock_write(self):
541
        responses = [(('ok', 'a token'), '')]
542
        transport_path = 'quack'
543
        repo, client = self.setup_fake_client_and_repository(
544
            responses, transport_path)
545
        result = repo.lock_write()
546
        self.assertEqual(
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.
547
            [('call', 'Repository.lock_write', ('///quack/', ''))],
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
548
            client._calls)
549
        self.assertEqual('a token', result)
550
551
    def test_lock_write_already_locked(self):
552
        responses = [(('LockContention', ), '')]
553
        transport_path = 'quack'
554
        repo, client = self.setup_fake_client_and_repository(
555
            responses, transport_path)
556
        self.assertRaises(errors.LockContention, repo.lock_write)
557
        self.assertEqual(
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.
558
            [('call', 'Repository.lock_write', ('///quack/', ''))],
559
            client._calls)
560
561
    def test_lock_write_unlockable(self):
562
        responses = [(('UnlockableTransport', ), '')]
563
        transport_path = 'quack'
564
        repo, client = self.setup_fake_client_and_repository(
565
            responses, transport_path)
566
        self.assertRaises(errors.UnlockableTransport, repo.lock_write)
567
        self.assertEqual(
568
            [('call', 'Repository.lock_write', ('///quack/', ''))],
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
569
            client._calls)
570
571
572
class TestRepositoryUnlock(TestRemoteRepository):
573
574
    def test_unlock(self):
575
        responses = [(('ok', 'a token'), ''),
576
                     (('ok',), '')]
577
        transport_path = 'quack'
578
        repo, client = self.setup_fake_client_and_repository(
579
            responses, transport_path)
580
        repo.lock_write()
581
        repo.unlock()
582
        self.assertEqual(
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.
583
            [('call', 'Repository.lock_write', ('///quack/', '')),
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
584
             ('call', 'Repository.unlock', ('///quack/', 'a token'))],
585
            client._calls)
586
587
    def test_unlock_wrong_token(self):
588
        # If somehow the token is wrong, unlock will raise TokenMismatch.
589
        responses = [(('ok', 'a token'), ''),
590
                     (('TokenMismatch',), '')]
591
        transport_path = 'quack'
592
        repo, client = self.setup_fake_client_and_repository(
593
            responses, transport_path)
594
        repo.lock_write()
595
        self.assertRaises(errors.TokenMismatch, repo.unlock)
596
597
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
598
class TestRepositoryHasRevision(TestRemoteRepository):
599
600
    def test_none(self):
601
        # repo.has_revision(None) should not cause any traffic.
602
        transport_path = 'quack'
603
        responses = None
604
        repo, client = self.setup_fake_client_and_repository(
605
            responses, transport_path)
606
607
        # The null revision is always there, so has_revision(None) == True.
608
        self.assertEqual(True, repo.has_revision(None))
609
610
        # The remote repo shouldn't be accessed.
611
        self.assertEqual([], client._calls)
2018.18.7 by Martin Pool
(broken) Start addng client proxy test for Repository.tarball
612
613
614
class TestRepositoryTarball(TestRemoteRepository):
615
616
    def test_repository_tarball(self):
617
        # it's not easy to construct the full tarball ahead of time, so we
618
        # won't use the canned responses 
619
        tarball_content = \
620
            'BZh91AY&SY\xc7\x94y\xbb\x00\x02\x85\x7f\x83\xc2\x90\x00 H\x03\xff\xc0\x11\x89\xd2\x00\x7f\xef\xdfp@H\x00\x10\x00\x080\x01\x8d\xa5f\x04T\xd4\xa3\xd4\xcd4\x8d\r0#\t\x88\xc26\x80\x01\x14\x82MOP\r\x0c\x87\x924\x00\x03C\xd4=@\xa9BMOB\x06\x9ai\xa1\xa0\x00\x00\x006\x03\xbe\xaa`\xcf33\xe6\x1e\xe4\x89JR\x9dN\xc3\xebK\x035\x00\xa0\\\xb7s\x0bh]\x8c\xb8M\xe3,\xa4\xa5,L\xd2\xfa/\xa5(\xa5\xe4\xc6&r@,\x81\xd2\xc8\x19\x83\xf0\x0c\x80\xe1\x80\xe2~\xf0\xa7+\r\x8f\xe3\x1e;>\x8bh\xa7\r}lM\x81\x9f@\x99\x846\\\xfdU\x95\xed\xec\xba\xdb\xa4/\x13\x83\x02X\x96\x13N\xa2e\x13^\xd7j\xadI\x8a\xa4\xbc\x96+}D\xa0\x06\xdb\xe2#[VT\x95\x19\xddeA\x99HB\xb1d\x86\xd0\'"*%2\x18\x19\x04\xb10\xa9\\D\xb6A\xad\x8b%D\xb1v\x13M\xa5\x7f!#41$y\xe0\xc2\x0e\xba\xb8\x1fF\xa8\x01\xae\xf1\xc7\xe2;\xe89\x9ba\x033\xb2\xcf\x13CK\xe0CY\xa1a\x19p\xa8gg(-\x83P6\xbd\n]#\xba\x02\x86r\x860\xe91\xef\x92\xbb\x11\xc4\x072:\x01\xc1\x8b\x88\xfd\xd6\x99D\x19K#C\x99\x03k\x9e#v\x8f\x03Tl\x07\xac\x9b\xb8@`,ZB3-2\x06\x85\xcfK\r\xea\xd9\x9d\xbc\x17\xfc]\xc9\x14\xe1BC\x1eQ\xe6\xec'
621
        transport_path = 'repo'
622
        expected_responses = [(('ok',), tarball_content),
623
            ]
624
        expected_calls = [(('Repository.tarball', '/repo', 'bz2',),),
625
            ]
626
        remote_repo, client = self.setup_fake_client_and_repository(
627
            expected_responses, transport_path)
628
        # Now actually ask for the tarball
629
        remote_repo._get_tarball('bz2')
630
        self.assertEqual(expected_calls, client._calls)
631
        
632
# TODO: Maybe add tests for Repository.tarball() - a bit hard to test in this
633
# framework as it's not 
634
635
    # TODO: Test for error when there's no repository there