/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: Alexander Belchenko
  • Date: 2007-04-30 19:52:44 UTC
  • mto: This revision was merged to the branch mainline in revision 2478.
  • Revision ID: bialix@ukr.net-20070430195244-pxc5r3x72ckh027e
Bugfix #110901: commit message template written with native line-endings; corresponding unit tests added

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
These tests correspond to tests.test_smart, which exercises the server side.
 
24
"""
 
25
 
 
26
from cStringIO import StringIO
 
27
 
 
28
from bzrlib import (
 
29
    bzrdir,
 
30
    errors,
 
31
    remote,
 
32
    repository,
 
33
    tests,
 
34
    )
 
35
from bzrlib.branch import Branch
 
36
from bzrlib.bzrdir import BzrDir, BzrDirFormat
 
37
from bzrlib.remote import (
 
38
    RemoteBranch,
 
39
    RemoteBzrDir,
 
40
    RemoteBzrDirFormat,
 
41
    RemoteRepository,
 
42
    )
 
43
from bzrlib.revision import NULL_REVISION
 
44
from bzrlib.smart import server, medium
 
45
from bzrlib.smart.client import _SmartClient
 
46
from bzrlib.transport.memory import MemoryTransport
 
47
from bzrlib.transport.remote import RemoteTransport
 
48
 
 
49
 
 
50
class BasicRemoteObjectTests(tests.TestCaseWithTransport):
 
51
 
 
52
    def setUp(self):
 
53
        self.transport_server = server.SmartTCPServer_for_testing
 
54
        super(BasicRemoteObjectTests, self).setUp()
 
55
        self.transport = self.get_transport()
 
56
        self.client = self.transport.get_smart_client()
 
57
        # make a branch that can be opened over the smart transport
 
58
        self.local_wt = BzrDir.create_standalone_workingtree('.')
 
59
 
 
60
    def tearDown(self):
 
61
        self.transport.disconnect()
 
62
        tests.TestCaseWithTransport.tearDown(self)
 
63
 
 
64
    def test_create_remote_bzrdir(self):
 
65
        b = remote.RemoteBzrDir(self.transport)
 
66
        self.assertIsInstance(b, BzrDir)
 
67
 
 
68
    def test_open_remote_branch(self):
 
69
        # open a standalone branch in the working directory
 
70
        b = remote.RemoteBzrDir(self.transport)
 
71
        branch = b.open_branch()
 
72
        self.assertIsInstance(branch, Branch)
 
73
 
 
74
    def test_remote_repository(self):
 
75
        b = BzrDir.open_from_transport(self.transport)
 
76
        repo = b.open_repository()
 
77
        revid = u'\xc823123123'.encode('utf8')
 
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))
 
81
 
 
82
    def test_remote_branch_revision_history(self):
 
83
        b = BzrDir.open_from_transport(self.transport).open_branch()
 
84
        self.assertEqual([], b.revision_history())
 
85
        r1 = self.local_wt.commit('1st commit')
 
86
        r2 = self.local_wt.commit('1st commit', rev_id=u'\xc8'.encode('utf8'))
 
87
        self.assertEqual([r1, r2], b.revision_history())
 
88
 
 
89
    def test_find_correct_format(self):
 
90
        """Should open a RemoteBzrDir over a RemoteTransport"""
 
91
        fmt = BzrDirFormat.find_format(self.transport)
 
92
        self.assertTrue(RemoteBzrDirFormat
 
93
                        in BzrDirFormat._control_server_formats)
 
94
        self.assertIsInstance(fmt, remote.RemoteBzrDirFormat)
 
95
 
 
96
    def test_open_detected_smart_format(self):
 
97
        fmt = BzrDirFormat.find_format(self.transport)
 
98
        d = fmt.open(self.transport)
 
99
        self.assertIsInstance(d, BzrDir)
 
100
 
 
101
 
 
102
class FakeProtocol(object):
 
103
    """Lookalike SmartClientRequestProtocolOne allowing body reading tests."""
 
104
 
 
105
    def __init__(self, body):
 
106
        self._body_buffer = StringIO(body)
 
107
 
 
108
    def read_body_bytes(self, count=-1):
 
109
        return self._body_buffer.read(count)
 
110
 
 
111
 
 
112
class FakeClient(_SmartClient):
 
113
    """Lookalike for _SmartClient allowing testing."""
 
114
    
 
115
    def __init__(self, responses):
 
116
        # We don't call the super init because there is no medium.
 
117
        """create a FakeClient.
 
