~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_remote.py

  • Committer: John Arbash Meinel
  • Date: 2008-09-24 12:53:22 UTC
  • mfrom: (3697.7.3 1.7)
  • mto: (3697.7.4 1.7)
  • mto: This revision was merged to the branch mainline in revision 3748.
  • Revision ID: john@arbash-meinel.com-20080924125322-p1i7pnr0m49v1hze
Bring in the 1.7 branch

Show diffs side-by-side

added added

removed removed

Lines of Context:
33
33
    remote,
34
34
    repository,
35
35
    tests,
 
36
    urlutils,
36
37
    )
37
38
from bzrlib.branch import Branch
38
39
from bzrlib.bzrdir import BzrDir, BzrDirFormat
106
107
        self.assertStartsWith(str(b), 'RemoteBranch(')
107
108
 
108
109
 
 
110
class FakeRemoteTransport(object):
 
111
    """This class provides the minimum support for use in place of a RemoteTransport.
 
112
    
 
113
    It doesn't actually transmit requests, but rather expects them to be
 
114
    handled by a FakeClient which holds canned responses.  It does not allow
 
115
    any vfs access, therefore is not suitable for testing any operation that
 
116
    will fallback to vfs access.  Backing the test by an instance of this
 
117
    class guarantees that it's - done using non-vfs operations.
 
118
    """
 
119
 
 
120
    _default_url = 'fakeremotetransport://host/path/'
 
121
 
 
122
    def __init__(self, url=None):
 
123
        if url is None:
 
124
            url = self._default_url
 
125
        self.base = url
 
126
 
 
127
    def __repr__(self):
 
128
        return "%r(%r)" % (self.__class__.__name__,
 
129
            self.base)
 
130
 
 
131
    def clone(self, relpath):
 
132
        return FakeRemoteTransport(urlutils.join(self.base, relpath))
 
133
 
 
134
    def get(self, relpath):
 
135
        # only get is specifically stubbed out, because it's usually the first
 
136
        # thing we do.  anything else will fail with an AttributeError.
 
137
        raise AssertionError("%r doesn't support file access to %r"
 
138
            % (self, relpath))
 
139
 
 
140
 
 
141
 
109
142
class FakeProtocol(object):
110
143
    """Lookalike SmartClientRequestProtocolOne allowing body reading tests."""
111
144
 
144
177
        self.responses = []
145
178
        self._calls = []
146
179
        self.expecting_body = False
 
180
        # if non-None, this is the list of expected calls, with only the
 
181
        # method name and arguments included.  the body might be hard to
 
182
        # compute so is not included
 
183
        self._expected_calls = None
147
184
        _SmartClient.__init__(self, FakeMedium(self._calls, fake_medium_base))
148
185
 
 
186
    def add_expected_call(self, call_name, call_args, response_type,
 
187
        response_args, response_body=None):
 
188
        if self._expected_calls is None:
 
189
            self._expected_calls = []
 
190
        self._expected_calls.append((call_name, call_args))
 
191
        self.responses.append((response_type, response_args, response_body))
 
192
 
149
193
    def add_success_response(self, *args):
150
194
        self.responses.append(('success', args, None))
151
195
 
158
202
    def add_unknown_method_response(self, verb):
159
203
        self.responses.append(('unknown', verb))
160
204
 
 
205
    def finished_test(self):
 
206
        if self._expected_calls:
 
207
            raise AssertionError("%r finished but was still expecting %r"
 
208
                % (self, self._expected_calls[0]))
 
209
 
161
210
    def _get_next_response(self):
162
 
        response_tuple = self.responses.pop(0)
 
211
        try:
 
212
            response_tuple = self.responses.pop(0)
 
213
        except IndexError, e:
 
214
            raise AssertionError("%r didn't expect any more calls"
 
215
                % (self,))
163
216
        if response_tuple[0] == 'unknown':
164
217
            raise errors.UnknownSmartMethod(response_tuple[1])
165
218
        elif response_tuple[0] == 'error':
166
219
            raise errors.ErrorFromSmartServer(response_tuple[1])
167
220
        return response_tuple
168
221
 
 
222
    def _check_call(self, method, args):
 
223
        if self._expected_calls is None:
 
224
            # the test should be updated to say what it expects
 
225
            return
 
226
        try:
 
227
            next_call = self._expected_calls.pop(0)
 
228
        except IndexError:
 
229
            raise AssertionError("%r didn't expect any more calls "
 
230
                "but got %r%r"
 
231
                % (self, method, args,))
 
232
        if method != next_call[0] or args != next_call[1]:
 
