/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_remote.py

  • Committer: Andrew Bennetts
  • Date: 2007-02-28 07:08:25 UTC
  • mfrom: (2018.13.1 hpss)
  • mto: (2018.5.80 hpss)
  • mto: This revision was merged to the branch mainline in revision 2435.
  • Revision ID: andrew.bennetts@canonical.com-20070228070825-q2dvkjb0a11ouhtx
Update to current hpss branch?  Fix lots of test failures.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2006, 2007 Canonical Ltd
 
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. 
 
22
"""
 
23
 
 
24
from cStringIO import StringIO
 
25
 
 
26
from bzrlib import (
 
27
    bzrdir,
 
28
    errors,
 
29
    remote,
 
30
    tests,
 
31
    )
 
32
from bzrlib.branch import Branch
 
33
from bzrlib.bzrdir import BzrDir, BzrDirFormat
 
34
from bzrlib.remote import (
 
35
    RemoteBranch,
 
36
    RemoteBzrDir,
 
37
    RemoteBzrDirFormat,
 
38
    RemoteRepository,
 
39
    )
 
40
from bzrlib.revision import NULL_REVISION
 
41
from bzrlib.smart import server
 
42
from bzrlib.smart.client import SmartClient
 
43
from bzrlib.transport import remote as remote_transport
 
44
from bzrlib.transport.memory import MemoryTransport
 
45
 
 
46
 
 
47
class BasicRemoteObjectTests(tests.TestCaseWithTransport):
 
48
 
 
49
    def setUp(self):
 
50
        super(BasicRemoteObjectTests, self).setUp()
 
51
        self.transport_server = server.SmartTCPServer_for_testing
 
52
        self.transport = self.get_transport()
 
53
        self.client = self.transport.get_smart_client()
 
54
        # make a branch that can be opened over the smart transport
 
55
        self.local_wt = BzrDir.create_standalone_workingtree('.')
 
56
 
 
57
    def test_create_remote_bzrdir(self):
 
58
        b = remote.RemoteBzrDir(self.transport)
 
59
        self.assertIsInstance(b, BzrDir)
 
60
 
 
61
    def test_open_remote_branch(self):
 
62
        # open a standalone branch in the working directory
 
63
        b = remote.RemoteBzrDir(self.transport)
 
64
        branch = b.open_branch()
 
65
 
 
66
    def test_remote_repository(self):
 
67
        b = BzrDir.open_from_transport(self.transport)
 
68
        repo = b.open_repository()
 
69
        revid = u'\xc823123123'
 
70
        self.assertFalse(repo.has_revision(revid))
 
71
        self.local_wt.commit(message='test commit', rev_id=revid)
 
72
        self.assertTrue(repo.has_revision(revid))
 
73
 
 
74
    def test_remote_branch_revision_history(self):
 
75
        b = BzrDir.open_from_transport(self.transport).open_branch()
 
76
        self.assertEqual([], b.revision_history())
 
77
        r1 = self.local_wt.commit('1st commit')
 
78
        r2 = self.local_wt.commit('1st commit', rev_id=u'\xc8')
 
79
        self.assertEqual([r1, r2], b.revision_history())
 
80
 
 
81
    def test_find_correct_format(self):
 
82
        """Should open a RemoteBzrDir over a RemoteTransport"""
 
83
        fmt = BzrDirFormat.find_format(self.transport)
 
84
        self.assertTrue(RemoteBzrDirFormat in BzrDirFormat._control_formats)
 
85
        self.assertIsInstance(fmt, remote.RemoteBzrDirFormat)
 
86
 
 
87
    def test_open_detected_smart_format(self):
 
88
        fmt = BzrDirFormat.find_format(self.transport)
 
89
        d = fmt.open(self.transport)
 
90
        self.assertIsInstance(d, BzrDir)
 
91
 
 
92
 
 
93
class FakeProtocol(object):
 
94
    """Lookalike SmartClientRequestProtocolOne allowing body reading tests."""
 
95
 
 
96
    def __init__(self, body):
 
97
        self._body_buffer = StringIO(body)
 
98
 
 
99
    def read_body_bytes(self, count=-1):
 
100
        return self._body_buffer.read(count)
 
101
 
 
102
 
 
103
class FakeClient(SmartClient):
 
104
    """Lookalike for SmartClient allowing testing."""
 
105
    
 
106
    def __init__(self, responses):
 
107
        # We don't call the super init because there is no medium.
 
108
        """create a FakeClient.
 