118
 
 
119
        :param respones: A list of response-tuple, body-data pairs to be sent
 
120
            back to callers.
 
121
        """
 
122
        self.responses = responses
 
123
        self._calls = []
 
124
 
 
125
    def call(self, method, *args):
 
126
        self._calls.append(('call', method, args))
 
127
        return self.responses.pop(0)[0]
 
128
 
 
129
    def call_expecting_body(self, method, *args):
 
130
        self._calls.append(('call_expecting_body', method, args))
 
131
        result = self.responses.pop(0)
 
132
        return result[0], FakeProtocol(result[1])
 
133
 
 
134
 
 
135
class TestBzrDirOpenBranch(tests.TestCase):
 
136
 
 
137
    def test_branch_present(self):
 
138
        client = FakeClient([(('ok', ''), ), (('ok', '', 'no', 'no'), )])
 
139
        transport = MemoryTransport()
 
140
        transport.mkdir('quack')
 
141
        transport = transport.clone('quack')
 
142
        bzrdir = RemoteBzrDir(transport, _client=client)
 
143
        result = bzrdir.open_branch()
 
144
        self.assertEqual(
 
145
            [('call', 'BzrDir.open_branch', ('///quack/',)),
 
146
             ('call', 'BzrDir.find_repository', ('///quack/',))],
 
147
            client._calls)
 
148
        self.assertIsInstance(result, RemoteBranch)
 
149
        self.assertEqual(bzrdir, result.bzrdir)
 
150
 
 
151
    def test_branch_missing(self):
 
152
        client = FakeClient([(('nobranch',), )])
 
153
        transport = MemoryTransport()
 
154
        transport.mkdir('quack')
 
155
        transport = transport.clone('quack')
 
156
        bzrdir = RemoteBzrDir(transport, _client=client)
 
157
        self.assertRaises(errors.NotBranchError, bzrdir.open_branch)
 
158
        self.assertEqual(
 
159
            [('call', 'BzrDir.open_branch', ('///quack/',))],
 
160
            client._calls)
 
161
 
 
162
    def check_open_repository(self, rich_root, subtrees):
 
163
        if rich_root:
 
164
            rich_response = 'yes'
 
165
        else:
 
166
            rich_response = 'no'
 
167
        if subtrees:
 
168
            subtree_response = 'yes'
 
169
        else:
 
170
            subtree_response = 'no'
 
171
        client = FakeClient([(('ok', '', rich_response, subtree_response), ),])
 
172
        transport = MemoryTransport()
 
173
        transport.mkdir('quack')
 
174
        transport = transport.clone('quack')
 
175
        bzrdir = RemoteBzrDir(transport, _client=client)
 
176
        result = bzrdir.open_repository()
 
177
        self.assertEqual(
 
178
            [('call', 'BzrDir.find_repository', ('///quack/',))],
 
179
            client._calls)
 
180
        self.assertIsInstance(result, RemoteRepository)
 
181
        self.assertEqual(bzrdir, result.bzrdir)
 
182
        self.assertEqual(rich_root, result._format.rich_root_data)
 
183
        self.assertEqual(subtrees, result._format.supports_tree_reference)
 
184
 
 
185
    def test_open_repository_sets_format_attributes(self):
 
186
        self.check_open_repository(True, True)
 
187
        self.check_open_repository(False, True)
 
188
        self.check_open_repository(True, False)
 
189
        self.check_open_repository(False, False)
 
190
 
 
191
    def test_old_server(self):
 
192
        """RemoteBzrDirFormat should fail to probe if the server version is too
 
193
        old.
 
194
        """
 
195
        self.assertRaises(errors.NotBranchError,
 
196
            RemoteBzrDirFormat.probe_transport, OldServerTransport())
 
197
 
 
198
 
 
199
class OldSmartClient(object):
 
200
    """A fake smart client for test_old_version that just returns a version one
 
201
    response to the 'hello' (query version) command.
 
202
    """
 
203
 
 
204
    def get_request(self):
 
205
        input_file = StringIO('ok\x011\n')
 
206
        output_file = StringIO()
 
207
        client_medium = medium.SmartSimplePipesClientMedium(
 
208
            input_file, output_file)
 
209
        return medium.SmartClientStreamMediumRequest(client_medium)
 
210
 
 
211
 
 
212
class OldServerTransport(object):
 
213
    """A fake transport for test_old_server that reports it's smart server
 
214
    protocol version as version one.
 