233
            raise AssertionError("%r expected %r%r "
 
234
                "but got %r%r"
 
235
                % (self, next_call[0], next_call[1], method, args,))
 
236
 
169
237
    def call(self, method, *args):
 
238
        self._check_call(method, args)
170
239
        self._calls.append(('call', method, args))
171
240
        return self._get_next_response()[1]
172
241
 
173
242
    def call_expecting_body(self, method, *args):
 
243
        self._check_call(method, args)
174
244
        self._calls.append(('call_expecting_body', method, args))
175
245
        result = self._get_next_response()
176
246
        self.expecting_body = True
177
247
        return result[1], FakeProtocol(result[2], self)
178
248
 
179
249
    def call_with_body_bytes_expecting_body(self, method, args, body):
 
250
        self._check_call(method, args)
180
251
        self._calls.append(('call_with_body_bytes_expecting_body', method,
181
252
            args, body))
182
253
        result = self._get_next_response()
288
359
        transport.mkdir('quack')
289
360
        transport = transport.clone('quack')
290
361
        client = FakeClient(transport.base)
291
 
        client.add_success_response('ok', '')
292
 
        client.add_success_response('ok', '', 'no', 'no', 'no')
 
362
        client.add_expected_call(
 
363
            'BzrDir.open_branch', ('quack/',),
 
364
            'success', ('ok', ''))
 
365
        client.add_expected_call(
 
366
            'BzrDir.find_repositoryV2', ('quack/',),
 
367
            'success', ('ok', '', 'no', 'no', 'no'))
 
368
        client.add_expected_call(
 
369
            'Branch.get_stacked_on_url', ('quack/',),
 
370
            'error', ('NotStacked',))
293
371
        bzrdir = RemoteBzrDir(transport, _client=client)
294
372
        result = bzrdir.open_branch()
295
 
        self.assertEqual(
296
 
            [('call', 'BzrDir.open_branch', ('quack/',)),
297
 
             ('call', 'BzrDir.find_repositoryV2', ('quack/',))],
298
 
            client._calls)
299
373
        self.assertIsInstance(result, RemoteBranch)
300
374
        self.assertEqual(bzrdir, result.bzrdir)
 
375
        client.finished_test()
301
376
 
302
377
    def test_branch_missing(self):
303
378
        transport = MemoryTransport()
333
408
        # transmitted as "~", not "%7E".
334
409
        transport = RemoteTCPTransport('bzr://localhost/~hello/')
335
410
        client = FakeClient(transport.base)
336
 
        client.add_success_response('ok', '')
337
 
        client.add_success_response('ok', '', 'no', 'no', 'no')
 
411
        client.add_expected_call(
 
412
            'BzrDir.open_branch', ('~hello/',),
 
413
            'success', ('ok', ''))
 
414
        client.add_expected_call(
 
415
            'BzrDir.find_repositoryV2', ('~hello/',),
 
416
            'success', ('ok', '', 'no', 'no', 'no'))
 
417
        client.add_expected_call(
 
418
            'Branch.get_stacked_on_url', ('~hello/',),
 
419
            'error', ('NotStacked',))
338
420
        bzrdir = RemoteBzrDir(transport, _client=client)
339
421
        result = bzrdir.open_branch()
340
 
        self.assertEqual(
341
 
            [('call', 'BzrDir.open_branch', ('~hello/',)),
342
 
             ('call', 'BzrDir.find_repositoryV2', ('~hello/',))],
343
 
            client._calls)
 
422
        client.finished_test()
344
423
 
345
424
    def check_open_repository(self, rich_root, subtrees, external_lookup='no'):
346
425
        transport = MemoryTransport()
427
506
        return OldSmartClient()
428
507
 
429
508
 
430
 
class TestBranchLastRevisionInfo(tests.TestCase):
 
509
class RemoteBranchTestCase(tests.TestCase):
 
510
 
 
511
    def make_remote_branch(self, transport, client):
 
512
        """Make a RemoteBranch using 'client' as its _SmartClient.
 
513
        
 
514
        A RemoteBzrDir and RemoteRepository will also be created to fill out
 
515
        the RemoteBranch, albeit with stub values for some of their attributes.
 
516
        """
 
517
        # we do not want bzrdir to make any remote calls, so use False as its
 
518
        # _client.  If it tries to make a remote call, this will fail
 
519
        # immediately.
 
520
        bzrdir = RemoteBzrDir(transport, _client=False)
 
521
        repo = RemoteRepository(bzrdir, None, _client=client)
 
522
        return RemoteBranch(bzrdir, repo, _client=client)
 
523
 
 
524
 
 
525
class TestBranchLastRevisionInfo(RemoteBranchTestCase):
431
526
 