109
 
 
110
        :param respones: A list of response-tuple, body-data pairs to be sent
 
111
            back to callers.
 
112
        """
 
113
        self.responses = responses
 
114
        self._calls = []
 
115
 
 
116
    def call(self, method, *args):
 
117
        self._calls.append(('call', method, args))
 
118
        return self.responses.pop(0)[0]
 
119
 
 
120
    def call2(self, method, *args):
 
121
        self._calls.append(('call2', method, args))
 
122
        result = self.responses.pop(0)
 
123
        return result[0], FakeProtocol(result[1])
 
124
 
 
125
 
 
126
class TestBzrDirOpenBranch(tests.TestCase):
 
127
 
 
128
    def test_branch_present(self):
 
129
        client = FakeClient([(('ok', ''), ), (('ok', ''), )])
 
130
        transport = MemoryTransport()
 
131
        transport.mkdir('quack')
 
132
        transport = transport.clone('quack')
 
133
        bzrdir = RemoteBzrDir(transport, _client=client)
 
134
        result = bzrdir.open_branch()
 
135
        self.assertEqual(
 
136
            [('call', 'BzrDir.open_branch', ('///quack/',)),
 
137
             ('call', 'BzrDir.find_repository', ('///quack/',))],
 
138
            client._calls)
 
139
        self.assertIsInstance(result, RemoteBranch)
 
140
        self.assertEqual(bzrdir, result.bzrdir)
 
141
 
 
142
    def test_branch_missing(self):
 
143
        client = FakeClient([(('nobranch',), )])
 
144
        transport = MemoryTransport()
 
145
        transport.mkdir('quack')
 
146
        transport = transport.clone('quack')
 
147
        bzrdir = RemoteBzrDir(transport, _client=client)
 
148
        self.assertRaises(errors.NotBranchError, bzrdir.open_branch)
 
149
        self.assertEqual(
 
150
            [('call', 'BzrDir.open_branch', ('///quack/',))],
 
151
            client._calls)
 
152
 
 
153
 
 
154
class TestBranchLastRevisionInfo(tests.TestCase):
 
155
 
 
156
    def test_empty_branch(self):
 
157
        # in an empty branch we decode the response properly
 
158
        client = FakeClient([(('ok', '0', ''), )])
 
159
        transport = MemoryTransport()
 
160
        transport.mkdir('quack')
 
161
        transport = transport.clone('quack')
 
162
        # we do not want bzrdir to make any remote calls
 
163
        bzrdir = RemoteBzrDir(transport, _client=False)
 
164
        branch = RemoteBranch(bzrdir, None, _client=client)
 
165
        result = branch.last_revision_info()
 
166
 
 
167
        self.assertEqual(
 
168
            [('call', 'Branch.last_revision_info', ('///quack/',))],
 
169
            client._calls)
 
170
        self.assertEqual((0, NULL_REVISION), result)
 
171
 
 
172
    def test_non_empty_branch(self):
 
173
        # in a non-empty branch we also decode the response properly
 
174
 
 
175
        client = FakeClient([(('ok', '2', u'\xc8'.encode('utf8')), )])
 
176
        transport = MemoryTransport()
 
177
        transport.mkdir('kwaak')
 
178
        transport = transport.clone('kwaak')
 
179
        # we do not want bzrdir to make any remote calls
 
180
        bzrdir = RemoteBzrDir(transport, _client=False)
 
181
        branch = RemoteBranch(bzrdir, None, _client=client)
 
182
        result = branch.last_revision_info()
 
183
 
 
184
        self.assertEqual(
 
185
            [('call', 'Branch.last_revision_info', ('///kwaak/',))],
 
186
            client._calls)
 
187
        self.assertEqual((2, u'\xc8'), result)
 
188
 
 
189
 
 
190
class TestBranchSetLastRevision(tests.TestCase):
 
191
 
 
192
    def test_set_empty(self):
 
193
        # set_revision_history([]) is translated to calling
 
194
        # Branch.set_last_revision(path, '') on the wire.
 
195
        client = FakeClient([
 
196
            # lock_write
 
197
            (('ok', 'branch token', 'repo token'), ),
 
198
            # set_last_revision
 
199
            (('ok',), ),
 
200
            # unlock
 
201
            (('ok',), )])
 
202
        transport = MemoryTransport()
 
203
        transport.mkdir('branch')
 
204
        transport = transport.clone('branch')
 
205
 
 
206
        bzrdir = RemoteBzrDir(transport, _client=False)
 
207
        branch = RemoteBranch(bzrdir, None, _client=client)
 
208
        # This is a hack to work around the problem that RemoteBranch currently
 
209
        # unnecessarily invokes _ensure_real upon a call to lock_write.
 
210
        branch._ensure_real = lambda: None
 
211
        branch.lock_write()
 
212
        client._calls = []
 
213
        result = branch.set_revision_history([])
 
214
        self.assertEqual(
 
215
            [('call', 'Branch.set_last_revision',
 
216
                ('///branch/', 'branch token', 'repo token', ''))],
 
217
            client._calls)
 
218
        branch.unlock()
 
219
        self.assertEqual(None, result)
 
220
 
 
221
    def test_set_nonempty(self):
 
222
        # set_revision_history([rev-id1, ..., rev-idN]) is translated to calling
 
223
        # Branch.set_last_revision(path, rev-idN) on the wire.
 
224
        client = FakeClient([
 
225
            # lock_write
 
226
            (('ok', 'branch token', 'repo token'), ),
 
227
            # set_last_revision
 
228
            (('ok',), ),
 
229
            # unlock
 
230
            (('ok',), )])
 
231
        transport = MemoryTransport()
 
232
        transport.mkdir('branch')
 
233
        transport = transport.clone('branch')
 
234
 
 
235
        bzrdir = RemoteBzrDir(transport, _client=False)
 
236
        branch = RemoteBranch(bzrdir, None, _client=client)
 
237
        # This is a hack to work around the problem that RemoteBranch currently
 
238
        # unnecessarily invokes _ensure_real upon a call to lock_write.
 
239
        branch._ensure_real = lambda: None
 
240
        # Lock the branch, reset the record of remote calls.
 
241
        branch.lock_write()
 
242
        client._calls = []
 
243
 
 
244
        result = branch.set_revision_history(['rev-id1', 'rev-id2'])
 
245
        self.assertEqual(
 
246
            [('call', 'Branch.set_last_revision',
 
247
                ('///branch/', 'branch token', 'repo token', 'rev-id2'))],
 
248
            client._calls)
 
249
        branch.unlock()
 
250
        self.assertEqual(None, result)
 
251
 
 
252
    def test_no_such_revision(self):
 
253
        # A response of 'NoSuchRevision' is translated into an exception.
 
254
        client = FakeClient([
 
255
            # lock_write
 
256
            (('ok', 'branch token', 'repo token'), ),
 
257
            # set_last_revision
 
258
            (('NoSuchRevision', 'rev-id'), ),
 
259
            # unlock
 
260
            (('ok',), )])
 
261
        transport = MemoryTransport()
 
262
        transport.mkdir('branch')
 
263
        transport = transport.clone('branch')
 
264
 
 
265
        bzrdir = RemoteBzrDir(transport, _client=False)
 
266
        branch = RemoteBranch(bzrdir, None, _client=client)
 
267
        branch._ensure_real = lambda: None
 
268
        branch.lock_write()
 
269
        client._calls = []
 
270
 
 
271
        self.assertRaises(
 
272
            errors.NoSuchRevision, branch.set_revision_history, ['rev-id'])
 
273
        branch.unlock()
 
274
 
 
275
 
 
276
class TestBranchControlGetBranchConf(tests.TestCase):
 
277
    """Test branch.control_files api munging...
 