215
    """
 
216
 
 
217
    def __init__(self):
 
218
        self.base = 'fake:'
 
219
 
 
220
    def get_smart_client(self):
 
221
        return OldSmartClient()
 
222
 
 
223
 
 
224
class TestBranchLastRevisionInfo(tests.TestCase):
 
225
 
 
226
    def test_empty_branch(self):
 
227
        # in an empty branch we decode the response properly
 
228
        client = FakeClient([(('ok', '0', 'null:'), )])
 
229
        transport = MemoryTransport()
 
230
        transport.mkdir('quack')
 
231
        transport = transport.clone('quack')
 
232
        # we do not want bzrdir to make any remote calls
 
233
        bzrdir = RemoteBzrDir(transport, _client=False)
 
234
        branch = RemoteBranch(bzrdir, None, _client=client)
 
235
        result = branch.last_revision_info()
 
236
 
 
237
        self.assertEqual(
 
238
            [('call', 'Branch.last_revision_info', ('///quack/',))],
 
239
            client._calls)
 
240
        self.assertEqual((0, NULL_REVISION), result)
 
241
 
 
242
    def test_non_empty_branch(self):
 
243
        # in a non-empty branch we also decode the response properly
 
244
        revid = u'\xc8'.encode('utf8')
 
245
        client = FakeClient([(('ok', '2', revid), )])
 
246
        transport = MemoryTransport()
 
247
        transport.mkdir('kwaak')
 
248
        transport = transport.clone('kwaak')
 
249
        # we do not want bzrdir to make any remote calls
 
250
        bzrdir = RemoteBzrDir(transport, _client=False)
 
251
        branch = RemoteBranch(bzrdir, None, _client=client)
 
252
        result = branch.last_revision_info()
 
253
 
 
254
        self.assertEqual(
 
255
            [('call', 'Branch.last_revision_info', ('///kwaak/',))],
 
256
            client._calls)
 
257
        self.assertEqual((2, revid), result)
 
258
 
 
259
 
 
260
class TestBranchSetLastRevision(tests.TestCase):
 
261
 
 
262
    def test_set_empty(self):
 
263
        # set_revision_history([]) is translated to calling
 
264
        # Branch.set_last_revision(path, '') on the wire.
 
265
        client = FakeClient([
 
266
            # lock_write
 
267
            (('ok', 'branch token', 'repo token'), ),
 
268
            # set_last_revision
 
269
            (('ok',), ),
 
270
            # unlock
 
271
            (('ok',), )])
 
272
        transport = MemoryTransport()
 
273
        transport.mkdir('branch')
 
274
        transport = transport.clone('branch')
 
275
 
 
276
        bzrdir = RemoteBzrDir(transport, _client=False)
 
277
        branch = RemoteBranch(bzrdir, None, _client=client)
 
278
        # This is a hack to work around the problem that RemoteBranch currently
 
279
        # unnecessarily invokes _ensure_real upon a call to lock_write.
 
280
        branch._ensure_real = lambda: None
 
281
        branch.lock_write()
 
282
        client._calls = []
 
283
        result = branch.set_revision_history([])
 
284
        self.assertEqual(
 
285
            [('call', 'Branch.set_last_revision',
 
286
                ('///branch/', 'branch token', 'repo token', 'null:'))],
 
287
            client._calls)
 
288
        branch.unlock()
 
289
        self.assertEqual(None, result)
 
290
 
 
291
    def test_set_nonempty(self):
 
292
        # set_revision_history([rev-id1, ..., rev-idN]) is translated to calling
 
293
        # Branch.set_last_revision(path, rev-idN) on the wire.
 
294
        client = FakeClient([
 
295
            # lock_write
 
296
            (('ok', 'branch token', 'repo token'), ),
 
297
            # set_last_revision
 
298
            (('ok',), ),
 
299
            # unlock
 
300
            (('ok',), )])
 
301
        transport = MemoryTransport()
 
302
        transport.mkdir('branch')
 
303
        transport = transport.clone('branch')
 
304
 
 
305
        bzrdir = RemoteBzrDir(transport, _client=False)
 
306
        branch = RemoteBranch(bzrdir, None, _client=client)
 
307
        # This is a hack to work around the problem that RemoteBranch currently
 
308
        # unnecessarily invokes _ensure_real upon a call to lock_write.
 
309
        branch._ensure_real = lambda: None
 
310
        # Lock the branch, reset the record of remote calls.
 
311
        branch.lock_write()
 
312
        client._calls = []
 
313
 
 
314
        result = branch.set_revision_history(['rev-id1', 'rev-id2'])
 
315
        self.assertEqual(
 
316
            [('call', 'Branch.set_last_revision',
 
317
                ('///branch/', 'branch token', 'repo token', 'rev-id2'))],
 
318
            client._calls)
 
319
        branch.unlock()
 
320
        self.assertEqual(None, result)
 
321
 
 
322
    def test_no_such_revision(self):
 
323
        # A response of 'NoSuchRevision' is translated into an exception.
 
324
        client = FakeClient([
 
325
            # lock_write
 
326
            (('ok', 'branch token', 'repo token'), ),
 
327
            # set_last_revision
 
328
            (('NoSuchRevision', 'rev-id'), ),
 
329
            # unlock
 
330
            (('ok',), )])
 
331
        transport = MemoryTransport()
 
332
        transport.mkdir('branch')
 
333
        transport = transport.clone('branch')
 
334
 
 
335
        bzrdir = RemoteBzrDir(transport, _client=False)
 
336
        branch = RemoteBranch(bzrdir, None, _client=client)
 
337
        branch._ensure_real = lambda: None
 
338
        branch.lock_write()
 
339
        client._calls = []
 
340
 
 
341
        self.assertRaises(
 
342
            errors.NoSuchRevision, branch.set_revision_history, ['rev-id'])
 
343
        branch.unlock()
 
344
 
 
345
 
 
346
class TestBranchControlGetBranchConf(tests.TestCaseWithMemoryTransport):
 
347
    """Test branch.control_files api munging...
 