432
527
    def test_empty_branch(self):
433
528
        # in an empty branch we decode the response properly
434
529
        transport = MemoryTransport()
435
530
        client = FakeClient(transport.base)
436
 
        client.add_success_response('ok', '0', 'null:')
 
531
        client.add_expected_call(
 
532
            'Branch.get_stacked_on_url', ('quack/',),
 
533
            'error', ('NotStacked',))
 
534
        client.add_expected_call(
 
535
            'Branch.last_revision_info', ('quack/',),
 
536
            'success', ('ok', '0', 'null:'))
437
537
        transport.mkdir('quack')
438
538
        transport = transport.clone('quack')
439
 
        # we do not want bzrdir to make any remote calls
440
 
        bzrdir = RemoteBzrDir(transport, _client=False)
441
 
        branch = RemoteBranch(bzrdir, None, _client=client)
 
539
        branch = self.make_remote_branch(transport, client)
442
540
        result = branch.last_revision_info()
443
 
 
444
 
        self.assertEqual(
445
 
            [('call', 'Branch.last_revision_info', ('quack/',))],
446
 
            client._calls)
 
541
        client.finished_test()
447
542
        self.assertEqual((0, NULL_REVISION), result)
448
543
 
449
544
    def test_non_empty_branch(self):
451
546
        revid = u'\xc8'.encode('utf8')
452
547
        transport = MemoryTransport()
453
548
        client = FakeClient(transport.base)
454
 
        client.add_success_response('ok', '2', revid)
 
549
        client.add_expected_call(
 
550
            'Branch.get_stacked_on_url', ('kwaak/',),
 
551
            'error', ('NotStacked',))
 
552
        client.add_expected_call(
 
553
            'Branch.last_revision_info', ('kwaak/',),
 
554
            'success', ('ok', '2', revid))
455
555
        transport.mkdir('kwaak')
456
556
        transport = transport.clone('kwaak')
457
 
        # we do not want bzrdir to make any remote calls
458
 
        bzrdir = RemoteBzrDir(transport, _client=False)
459
 
        branch = RemoteBranch(bzrdir, None, _client=client)
 
557
        branch = self.make_remote_branch(transport, client)
460
558
        result = branch.last_revision_info()
461
 
 
462
 
        self.assertEqual(
463
 
            [('call', 'Branch.last_revision_info', ('kwaak/',))],
464
 
            client._calls)
465
559
        self.assertEqual((2, revid), result)
466
560
 
467
561
 
468
 
class TestBranchSetLastRevision(tests.TestCase):
 
562
class TestBranch_get_stacked_on_url(tests.TestCaseWithMemoryTransport):
 
563
    """Test Branch._get_stacked_on_url rpc"""
 
564
 
 
565
    def test_get_stacked_on_invalid_url(self):
 
566
        raise tests.KnownFailure('opening a branch requires the server to open the fallback repository')
 
567
        transport = FakeRemoteTransport('fakeremotetransport:///')
 
568
        client = FakeClient(transport.base)
 
569
        client.add_expected_call(
 
570
            'Branch.get_stacked_on_url', ('.',),
 
571
            'success', ('ok', 'file:///stacked/on'))
 
572
        bzrdir = RemoteBzrDir(transport, _client=client)
 
573
        branch = RemoteBranch(bzrdir, None, _client=client)
 
574
        result = branch.get_stacked_on_url()
 
575
        self.assertEqual(
 
576
            'file:///stacked/on', result)
 
577
 
 
578
    def test_backwards_compatible(self):
 
579
        # like with bzr1.6 with no Branch.get_stacked_on_url rpc
 
580
        base_branch = self.make_branch('base', format='1.6')
 
581
        stacked_branch = self.make_branch('stacked', format='1.6')
 
582
        stacked_branch.set_stacked_on_url('../base')
 
583
        client = FakeClient(self.get_url())
 
584
        client.add_expected_call(
 
585
            'BzrDir.open_branch', ('stacked/',),
 
586
            'success', ('ok', ''))
 
587
        client.add_expected_call(
 
588
            'BzrDir.find_repositoryV2', ('stacked/',),
 
589
            'success', ('ok', '', 'no', 'no', 'no'))
 
590
        # called twice, once from constructor and then again by us
 
591
        client.add_expected_call(
 
592
            'Branch.get_stacked_on_url', ('stacked/',),
 
593
            'unknown', ('Branch.get_stacked_on_url',))
 
594
        client.add_expected_call(
 
595
            'Branch.get_stacked_on_url', ('stacked/',),
 
596
            'unknown', ('Branch.get_stacked_on_url',))
 