278
 
 
279
    we special case RemoteBranch.control_files.get('branch.conf') to
 
280
    call a specific API so that RemoteBranch's can intercept configuration
 
281
    file reading, allowing them to signal to the client about things like
 
282
    'email is configured for commits'.
 
283
    """
 
284
 
 
285
    def test_get_branch_conf(self):
 
286
        # in an empty branch we decode the response properly
 
287
        client = FakeClient([(('ok', ), 'config file body')])
 
288
        transport = MemoryTransport()
 
289
        transport.mkdir('quack')
 
290
        transport = transport.clone('quack')
 
291
        # we do not want bzrdir to make any remote calls
 
292
        bzrdir = RemoteBzrDir(transport, _client=False)
 
293
        branch = RemoteBranch(bzrdir, None, _client=client)
 
294
        result = branch.control_files.get('branch.conf')
 
295
        self.assertEqual(
 
296
            [('call2', 'Branch.get_config_file', ('///quack/',))],
 
297
            client._calls)
 
298
        self.assertEqual('config file body', result.read())
 
299
 
 
300
 
 
301
class TestRemoteRepository(tests.TestCase):
 
302
 
 
303
    def setup_fake_client_and_repository(self, responses, transport_path):
 
304
        """Create the fake client and repository for testing with."""
 
305
        client = FakeClient(responses)
 
306
        transport = MemoryTransport()
 
307
        transport.mkdir(transport_path)
 
308
        transport = transport.clone(transport_path)
 
309
        # we do not want bzrdir to make any remote calls
 
310
        bzrdir = RemoteBzrDir(transport, _client=False)
 
311
        repo = RemoteRepository(bzrdir, None, _client=client)
 
312
        return repo, client
 
313
 
 
314
 
 
315
class TestRepositoryGatherStats(TestRemoteRepository):
 
316
 
 
317
    def test_revid_none(self):
 
318
        # ('ok',), body with revisions and size
 
319
        responses = [(('ok', ), 'revisions: 2\nsize: 18\n')]
 
320
        transport_path = 'quack'
 
321
        repo, client = self.setup_fake_client_and_repository(
 
322
            responses, transport_path)
 
323
        result = repo.gather_stats(None)
 
324
        self.assertEqual(
 
325
            [('call2', 'Repository.gather_stats', ('///quack/','','no'))],
 
326
            client._calls)
 
327
        self.assertEqual({'revisions': 2, 'size': 18}, result)
 
328
 
 
329
    def test_revid_no_committers(self):
 
330
        # ('ok',), body without committers
 
331
        responses = [(('ok', ),
 
332
                      'firstrev: 123456.300 3600\n'
 
333
                      'latestrev: 654231.400 0\n'
 
334
                      'revisions: 2\n'
 
335
                      'size: 18\n')]
 
336
        transport_path = 'quick'
 
337
        revid = u'\xc8'
 
338
        repo, client = self.setup_fake_client_and_repository(
 
339
            responses, transport_path)
 
340
        result = repo.gather_stats(revid)
 
341
        self.assertEqual(
 
342
            [('call2', 'Repository.gather_stats',
 
343
              ('///quick/', revid.encode('utf8'), 'no'))],
 
344
            client._calls)
 
345
        self.assertEqual({'revisions': 2, 'size': 18,
 
346
                          'firstrev': (123456.300, 3600),
 
347
                          'latestrev': (654231.400, 0),},
 
348
                         result)
 
349
 
 
350
    def test_revid_with_committers(self):
 
351
        # ('ok',), body with committers
 
352
        responses = [(('ok', ),
 
353
                      'committers: 128\n'
 
354
                      'firstrev: 123456.300 3600\n'
 
355
                      'latestrev: 654231.400 0\n'
 
356
                      'revisions: 2\n'
 
357
                      'size: 18\n')]
 
358
        transport_path = 'buick'
 
359
        revid = u'\xc8'
 
360
        repo, client = self.setup_fake_client_and_repository(
 
361
            responses, transport_path)
 
362
        result = repo.gather_stats(revid, True)
 
363
        self.assertEqual(
 
364
            [('call2', 'Repository.gather_stats',
 
365
              ('///buick/', revid.encode('utf8'), 'yes'))],
 
366
            client._calls)
 
367
        self.assertEqual({'revisions': 2, 'size': 18,
 
368
                          'committers': 128,
 
369
                          'firstrev': (123456.300, 3600),
 
370
                          'latestrev': (654231.400, 0),},
 
371
                         result)
 
372
 
 
373
 
 
374
class TestRepositoryGetRevisionGraph(TestRemoteRepository):
 
375
    
 
376
    def test_null_revision(self):
 
377
        # a null revision has the predictable result {}, we should have no wire
 
378
        # traffic when calling it with this argument
 
379
        responses = [(('notused', ), '')]
 
380
        transport_path = 'empty'
 
381
        repo, client = self.setup_fake_client_and_repository(
 
382
            responses, transport_path)
 
383
        result = repo.get_revision_graph(NULL_REVISION)
 
384
        self.assertEqual([], client._calls)
 
385
        self.assertEqual({}, result)
 
386
 
 
387
    def test_none_revision(self):
 
388
        # with none we want the entire graph
 
389
        r1 = u'\u0e33'
 
390
        r2 = u'\u0dab'
 
391
        lines = [' '.join([r2, r1]), r1]
 
392
        encoded_body = '\n'.join(lines).encode('utf8')
 
393
 
 
394
        responses = [(('ok', ), encoded_body)]
 
395
        transport_path = 'sinhala'
 
396
        repo, client = self.setup_fake_client_and_repository(
 
397
            responses, transport_path)
 
398
        result = repo.get_revision_graph()
 
399
        self.assertEqual(
 
400
            [('call2', 'Repository.get_revision_graph', ('///sinhala/', ''))],
 
401
            client._calls)
 
402
        self.assertEqual({r1: [], r2: [r1]}, result)
 
403
 
 
404
    def test_specific_revision(self):
 
405
        # with a specific revision we want the graph for that
 
406
        # with none we want the entire graph
 
407
        r11 = u'\u0e33'
 
408
        r12 = u'\xc9'
 
409
        r2 = u'\u0dab'
 
410
        lines = [' '.join([r2, r11, r12]), r11, r12]
 
411
        encoded_body = '\n'.join(lines).encode('utf8')
 
412
 
 
413
        responses = [(('ok', ), encoded_body)]
 
414
        transport_path = 'sinhala'
 
415
        repo, client = self.setup_fake_client_and_repository(
 
416
            responses, transport_path)
 
417
        result = repo.get_revision_graph(r2)
 
418
        self.assertEqual(
 
419
            [('call2', 'Repository.get_revision_graph', ('///sinhala/', r2.encode('utf8')))],
 
420
            client._calls)
 
421
        self.assertEqual({r11: [], r12: [], r2: [r11, r12], }, result)
 
422
 
 
423
    def test_no_such_revision(self):
 
424
        revid = '123'
 
425
        responses = [(('nosuchrevision', revid), '')]
 
426
        transport_path = 'sinhala'
 
427
        repo, client = self.setup_fake_client_and_repository(
 
428
            responses, transport_path)
 
429
        # also check that the right revision is reported in the error
 
430
        self.assertRaises(errors.NoSuchRevision,
 
431
            repo.get_revision_graph, revid)
 
432
        self.assertEqual(
 
433
            [('call2', 'Repository.get_revision_graph', ('///sinhala/', revid))],
 
434
            client._calls)
 
435
 
 
436
        
 
437
class TestRepositoryIsShared(TestRemoteRepository):
 
438
 
 
439
    def test_is_shared(self):
 
440
        # ('yes', ) for Repository.is_shared -> 'True'.
 
441
        responses = [(('yes', ), )]
 
442
        transport_path = 'quack'
 
443
        repo, client = self.setup_fake_client_and_repository(
 
444
            responses, transport_path)
 
445
        result = repo.is_shared()
 
446
        self.assertEqual(
 
447
            [('call', 'Repository.is_shared', ('///quack/',))],
 
448
            client._calls)
 
449
        self.assertEqual(True, result)
 
450
 
 
451
    def test_is_not_shared(self):
 
452
        # ('no', ) for Repository.is_shared -> 'False'.
 
453
        responses = [(('no', ), )]
 
454
        transport_path = 'qwack'
 
455
        repo, client = self.setup_fake_client_and_repository(
 
456
            responses, transport_path)
 
457
        result = repo.is_shared()
 
458
        self.assertEqual(
 
459
            [('call', 'Repository.is_shared', ('///qwack/',))],
 
460
            client._calls)
 
461
        self.assertEqual(False, result)
 
462
 
 
463
 
 
464
class TestRepositoryLockWrite(TestRemoteRepository):
 
465
 
 
466
    def test_lock_write(self):
 
467
        responses = [(('ok', 'a token'), '')]
 
468
        transport_path = 'quack'
 
469
        repo, client = self.setup_fake_client_and_repository(
 
470
            responses, transport_path)
 
471
        result = repo.lock_write()
 
472
        self.assertEqual(
 
473
            [('call', 'Repository.lock_write', ('///quack/',))],
 
474
            client._calls)
 
475
        self.assertEqual('a token', result)
 
476
 
 
477
    def test_lock_write_already_locked(self):
 
478
        responses = [(('LockContention', ), '')]
 
479
        transport_path = 'quack'
 
480
        repo, client = self.setup_fake_client_and_repository(
 
481
            responses, transport_path)
 
482
        self.assertRaises(errors.LockContention, repo.lock_write)
 
483
        self.assertEqual(
 
484
            [('call', 'Repository.lock_write', ('///quack/',))],
 
485
            client._calls)
 
486
 
 
487
 
 
488
class TestRepositoryUnlock(TestRemoteRepository):
 
489
 
 
490
    def test_unlock(self):
 
491
        responses = [(('ok', 'a token'), ''),
 
492
                     (('ok',), '')]
 
493
        transport_path = 'quack'
 
494
        repo, client = self.setup_fake_client_and_repository(
 
495
            responses, transport_path)
 
496
        repo.lock_write()
 
497
        repo.unlock()
 
498
        self.assertEqual(
 
499
            [('call', 'Repository.lock_write', ('///quack/',)),
 
500
             ('call', 'Repository.unlock', ('///quack/', 'a token'))],
 
501
            client._calls)
 
502
 
 
503
    def test_unlock_wrong_token(self):
 
504
        # If somehow the token is wrong, unlock will raise TokenMismatch.
 
505
        responses = [(('ok', 'a token'), ''),
 
506
                     (('TokenMismatch',), '')]
 
507
        transport_path = 'quack'
 
508
        repo, client = self.setup_fake_client_and_repository(
 
509
            responses, transport_path)
 
510
        repo.lock_write()
 
511
        self.assertRaises(errors.TokenMismatch, repo.unlock)
 
512
 
 
513
 
 
514
class TestRepositoryHasRevision(TestRemoteRepository):
 
515
 
 
516
    def test_none(self):
 
517
        # repo.has_revision(None) should not cause any traffic.
 
518
        transport_path = 'quack'
 
519
        responses = None
 
520
        repo, client = self.setup_fake_client_and_repository(
 
521
            responses, transport_path)
 
522
 
 
523
        # The null revision is always there, so has_revision(None) == True.
 
524
        self.assertEqual(True, repo.has_revision(None))
 
525
 
 
526
        # The remote repo shouldn't be accessed.
 
527
        self.assertEqual([], client._calls)
 
528
 
 
529