348
 
 
349
    We special case RemoteBranch.control_files.get('branch.conf') to
 
350
    call a specific API so that RemoteBranch's can intercept configuration
 
351
    file reading, allowing them to signal to the client about things like
 
352
    'email is configured for commits'.
 
353
    """
 
354
 
 
355
    def test_get_branch_conf(self):
 
356
        # in an empty branch we decode the response properly
 
357
        client = FakeClient([(('ok', ), 'config file body')])
 
358
        # we need to make a real branch because the remote_branch.control_files
 
359
        # will trigger _ensure_real.
 
360
        branch = self.make_branch('quack')
 
361
        transport = branch.bzrdir.root_transport
 
362
        # we do not want bzrdir to make any remote calls
 
363
        bzrdir = RemoteBzrDir(transport, _client=False)
 
364
        branch = RemoteBranch(bzrdir, None, _client=client)
 
365
        result = branch.control_files.get('branch.conf')
 
366
        self.assertEqual(
 
367
            [('call_expecting_body', 'Branch.get_config_file', ('///quack/',))],
 
368
            client._calls)
 
369
        self.assertEqual('config file body', result.read())
 
370
 
 
371
 
 
372
class TestBranchLockWrite(tests.TestCase):
 
373
 
 
374
    def test_lock_write_unlockable(self):
 
375
        client = FakeClient([(('UnlockableTransport', ), '')])
 
376
        transport = MemoryTransport()
 
377
        transport.mkdir('quack')
 
378
        transport = transport.clone('quack')
 
379
        # we do not want bzrdir to make any remote calls
 
380
        bzrdir = RemoteBzrDir(transport, _client=False)
 
381
        branch = RemoteBranch(bzrdir, None, _client=client)
 
382
        self.assertRaises(errors.UnlockableTransport, branch.lock_write)
 
383
        self.assertEqual(
 
384
            [('call', 'Branch.lock_write', ('///quack/', '', ''))],
 
385
            client._calls)
 
386
 
 
387
 
 
388
class TestTransportIsReadonly(tests.TestCase):
 
389
 
 
390
    def test_true(self):
 
391
        client = FakeClient([(('yes',), '')])
 
392
        transport = RemoteTransport('bzr://example.com/', medium=False,
 
393
                                    _client=client)
 
394
        self.assertEqual(True, transport.is_readonly())
 
395
        self.assertEqual(
 
396
            [('call', 'Transport.is_readonly', ())],
 
397
            client._calls)
 
398
 
 
399
    def test_false(self):
 
400
        client = FakeClient([(('no',), '')])
 
401
        transport = RemoteTransport('bzr://example.com/', medium=False,
 
402
                                    _client=client)
 
403
        self.assertEqual(False, transport.is_readonly())
 
404
        self.assertEqual(
 
405
            [('call', 'Transport.is_readonly', ())],
 
406
            client._calls)
 
407
 
 
408
    def test_error_from_old_server(self):
 
409
        """bzr 0.15 and earlier servers don't recognise the is_readonly verb.
 