597
        # this will also do vfs access, but that goes direct to the transport
 
598
        # and isn't seen by the FakeClient.
 
599
        bzrdir = RemoteBzrDir(self.get_transport('stacked'), _client=client)
 
600
        branch = bzrdir.open_branch()
 
601
        result = branch.get_stacked_on_url()
 
602
        self.assertEqual('../base', result)
 
603
        client.finished_test()
 
604
        # it's in the fallback list both for the RemoteRepository and its vfs
 
605
        # repository
 
606
        self.assertEqual(1, len(branch.repository._fallback_repositories))
 
607
        self.assertEqual(1,
 
608
            len(branch.repository._real_repository._fallback_repositories))
 
609
 
 
610
    def test_get_stacked_on_real_branch(self):
 
611
        base_branch = self.make_branch('base', format='1.6')
 
612
        stacked_branch = self.make_branch('stacked', format='1.6')
 
613
        stacked_branch.set_stacked_on_url('../base')
 
614
        client = FakeClient(self.get_url())
 
615
        client.add_expected_call(
 
616
            'BzrDir.open_branch', ('stacked/',),
 
617
            'success', ('ok', ''))
 
618
        client.add_expected_call(
 
619
            'BzrDir.find_repositoryV2', ('stacked/',),
 
620
            'success', ('ok', '', 'no', 'no', 'no'))
 
621
        # called twice, once from constructor and then again by us
 
622
        client.add_expected_call(
 
623
            'Branch.get_stacked_on_url', ('stacked/',),
 
624
            'success', ('ok', '../base'))
 
625
        client.add_expected_call(
 
626
            'Branch.get_stacked_on_url', ('stacked/',),
 
627
            'success', ('ok', '../base'))
 
628
        bzrdir = RemoteBzrDir(self.get_transport('stacked'), _client=client)
 
629
        branch = bzrdir.open_branch()
 
630
        result = branch.get_stacked_on_url()
 
631
        self.assertEqual('../base', result)
 
632
        client.finished_test()
 
633
        # it's in the fallback list both for the RemoteRepository and its vfs
 
634
        # repository
 
635
        self.assertEqual(1, len(branch.repository._fallback_repositories))
 
636
        self.assertEqual(1,
 
637
            len(branch.repository._real_repository._fallback_repositories))
 
638
 
 
639
 
 
640
class TestBranchSetLastRevision(RemoteBranchTestCase):
469
641
 
470
642
    def test_set_empty(self):
471
643
        # set_revision_history([]) is translated to calling
475
647
        transport = transport.clone('branch')
476
648
 
477
649
        client = FakeClient(transport.base)
478
 
        # lock_write
479
 
        client.add_success_response('ok', 'branch token', 'repo token')
480
 
        # set_last_revision
481
 
        client.add_success_response('ok')
482
 
        # unlock
483
 
        client.add_success_response('ok')
484
 
        bzrdir = RemoteBzrDir(transport, _client=False)
485
 
        branch = RemoteBranch(bzrdir, None, _client=client)
 
650
        client.add_expected_call(
 
651
            'Branch.get_stacked_on_url', ('branch/',),
 
652
            'error', ('NotStacked',))
 
653
        client.add_expected_call(
 
654
            'Branch.lock_write', ('branch/', '', ''),
 
655
            'success', ('ok', 'branch token', 'repo token'))
 
656
        client.add_expected_call(
 
657
            'Branch.set_last_revision', ('branch/', 'branch token', 'repo token', 'null:',),
 
658
            'success', ('ok',))
 
659
        client.add_expected_call(
 
660
            'Branch.unlock', ('branch/', 'branch token', 'repo token'),
 
661
            'success', ('ok',))
 
662
        branch = self.make_remote_branch(transport, client)
486
663
        # This is a hack to work around the problem that RemoteBranch currently
487
664
        # unnecessarily invokes _ensure_real upon a call to lock_write.
488
665
        branch._ensure_real = lambda: None
489
666
        branch.lock_write()
490
 
        client._calls = []
491
667
        result = branch.set_revision_history([])
492
 
        self.assertEqual(
493
 
            [('call', 'Branch.set_last_revision',
494
 
                ('branch/', 'branch token', 'repo token', 'null:'))],
495
 
            client._calls)
496
668
        branch.unlock()
497
669
        self.assertEqual(None, result)
 
670
        client.finished_test()
498
671
 
499
672
    def test_set_nonempty(self):
500
673
        # set_revision_history([rev-id1, ..., rev-idN]) is translated to calling
504
677
        transport = transport.clone('branch')
505
678
 
506
679
        client = FakeClient(transport.base)