410
        
 
411
        Clients should treat it as a "no" response, because is_readonly is only
 
412
        advisory anyway (a transport could be read-write, but then the
 
413
        underlying filesystem could be readonly anyway).
 
414
        """
 
415
        client = FakeClient([(
 
416
            ('error', "Generic bzr smart protocol error: "
 
417
                      "bad request 'Transport.is_readonly'"), '')])
 
418
        transport = RemoteTransport('bzr://example.com/', medium=False,
 
419
                                    _client=client)
 
420
        self.assertEqual(False, transport.is_readonly())
 
421
        self.assertEqual(
 
422
            [('call', 'Transport.is_readonly', ())],
 
423
            client._calls)
 
424
 
 
425
 
 
426
class TestRemoteRepository(tests.TestCase):
 
427
    """Base for testing RemoteRepository protocol usage.
 
428
    
 
429
    These tests contain frozen requests and responses.  We want any changes to 
 
430
    what is sent or expected to be require a thoughtful update to these tests
 
431
    because they might break compatibility with different-versioned servers.
 
432
    """
 
433
 
 
434
    def setup_fake_client_and_repository(self, responses, transport_path):
 
435
        """Create the fake client and repository for testing with.
 
436
        
 
437
        There's no real server here; we just have canned responses sent
 
438
        back one by one.
 
439
        
 
440
        :param transport_path: Path below the root of the MemoryTransport
 
441
            where the repository will be created.
 