507
 
        # lock_write
508
 
        client.add_success_response('ok', 'branch token', 'repo token')
509
 
        # set_last_revision
510
 
        client.add_success_response('ok')
511
 
        # unlock
512
 
        client.add_success_response('ok')
513
 
        bzrdir = RemoteBzrDir(transport, _client=False)
514
 
        branch = RemoteBranch(bzrdir, None, _client=client)
 
680
        client.add_expected_call(
 
681
            'Branch.get_stacked_on_url', ('branch/',),
 
682
            'error', ('NotStacked',))
 
683
        client.add_expected_call(
 
684
            'Branch.lock_write', ('branch/', '', ''),
 
685
            'success', ('ok', 'branch token', 'repo token'))
 
686
        client.add_expected_call(
 
687
            'Branch.set_last_revision', ('branch/', 'branch token', 'repo token', 'rev-id2',),
 
688
            'success', ('ok',))
 
689
        client.add_expected_call(
 
690
            'Branch.unlock', ('branch/', 'branch token', 'repo token'),
 
691
            'success', ('ok',))
 
692
        branch = self.make_remote_branch(transport, client)
515
693
        # This is a hack to work around the problem that RemoteBranch currently
516
694
        # unnecessarily invokes _ensure_real upon a call to lock_write.
517
695
        branch._ensure_real = lambda: None
518
696
        # Lock the branch, reset the record of remote calls.
519
697
        branch.lock_write()
520
 
        client._calls = []
521
 
 
522
698
        result = branch.set_revision_history(['rev-id1', 'rev-id2'])
523
 
        self.assertEqual(
524
 
            [('call', 'Branch.set_last_revision',
525
 
                ('branch/', 'branch token', 'repo token', 'rev-id2'))],
526
 
            client._calls)
527
699
        branch.unlock()
528
700
        self.assertEqual(None, result)
 
701
        client.finished_test()
529
702
 
530
703
    def test_no_such_revision(self):
531
704
        transport = MemoryTransport()
533
706
        transport = transport.clone('branch')
534
707
        # A response of 'NoSuchRevision' is translated into an exception.
535
708
        client = FakeClient(transport.base)
536
 
        # lock_write
537
 
        client.add_success_response('ok', 'branch token', 'repo token')
538
 
        # set_last_revision
539
 
        client.add_error_response('NoSuchRevision', 'rev-id')
540
 
        # unlock
541
 
        client.add_success_response('ok')
 
709
        client.add_expected_call(
 
710
            'Branch.get_stacked_on_url', ('branch/',),
 
711
            'error', ('NotStacked',))
 
712
        client.add_expected_call(
 
713
            'Branch.lock_write', ('branch/', '', ''),
 
714
            'success', ('ok', 'branch token', 'repo token'))
 
715
        client.add_expected_call(
 
716
            'Branch.set_last_revision', ('branch/', 'branch token', 'repo token', 'rev-id',),
 
717
            'error', ('NoSuchRevision', 'rev-id'))
 
718
        client.add_expected_call(
 
719
            'Branch.unlock', ('branch/', 'branch token', 'repo token'),
 
720
            'success', ('ok',))
542
721
 
543
 
        bzrdir = RemoteBzrDir(transport, _client=False)
544
 
        repo = RemoteRepository(bzrdir, None, _client=client)
545
 
        branch = RemoteBranch(bzrdir, repo, _client=client)
546
 
        branch._ensure_real = lambda: None
 
722
        branch = self.make_remote_branch(transport, client)
547
723
        branch.lock_write()
548
 
        client._calls = []
549
 
 
550
724
        self.assertRaises(
551
725
            errors.NoSuchRevision, branch.set_revision_history, ['rev-id'])
552
726
        branch.unlock()
 
727
        client.finished_test()
553
728
 
554
729
    def test_tip_change_rejected(self):
555
730
        """TipChangeRejected responses cause a TipChangeRejected exception to
559
734
        transport.mkdir('branch')
560
735
        transport = transport.clone('branch')
561
736
        client = FakeClient(transport.base)
562
 
        # lock_write
563
 
        client.add_success_response('ok', 'branch token', 'repo token')
564
 
        # set_last_revision
565
737
        rejection_msg_unicode = u'rejection message\N{INTERROBANG}'
566
738
        rejection_msg_utf8 = rejection_msg_unicode.encode('utf8')
567
 
        client.add_error_response('TipChangeRejected', rejection_msg_utf8)
568
 
        # unlock
569
 
        client.add_success_response('ok')
570
 
 
571
 
        bzrdir = RemoteBzrDir(transport, _client=False)
572
 
        repo = RemoteRepository(bzrdir, None, _client=client)
573
 
        branch = RemoteBranch(bzrdir, repo, _client=client)
 
739
        client.add_expected_call(
 
740
            'Branch.get_stacked_on_url', ('branch/',),
 
741
            'error', ('NotStacked',))
 
742
        client.add_expected_call(
 
743
            'Branch.lock_write', ('branch/', '', ''),
 
744
            'success', ('ok', 'branch token', 'repo token'))
 
745
        client.add_expected_call(
 
746
            'Branch.set_last_revision', ('branch/', 'branch token', 'repo token', 'rev-id',),
 
747
            'error', ('TipChangeRejected', rejection_msg_utf8))
 
748
        client.add_expected_call(
 
749
            'Branch.unlock', ('branch/', 'branch token', 'repo token'),
 
750
            'success', ('ok',))
 
751
        branch = self.make_remote_branch(transport, client)
574
752
        branch._ensure_real = lambda: None
575
753
        branch.lock_write()
576
754
        self.addCleanup(branch.unlock)
577
 
        client._calls = []
578
 
 
579
755
        # The 'TipChangeRejected' error response triggered by calling
580
756
        # set_revision_history causes a TipChangeRejected exception.
581
757
        err = self.assertRaises(
584
760
        # object.
585
761
        self.assertIsInstance(err.msg, unicode)
586
762
        self.assertEqual(rejection_msg_unicode, err.msg)
587
 
 
588
 
 
589
 
class TestBranchSetLastRevisionInfo(tests.TestCase):
 
763
        branch.unlock()
 
764
        client.finished_test()
 
765
 
 
766
 
 
767
class TestBranchSetLastRevisionInfo(RemoteBranchTestCase):
590
768
 
591
769
    def test_set_last_revision_info(self):
592
770
        # set_last_revision_info(num, 'rev-id') is translated to calling
595
773
        transport.mkdir('branch')
596
774
        transport = transport.clone('branch')
597
775
        client = FakeClient(transport.base)
 
776
        # get_stacked_on_url
 
777
        client.add_error_response('NotStacked')
598
778
        # lock_write
599
779
        client.add_success_response('ok', 'branch token', 'repo token')
600
780
        # set_last_revision
602
782
        # unlock
603
783
        client.add_success_response('ok')
604
784
 
605
 
        bzrdir = RemoteBzrDir(transport, _client=False)
606
 
        branch = RemoteBranch(bzrdir, None, _client=client)
607
 
        # This is a hack to work around the problem that RemoteBranch currently
608
 
        # unnecessarily invokes _ensure_real upon a call to lock_write.
609
 
        branch._ensure_real = lambda: None
 
785
        branch = self.make_remote_branch(transport, client)
610
786
        # Lock the branch, reset the record of remote calls.
611
787
        branch.lock_write()
612
788
        client._calls = []
624
800
        transport.mkdir('branch')
625
801
        transport = transport.clone('branch')
626
802
        client = FakeClient(transport.base)
 
803
        # get_stacked_on_url
 
804
        client.add_error_response('NotStacked')
627
805
        # lock_write
628
806
        client.add_success_response('ok', 'branch token', 'repo token')
629
807
        # set_last_revision
631
809
        # unlock
632
810
        client.add_success_response('ok')
633
811
 
634
 
        bzrdir = RemoteBzrDir(transport, _client=False)
635
 
        repo = RemoteRepository(bzrdir, None, _client=client)
636
 
        branch = RemoteBranch(bzrdir, repo, _client=client)
637
 
        # This is a hack to work around the problem that RemoteBranch currently
638
 
        # unnecessarily invokes _ensure_real upon a call to lock_write.
639
 
        branch._ensure_real = lambda: None
 
812
        branch = self.make_remote_branch(transport, client)
640
813
        # Lock the branch, reset the record of remote calls.
641
814
        branch.lock_write()
642
815
        client._calls = []
651
824
        branch._lock_count = 2
652
825
        branch._lock_token = 'branch token'
653
826
        branch._repo_lock_token = 'repo token'
 
827
        branch.repository._lock_mode = 'w'
 
828
        branch.repository._lock_count = 2
 
829
        branch.repository._lock_token = 'repo token'
654
830
 
655
831
    def test_backwards_compatibility(self):
656
832
        """If the server does not support the Branch.set_last_revision_info
668
844
        transport.mkdir('branch')
669
845
        transport = transport.clone('branch')
670
846
        client = FakeClient(transport.base)
671
 
        client.add_unknown_method_response('Branch.set_last_revision_info')
672
 
        bzrdir = RemoteBzrDir(transport, _client=False)