442
        """
 
443
        client = FakeClient(responses)
 
444
        transport = MemoryTransport()
 
445
        transport.mkdir(transport_path)
 
446
        transport = transport.clone(transport_path)
 
447
        # we do not want bzrdir to make any remote calls
 
448
        bzrdir = RemoteBzrDir(transport, _client=False)
 
449
        repo = RemoteRepository(bzrdir, None, _client=client)
 
450
        return repo, client
 
451
 
 
452
 
 
453
class TestRepositoryGatherStats(TestRemoteRepository):
 
454
 
 
455
    def test_revid_none(self):
 
456
        # ('ok',), body with revisions and size
 
457
        responses = [(('ok', ), 'revisions: 2\nsize: 18\n')]
 
458
        transport_path = 'quack'
 
459
        repo, client = self.setup_fake_client_and_repository(
 
460
            responses, transport_path)
 
461
        result = repo.gather_stats(None)
 
462
        self.assertEqual(
 
463
            [('call_expecting_body', 'Repository.gather_stats',
 
464
             ('///quack/','','no'))],
 
465
            client._calls)
 
466
        self.assertEqual({'revisions': 2, 'size': 18}, result)
 
467
 
 
468
    def test_revid_no_committers(self):
 
469
        # ('ok',), body without committers
 
470
        responses = [(('ok', ),
 
471
                      'firstrev: 123456.300 3600\n'
 
472
                      'latestrev: 654231.400 0\n'
 
473
                      'revisions: 2\n'
 
474
                      'size: 18\n')]
 
475
        transport_path = 'quick'
 
476
        revid = u'\xc8'.encode('utf8')
 
477
        repo, client = self.setup_fake_client_and_repository(
 
478
            responses, transport_path)
 
479
        result = repo.gather_stats(revid)
 
480
        self.assertEqual(
 
481
            [('call_expecting_body', 'Repository.gather_stats',
 
482
              ('///quick/', revid, 'no'))],
 
483
            client._calls)
 
484
        self.assertEqual({'revisions': 2, 'size': 18,
 
485
                          'firstrev': (123456.300, 3600),
 
486
                          'latestrev': (654231.400, 0),},
 
487
                         result)
 
488
 
 
489
    def test_revid_with_committers(self):
 
490
        # ('ok',), body with committers
 
491
        responses = [(('ok', ),
 
492
                      'committers: 128\n'
 
493
                      'firstrev: 123456.300 3600\n'
 
494
                      'latestrev: 654231.400 0\n'
 
495
                      'revisions: 2\n'
 
496
                      'size: 18\n')]
 
497
        transport_path = 'buick'
 
498
        revid = u'\xc8'.encode('utf8')
 
499
        repo, client = self.setup_fake_client_and_repository(
 
500
            responses, transport_path)
 
501
        result = repo.gather_stats(revid, True)
 
502
        self.assertEqual(
 
503
            [('call_expecting_body', 'Repository.gather_stats',
 
504
              ('///buick/', revid, 'yes'))],
 
505
            client._calls)
 
506
        self.assertEqual({'revisions': 2, 'size': 18,
 
507
                          'committers': 128,
 
508
                          'firstrev': (123456.300, 3600),
 
509
                          'latestrev': (654231.400, 0),},
 
510
                         result)
 
511
 
 
512
 
 
513
class TestRepositoryGetRevisionGraph(TestRemoteRepository):
 
514
    
 
515
    def test_null_revision(self):
 
516
        # a null revision has the predictable result {}, we should have no wire
 
517
        # traffic when calling it with this argument
 
518
        responses = [(('notused', ), '')]
 
519
        transport_path = 'empty'
 
520
        repo, client = self.setup_fake_client_and_repository(
 
521
            responses, transport_path)
 
522
        result = repo.get_revision_graph(NULL_REVISION)
 
523
        self.assertEqual([], client._calls)
 
524
        self.assertEqual({}, result)
 
525
 
 
526
    def test_none_revision(self):
 
527
        # with none we want the entire graph
 
528
        r1 = u'\u0e33'.encode('utf8')
 
529
        r2 = u'\u0dab'.encode('utf8')
 
530
        lines = [' '.join([r2, r1]), r1]
 
531
        encoded_body = '\n'.join(lines)
 
532
 
 
533
        responses = [(('ok', ), encoded_body)]
 
534
        transport_path = 'sinhala'
 
535
        repo, client = self.setup_fake_client_and_repository(
 
536
            responses, transport_path)
 
537
        result = repo.get_revision_graph()
 
538
        self.assertEqual(
 
539
            [('call_expecting_body', 'Repository.get_revision_graph',
 
540
             ('///sinhala/', ''))],
 
541
            client._calls)
 
542
        self.assertEqual({r1: [], r2: [r1]}, result)
 
543
 
 
544
    def test_specific_revision(self):
 
545
        # with a specific revision we want the graph for that
 
546
        # with none we want the entire graph
 
547
        r11 = u'\u0e33'.encode('utf8')
 
548
        r12 = u'\xc9'.encode('utf8')
 
549
        r2 = u'\u0dab'.encode('utf8')
 
550
        lines = [' '.join([r2, r11, r12]), r11, r12]
 
551
        encoded_body = '\n'.join(lines)
 
552
 
 
553
        responses = [(('ok', ), encoded_body)]
 
554
        transport_path = 'sinhala'
 
555
        repo, client = self.setup_fake_client_and_repository(
 
556
            responses, transport_path)
 
557
        result = repo.get_revision_graph(r2)
 
558
        self.assertEqual(
 
559
            [('call_expecting_body', 'Repository.get_revision_graph',
 
560
             ('///sinhala/', r2))],
 
561
            client._calls)
 
562
        self.assertEqual({r11: [], r12: [], r2: [r11, r12], }, result)
 
563
 
 
564
    def test_no_such_revision(self):
 
565
        revid = '123'
 
566
        responses = [(('nosuchrevision', revid), '')]
 
567
        transport_path = 'sinhala'
 
568
        repo, client = self.setup_fake_client_and_repository(
 
569
            responses, transport_path)
 
570
        # also check that the right revision is reported in the error
 
571
        self.assertRaises(errors.NoSuchRevision,
 
572
            repo.get_revision_graph, revid)
 
573
        self.assertEqual(
 
574
            [('call_expecting_body', 'Repository.get_revision_graph',
 
575
             ('///sinhala/', revid))],
 
576
            client._calls)
 
577
 
 
578
        
 
579
class TestRepositoryIsShared(TestRemoteRepository):
 
580
 
 
581
    def test_is_shared(self):
 
582
        # ('yes', ) for Repository.is_shared -> 'True'.
 
583
        responses = [(('yes', ), )]
 
584
        transport_path = 'quack'
 
585
        repo, client = self.setup_fake_client_and_repository(
 
586
            responses, transport_path)
 
587
        result = repo.is_shared()
 
588
        self.assertEqual(
 
589
            [('call', 'Repository.is_shared', ('///quack/',))],
 
590
            client._calls)
 
591
        self.assertEqual(True, result)
 
592
 
 
593
    def test_is_not_shared(self):
 
594
        # ('no', ) for Repository.is_shared -> 'False'.
 
595
        responses = [(('no', ), )]
 
596
        transport_path = 'qwack'
 
597
        repo, client = self.setup_fake_client_and_repository(
 
598
            responses, transport_path)
 
599
        result = repo.is_shared()
 
600
        self.assertEqual(
 
601
            [('call', 'Repository.is_shared', ('///qwack/',))],
 
602
            client._calls)
 
603
        self.assertEqual(False, result)
 
604
 
 
605
 
 
606
class TestRepositoryLockWrite(TestRemoteRepository):
 
607
 
 
608
    def test_lock_write(self):
 
609
        responses = [(('ok', 'a token'), '')]
 
610
        transport_path = 'quack'
 
611
        repo, client = self.setup_fake_client_and_repository(
 
612
            responses, transport_path)
 
613
        result = repo.lock_write()
 
614
        self.assertEqual(
 
615
            [('call', 'Repository.lock_write', ('///quack/', ''))],
 
616
            client._calls)
 
617
        self.assertEqual('a token', result)
 
618
 
 
619
    def test_lock_write_already_locked(self):
 
620
        responses = [(('LockContention', ), '')]
 
621
        transport_path = 'quack'
 
622
        repo, client = self.setup_fake_client_and_repository(
 
623
            responses, transport_path)
 
624
        self.assertRaises(errors.LockContention, repo.lock_write)
 
625
        self.assertEqual(
 
626
            [('call', 'Repository.lock_write', ('///quack/', ''))],
 
627
            client._calls)
 
628
 
 
629
    def test_lock_write_unlockable(self):
 
630
        responses = [(('UnlockableTransport', ), '')]
 
631
        transport_path = 'quack'
 
632
        repo, client = self.setup_fake_client_and_repository(
 
633
            responses, transport_path)
 
634
        self.assertRaises(errors.UnlockableTransport, repo.lock_write)
 
635
        self.assertEqual(
 
636
            [('call', 'Repository.lock_write', ('///quack/', ''))],
 
637
            client._calls)
 
638
 
 
639
 
 
640
class TestRepositoryUnlock(TestRemoteRepository):
 
641
 
 
642
    def test_unlock(self):
 
643
        responses = [(('ok', 'a token'), ''),
 
644
                     (('ok',), '')]
 
645
        transport_path = 'quack'
 
646
        repo, client = self.setup_fake_client_and_repository(
 
647
            responses, transport_path)
 
648
        repo.lock_write()
 
649
        repo.unlock()
 
650
        self.assertEqual(
 
651
            [('call', 'Repository.lock_write', ('///quack/', '')),
 
652
             ('call', 'Repository.unlock', ('///quack/', 'a token'))],
 
653
            client._calls)
 
654
 
 
655
    def test_unlock_wrong_token(self):
 
656
        # If somehow the token is wrong, unlock will raise TokenMismatch.
 
657
        responses = [(('ok', 'a token'), ''),
 
658
                     (('TokenMismatch',), '')]
 
659
        transport_path = 'quack'
 
660
        repo, client = self.setup_fake_client_and_repository(
 
661
            responses, transport_path)
 
662
        repo.lock_write()
 
663
        self.assertRaises(errors.TokenMismatch, repo.unlock)
 
664
 
 
665
 
 
666
class TestRepositoryHasRevision(TestRemoteRepository):
 
667
 
 
668
    def test_none(self):
 
669
        # repo.has_revision(None) should not cause any traffic.
 
670
        transport_path = 'quack'
 
671
        responses = None
 
672
        repo, client = self.setup_fake_client_and_repository(
 
673
            responses, transport_path)
 
674
 
 
675
        # The null revision is always there, so has_revision(None) == True.
 
676
        self.assertEqual(True, repo.has_revision(None))
 
677
 
 
678
        # The remote repo shouldn't be accessed.
 
679
        self.assertEqual([], client._calls)
 
680
 
 
681
 
 
682
class TestRepositoryTarball(TestRemoteRepository):
 
683
 
 
684
    # This is a canned tarball reponse we can validate against
 
685
    tarball_content = (
 
686
        'QlpoOTFBWSZTWdGkj3wAAWF/k8aQACBIB//A9+8cIX/v33AACEAYABAECEACNz'
 
687
        'JqsgJJFPTSnk1A3qh6mTQAAAANPUHkagkSTEkaA09QaNAAAGgAAAcwCYCZGAEY'
 
688
        'mJhMJghpiaYBUkKammSHqNMZQ0NABkNAeo0AGneAevnlwQoGzEzNVzaYxp/1Uk'
 
689
        'xXzA1CQX0BJMZZLcPBrluJir5SQyijWHYZ6ZUtVqqlYDdB2QoCwa9GyWwGYDMA'
 
690
        'OQYhkpLt/OKFnnlT8E0PmO8+ZNSo2WWqeCzGB5fBXZ3IvV7uNJVE7DYnWj6qwB'
 
691
        'k5DJDIrQ5OQHHIjkS9KqwG3mc3t+F1+iujb89ufyBNIKCgeZBWrl5cXxbMGoMs'
 
692
        'c9JuUkg5YsiVcaZJurc6KLi6yKOkgCUOlIlOpOoXyrTJjK8ZgbklReDdwGmFgt'
 
693
        'dkVsAIslSVCd4AtACSLbyhLHryfb14PKegrVDba+U8OL6KQtzdM5HLjAc8/p6n'
 
694
        '0lgaWU8skgO7xupPTkyuwheSckejFLK5T4ZOo0Gda9viaIhpD1Qn7JqqlKAJqC'
 
695
        'QplPKp2nqBWAfwBGaOwVrz3y1T+UZZNismXHsb2Jq18T+VaD9k4P8DqE3g70qV'
 
696
        'JLurpnDI6VS5oqDDPVbtVjMxMxMg4rzQVipn2Bv1fVNK0iq3Gl0hhnnHKm/egy'
 
697
        'nWQ7QH/F3JFOFCQ0aSPfA='
 
698
        ).decode('base64')
 
699
 
 
700
    def test_repository_tarball(self):
 
701
        # Test that Repository.tarball generates the right operations
 
702
        transport_path = 'repo'
 
703
        expected_responses = [(('ok',), self.tarball_content),
 
704
            ]
 
705
        expected_calls = [('call_expecting_body', 'Repository.tarball',
 
706
                           ('///repo/', 'bz2',),),
 
707
            ]
 
708
        remote_repo, client = self.setup_fake_client_and_repository(
 
709
            expected_responses, transport_path)
 
710
        # Now actually ask for the tarball
 
711
        tarball_file = remote_repo._get_tarball('bz2')
 
712
        try:
 
713
            self.assertEqual(expected_calls, client._calls)
 
714
            self.assertEqual(self.tarball_content, tarball_file.read())
 
715
        finally:
 
716
            tarball_file.close()
 
717
 
 
718
    def test_sprout_uses_tarball(self):
 
719
        # RemoteRepository.sprout should try to use the
 
720
        # tarball command rather than accessing all the files
 
721
        transport_path = 'srcrepo'
 
722
        expected_responses = [(('ok',), self.tarball_content),
 
723
            ]
 
724
        expected_calls = [('call2', 'Repository.tarball', ('///srcrepo/', 'bz2',),),
 
725
            ]
 
726
        remote_repo, client = self.setup_fake_client_and_repository(
 
727
            expected_responses, transport_path)
 
728
        # make a regular local repository to receive the results
 
729
        dest_transport = MemoryTransport()
 
730
        dest_transport.mkdir('destrepo')
 
731
        bzrdir_format = bzrdir.format_registry.make_bzrdir('default')
 
732
        dest_bzrdir = bzrdir_format.initialize_on_transport(dest_transport)
 
733
        # try to copy...
 
734
        remote_repo.sprout(dest_bzrdir)
 
735
 
 
736
 
 
737
class TestRemoteRepositoryCopyContent(tests.TestCaseWithTransport):
 
738
    """RemoteRepository.copy_content_into optimizations"""
 
739
 
 
740
    def test_copy_content_remote_to_local(self):
 
741
        self.transport_server = server.SmartTCPServer_for_testing
 
742
        src_repo = self.make_repository('repo1')
 
743
        src_repo = repository.Repository.open(self.get_url('repo1'))
 
744
        # At the moment the tarball-based copy_content_into can't write back
 
745
        # into a smart server.  It would be good if it could upload the
 
746
        # tarball; once that works we'd have to create repositories of
 
747
        # different formats. -- mbp 20070410
 
748
        dest_url = self.get_vfs_only_url('repo2')
 
749
        dest_bzrdir = BzrDir.create(dest_url)
 
750
        dest_repo = dest_bzrdir.create_repository()
 
751
        self.assertFalse(isinstance(dest_repo, RemoteRepository))
 
752
        self.assertTrue(isinstance(src_repo, RemoteRepository))
 
753
        src_repo.copy_content_into(dest_repo)