673
 
        branch = RemoteBranch(bzrdir, None, _client=client)
 
847
        client.add_expected_call(
 
848
            'Branch.get_stacked_on_url', ('branch/',),
 
849
            'error', ('NotStacked',))
 
850
        client.add_expected_call(
 
851
            'Branch.set_last_revision_info',
 
852
            ('branch/', 'branch token', 'repo token', '1234', 'a-revision-id',),
 
853
            'unknown', 'Branch.set_last_revision_info')
 
854
 
 
855
        branch = self.make_remote_branch(transport, client)
674
856
        class StubRealBranch(object):
675
857
            def __init__(self):
676
858
                self.calls = []
686
868
        # Call set_last_revision_info, and verify it behaved as expected.
687
869
        result = branch.set_last_revision_info(1234, 'a-revision-id')
688
870
        self.assertEqual(
689
 
            [('call', 'Branch.set_last_revision_info',
690
 
                ('branch/', 'branch token', 'repo token',
691
 
                 '1234', 'a-revision-id')),],
692
 
            client._calls)
693
 
        self.assertEqual(
694
871
            [('set_last_revision_info', 1234, 'a-revision-id')],
695
872
            real_branch.calls)
 
873
        client.finished_test()
696
874
 
697
875
    def test_unexpected_error(self):
698
 
        # A response of 'NoSuchRevision' is translated into an exception.
 
876
        # If the server sends an error the client doesn't understand, it gets
 
877
        # turned into an UnknownErrorFromSmartServer, which is presented as a
 
878
        # non-internal error to the user.
699
879
        transport = MemoryTransport()
700
880
        transport.mkdir('branch')
701
881
        transport = transport.clone('branch')
702
882
        client = FakeClient(transport.base)
 
883
        # get_stacked_on_url
 
884
        client.add_error_response('NotStacked')
703
885
        # lock_write
704
886
        client.add_success_response('ok', 'branch token', 'repo token')
705
887
        # set_last_revision
707
889
        # unlock
708
890
        client.add_success_response('ok')
709
891
 
710
 
        bzrdir = RemoteBzrDir(transport, _client=False)
711
 
        repo = RemoteRepository(bzrdir, None, _client=client)
712
 
        branch = RemoteBranch(bzrdir, repo, _client=client)
713
 
        # This is a hack to work around the problem that RemoteBranch currently
714
 
        # unnecessarily invokes _ensure_real upon a call to lock_write.
715
 
        branch._ensure_real = lambda: None
 
892
        branch = self.make_remote_branch(transport, client)
716
893
        # Lock the branch, reset the record of remote calls.
717
894
        branch.lock_write()
718
895
        client._calls = []
719
896
 
720
897
        err = self.assertRaises(
721
 
            errors.ErrorFromSmartServer,
 
898
            errors.UnknownErrorFromSmartServer,
722
899
            branch.set_last_revision_info, 123, 'revid')
723
900
        self.assertEqual(('UnexpectedError',), err.error_tuple)
724
901
        branch.unlock()
731
908
        transport.mkdir('branch')
732
909
        transport = transport.clone('branch')
733
910
        client = FakeClient(transport.base)
 
911
        # get_stacked_on_url
 
912
        client.add_error_response('NotStacked')
734
913
        # lock_write
735
914
        client.add_success_response('ok', 'branch token', 'repo token')
736
915
        # set_last_revision
738
917
        # unlock
739
918
        client.add_success_response('ok')
740
919
 
741
 
        bzrdir = RemoteBzrDir(transport, _client=False)
742
 
        repo = RemoteRepository(bzrdir, None, _client=client)
743
 
        branch = RemoteBranch(bzrdir, repo, _client=client)
744
 
        # This is a hack to work around the problem that RemoteBranch currently
745
 
        # unnecessarily invokes _ensure_real upon a call to lock_write.
746
 
        branch._ensure_real = lambda: None
 
920
        branch = self.make_remote_branch(transport, client)
747
921
        # Lock the branch, reset the record of remote calls.
748
922
        branch.lock_write()
749
923
        self.addCleanup(branch.unlock)
783
957
        ##     client._calls)
784
958
 
785
959
 
786
 
class TestBranchLockWrite(tests.TestCase):
 
960
class TestBranchLockWrite(RemoteBranchTestCase):
787
961
 
788
962
    def test_lock_write_unlockable(self):
789
963
        transport = MemoryTransport()
790
964
        client = FakeClient(transport.base)
791
 
        client.add_error_response('UnlockableTransport')
 
965
        client.add_expected_call(
 
966
            'Branch.get_stacked_on_url', ('quack/',),
 
967
            'error', ('NotStacked',),)
 
968
        client.add_expected_call(
 
969
            'Branch.lock_write', ('quack/', '', ''),
 
970
            'error', ('UnlockableTransport',))
792
971
        transport.mkdir('quack')
793
972
        transport = transport.clone('quack')
794
 
        # we do not want bzrdir to make any remote calls
795
 
        bzrdir = RemoteBzrDir(transport, _client=False)
796
 
        repo = RemoteRepository(bzrdir, None, _client=client)
797
 
        branch = RemoteBranch(bzrdir, repo, _client=client)
 
973
        branch = self.make_remote_branch(transport, client)
798
974
        self.assertRaises(errors.UnlockableTransport, branch.lock_write)
799
 
        self.assertEqual(
800
 
            [('call', 'Branch.lock_write', ('quack/', '', ''))],
801
 
            client._calls)
 
975
        client.finished_test()
802
976
 
803
977
 
804
978
class TestTransportIsReadonly(tests.TestCase):
1102
1276
        transport_path = 'sinhala'
1103
1277
        repo, client = self.setup_fake_client_and_repository(transport_path)
1104
1278
        client.add_error_response('AnUnexpectedError')
1105
 
        e = self.assertRaises(errors.ErrorFromSmartServer,
 
1279
        e = self.assertRaises(errors.UnknownErrorFromSmartServer,
1106
1280
            self.applyDeprecated, one_four, repo.get_revision_graph, revid)
1107
1281
        self.assertEqual(('AnUnexpectedError',), e.error_tuple)
1108
1282
 
1372
1546
        error_tuple = ('An unknown error tuple',)
1373
1547
        server_error = errors.ErrorFromSmartServer(error_tuple)
1374
1548
        translated_error = self.translateErrorFromSmartServer(server_error)
1375
 
        self.assertEqual(server_error, translated_error)
 
1549
        expected_error = errors.UnknownErrorFromSmartServer(server_error)
 
1550
        self.assertEqual(expected_error, translated_error)
1376
1551
 
1377
1552
    def test_context_missing_a_key(self):
1378
1553
        """In case of a bug in the client, or perhaps an unexpected response
1393
1568
            self._get_log(keep_log_file=True),
1394
1569
            "Missing key 'branch' in context")
1395
1570
        
 
1571
 
 
1572
class TestStacking(tests.TestCaseWithTransport):
 
1573
    """Tests for operations on stacked remote repositories.
 
1574
    
 
1575
    The underlying format type must support stacking.
 
1576
    """
 
1577
 
 
1578
    def test_access_stacked_remote(self):
 
1579
        # based on <http://launchpad.net/bugs/261315>
 
1580
        # make a branch stacked on another repository containing an empty
 
1581
        # revision, then open it over hpss - we should be able to see that
 
1582
        # revision.
 
1583
        base_transport = self.get_transport()
 
1584
        base_builder = self.make_branch_builder('base', format='1.6')
 
1585
        base_builder.start_series()
 
1586
        base_revid = base_builder.build_snapshot('rev-id', None,
 
1587
            [('add', ('', None, 'directory', None))],
 
1588
            'message')
 
1589
        base_builder.finish_series()
 
1590
        stacked_branch = self.make_branch('stacked', format='1.6')
 
1591
        stacked_branch.set_stacked_on_url('../base')
 
1592
        # start a server looking at this
 
1593
        smart_server = server.SmartTCPServer_for_testing()
 
1594
        smart_server.setUp()
 
1595
        self.addCleanup(smart_server.tearDown)
 
1596
        remote_bzrdir = BzrDir.open(smart_server.get_url() + '/stacked')
 
1597
        # can get its branch and repository
 
1598
        remote_branch = remote_bzrdir.open_branch()
 
1599
        remote_repo = remote_branch.repository
 
1600
        remote_repo.lock_read()
 
1601
        try:
 
1602
            # it should have an appropriate fallback repository, which should also
 
1603
            # be a RemoteRepository
 
1604
            self.assertEquals(len(remote_repo._fallback_repositories), 1)
 
1605
            self.assertIsInstance(remote_repo._fallback_repositories[0],
 
1606
                RemoteRepository)
 
1607
            # and it has the revision committed to the underlying repository;
 
1608
            # these have varying implementations so we try several of them
 
1609
            self.assertTrue(remote_repo.has_revisions([base_revid]))
 
1610
            self.assertTrue(remote_repo.has_revision(base_revid))
 
1611
            self.assertEqual(remote_repo.get_revision(base_revid).message,
 
1612
                'message')
 
1613
        finally:
 
1614
            remote_repo.unlock()