~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_remote.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2010-09-29 22:03:03 UTC
  • mfrom: (5416.2.6 jam-integration)
  • Revision ID: pqm@pqm.ubuntu.com-20100929220303-cr95h8iwtggco721
(mbp) Add 'break-lock --force'

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006, 2007, 2008, 2009 Canonical Ltd
 
1
# Copyright (C) 2006-2010 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
27
27
from cStringIO import StringIO
28
28
 
29
29
from bzrlib import (
 
30
    branch,
30
31
    bzrdir,
31
32
    config,
 
33
    controldir,
32
34
    errors,
33
35
    graph,
34
 
    pack,
 
36
    inventory,
 
37
    inventory_delta,
35
38
    remote,
36
39
    repository,
37
 
    smart,
38
40
    tests,
 
41
    transport,
39
42
    treebuilder,
40
 
    urlutils,
 
43
    versionedfile,
41
44
    )
42
45
from bzrlib.branch import Branch
43
 
from bzrlib.bzrdir import BzrDir, BzrDirFormat
 
46
from bzrlib.bzrdir import (
 
47
    BzrDir,
 
48
    BzrDirFormat,
 
49
    RemoteBzrProber,
 
50
    )
44
51
from bzrlib.remote import (
45
52
    RemoteBranch,
46
53
    RemoteBranchFormat,
47
54
    RemoteBzrDir,
48
 
    RemoteBzrDirFormat,
49
55
    RemoteRepository,
50
56
    RemoteRepositoryFormat,
51
57
    )
52
58
from bzrlib.repofmt import groupcompress_repo, pack_repo
53
59
from bzrlib.revision import NULL_REVISION
54
 
from bzrlib.smart import server, medium
 
60
from bzrlib.smart import medium
55
61
from bzrlib.smart.client import _SmartClient
56
62
from bzrlib.smart.repository import SmartServerRepositoryGetParentMap
57
63
from bzrlib.tests import (
58
64
    condition_isinstance,
59
65
    split_suite_by_condition,
60
66
    multiply_tests,
61
 
    KnownFailure,
 
67
    test_server,
62
68
    )
63
 
from bzrlib.transport import get_transport, http
64
69
from bzrlib.transport.memory import MemoryTransport
65
70
from bzrlib.transport.remote import (
66
71
    RemoteTransport,
73
78
        standard_tests, condition_isinstance(BasicRemoteObjectTests))
74
79
    smart_server_version_scenarios = [
75
80
        ('HPSS-v2',
76
 
            {'transport_server': server.SmartTCPServer_for_testing_v2_only}),
 
81
         {'transport_server': test_server.SmartTCPServer_for_testing_v2_only}),
77
82
        ('HPSS-v3',
78
 
            {'transport_server': server.SmartTCPServer_for_testing})]
 
83
         {'transport_server': test_server.SmartTCPServer_for_testing})]
79
84
    return multiply_tests(to_adapt, smart_server_version_scenarios, result)
80
85
 
81
86
 
86
91
        self.transport = self.get_transport()
87
92
        # make a branch that can be opened over the smart transport
88
93
        self.local_wt = BzrDir.create_standalone_workingtree('.')
89
 
 
90
 
    def tearDown(self):
91
 
        self.transport.disconnect()
92
 
        tests.TestCaseWithTransport.tearDown(self)
 
94
        self.addCleanup(self.transport.disconnect)
93
95
 
94
96
    def test_create_remote_bzrdir(self):
95
97
        b = remote.RemoteBzrDir(self.transport, remote.RemoteBzrDirFormat())
119
121
    def test_find_correct_format(self):
120
122
        """Should open a RemoteBzrDir over a RemoteTransport"""
121
123
        fmt = BzrDirFormat.find_format(self.transport)
122
 
        self.assertTrue(RemoteBzrDirFormat
123
 
                        in BzrDirFormat._control_server_formats)
 
124
        self.assertTrue(bzrdir.RemoteBzrProber
 
125
                        in controldir.ControlDirFormat._server_probers)
124
126
        self.assertIsInstance(fmt, remote.RemoteBzrDirFormat)
125
127
 
126
128
    def test_open_detected_smart_format(self):
132
134
        b = BzrDir.open_from_transport(self.transport).open_branch()
133
135
        self.assertStartsWith(str(b), 'RemoteBranch(')
134
136
 
 
137
    def test_remote_bzrdir_repr(self):
 
138
        b = BzrDir.open_from_transport(self.transport)
 
139
        self.assertStartsWith(str(b), 'RemoteBzrDir(')
 
140
 
135
141
    def test_remote_branch_format_supports_stacking(self):
136
142
        t = self.transport
137
143
        self.make_branch('unstackable', format='pack-0.92')
277
283
        self.expecting_body = True
278
284
        return result[1], FakeProtocol(result[2], self)
279
285
 
 
286
    def call_with_body_bytes(self, method, args, body):
 
287
        self._check_call(method, args)
 
288
        self._calls.append(('call_with_body_bytes', method, args, body))
 
289
        result = self._get_next_response()
 
290
        return result[1], FakeProtocol(result[2], self)
 
291
 
280
292
    def call_with_body_bytes_expecting_body(self, method, args, body):
281
293
        self._check_call(method, args)
282
294
        self._calls.append(('call_with_body_bytes_expecting_body', method,
332
344
        reference_bzrdir_format = bzrdir.format_registry.get('default')()
333
345
        return reference_bzrdir_format.repository_format
334
346
 
335
 
    def disable_verb(self, verb):
336
 
        """Disable a verb for one test."""
337
 
        request_handlers = smart.request.request_handlers
338
 
        orig_method = request_handlers.get(verb)
339
 
        request_handlers.remove(verb)
340
 
        def restoreVerb():
341
 
            request_handlers.register(verb, orig_method)
342
 
        self.addCleanup(restoreVerb)
 
347
    def assertFinished(self, fake_client):
 
348
        """Assert that all of a FakeClient's expected calls have occurred."""
 
349
        fake_client.finished_test()
343
350
 
344
351
 
345
352
class Test_ClientMedium_remote_path_from_transport(tests.TestCase):
351
358
        a given client_base and transport_base.
352
359
        """
353
360
        client_medium = medium.SmartClientMedium(client_base)
354
 
        transport = get_transport(transport_base)
355
 
        result = client_medium.remote_path_from_transport(transport)
 
361
        t = transport.get_transport(transport_base)
 
362
        result = client_medium.remote_path_from_transport(t)
356
363
        self.assertEqual(expected, result)
357
364
 
358
365
    def test_remote_path_from_transport(self):
369
376
        a given transport_base and relpath of that transport.  (Note that
370
377
        HttpTransportBase is a subclass of SmartClientMedium)
371
378
        """
372
 
        base_transport = get_transport(transport_base)
 
379
        base_transport = transport.get_transport(transport_base)
373
380
        client_medium = base_transport.get_smart_medium()
374
381
        cloned_transport = base_transport.clone(relpath)
375
382
        result = client_medium.remote_path_from_transport(cloned_transport)
410
417
        # Calling _remember_remote_is_before again with a lower value works.
411
418
        client_medium._remember_remote_is_before((1, 5))
412
419
        self.assertTrue(client_medium._is_remote_before((1, 5)))
413
 
        # You cannot call _remember_remote_is_before with a larger value.
414
 
        self.assertRaises(
415
 
            AssertionError, client_medium._remember_remote_is_before, (1, 9))
 
420
        # If you call _remember_remote_is_before with a higher value it logs a
 
421
        # warning, and continues to remember the lower value.
 
422
        self.assertNotContainsRe(self.get_log(), '_remember_remote_is_before')
 
423
        client_medium._remember_remote_is_before((1, 9))
 
424
        self.assertContainsRe(self.get_log(), '_remember_remote_is_before')
 
425
        self.assertTrue(client_medium._is_remote_before((1, 5)))
416
426
 
417
427
 
418
428
class TestBzrDirCloningMetaDir(TestRemote):
437
447
            'BzrDir.cloning_metadir', ('quack/', 'False'),
438
448
            'error', ('BranchReference',)),
439
449
        client.add_expected_call(
440
 
            'BzrDir.open_branchV2', ('quack/',),
 
450
            'BzrDir.open_branchV3', ('quack/',),
441
451
            'success', ('ref', self.get_url('referenced'))),
442
452
        a_bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
443
453
            _client=client)
446
456
        self.assertEqual(bzrdir.BzrDirMetaFormat1, type(result))
447
457
        self.assertEqual(expected._repository_format, result._repository_format)
448
458
        self.assertEqual(expected._branch_format, result._branch_format)
449
 
        client.finished_test()
 
459
        self.assertFinished(client)
450
460
 
451
461
    def test_current_server(self):
452
462
        transport = self.get_transport('.')
467
477
        self.assertEqual(bzrdir.BzrDirMetaFormat1, type(result))
468
478
        self.assertEqual(None, result._repository_format)
469
479
        self.assertEqual(None, result._branch_format)
470
 
        client.finished_test()
 
480
        self.assertFinished(client)
 
481
 
 
482
 
 
483
class TestBzrDirOpen(TestRemote):
 
484
 
 
485
    def make_fake_client_and_transport(self, path='quack'):
 
486
        transport = MemoryTransport()
 
487
        transport.mkdir(path)
 
488
        transport = transport.clone(path)
 
489
        client = FakeClient(transport.base)
 
490
        return client, transport
 
491
 
 
492
    def test_absent(self):
 
493
        client, transport = self.make_fake_client_and_transport()
 
494
        client.add_expected_call(
 
495
            'BzrDir.open_2.1', ('quack/',), 'success', ('no',))
 
496
        self.assertRaises(errors.NotBranchError, RemoteBzrDir, transport,
 
497
                remote.RemoteBzrDirFormat(), _client=client, _force_probe=True)
 
498
        self.assertFinished(client)
 
499
 
 
500
    def test_present_without_workingtree(self):
 
501
        client, transport = self.make_fake_client_and_transport()
 
502
        client.add_expected_call(
 
503
            'BzrDir.open_2.1', ('quack/',), 'success', ('yes', 'no'))
 
504
        bd = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
 
505
            _client=client, _force_probe=True)
 
506
        self.assertIsInstance(bd, RemoteBzrDir)
 
507
        self.assertFalse(bd.has_workingtree())
 
508
        self.assertRaises(errors.NoWorkingTree, bd.open_workingtree)
 
509
        self.assertFinished(client)
 
510
 
 
511
    def test_present_with_workingtree(self):
 
512
        client, transport = self.make_fake_client_and_transport()
 
513
        client.add_expected_call(
 
514
            'BzrDir.open_2.1', ('quack/',), 'success', ('yes', 'yes'))
 
515
        bd = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
 
516
            _client=client, _force_probe=True)
 
517
        self.assertIsInstance(bd, RemoteBzrDir)
 
518
        self.assertTrue(bd.has_workingtree())
 
519
        self.assertRaises(errors.NotLocalUrl, bd.open_workingtree)
 
520
        self.assertFinished(client)
 
521
 
 
522
    def test_backwards_compat(self):
 
523
        client, transport = self.make_fake_client_and_transport()
 
524
        client.add_expected_call(
 
525
            'BzrDir.open_2.1', ('quack/',), 'unknown', ('BzrDir.open_2.1',))
 
526
        client.add_expected_call(
 
527
            'BzrDir.open', ('quack/',), 'success', ('yes',))
 
528
        bd = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
 
529
            _client=client, _force_probe=True)
 
530
        self.assertIsInstance(bd, RemoteBzrDir)
 
531
        self.assertFinished(client)
 
532
 
 
533
    def test_backwards_compat_hpss_v2(self):
 
534
        client, transport = self.make_fake_client_and_transport()
 
535
        # Monkey-patch fake client to simulate real-world behaviour with v2
 
536
        # server: upon first RPC call detect the protocol version, and because
 
537
        # the version is 2 also do _remember_remote_is_before((1, 6)) before
 
538
        # continuing with the RPC.
 
539
        orig_check_call = client._check_call
 
540
        def check_call(method, args):
 
541
            client._medium._protocol_version = 2
 
542
            client._medium._remember_remote_is_before((1, 6))
 
543
            client._check_call = orig_check_call
 
544
            client._check_call(method, args)
 
545
        client._check_call = check_call
 
546
        client.add_expected_call(
 
547
            'BzrDir.open_2.1', ('quack/',), 'unknown', ('BzrDir.open_2.1',))
 
548
        client.add_expected_call(
 
549
            'BzrDir.open', ('quack/',), 'success', ('yes',))
 
550
        bd = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
 
551
            _client=client, _force_probe=True)
 
552
        self.assertIsInstance(bd, RemoteBzrDir)
 
553
        self.assertFinished(client)
471
554
 
472
555
 
473
556
class TestBzrDirOpenBranch(TestRemote):
477
560
        self.make_branch('.')
478
561
        a_dir = BzrDir.open(self.get_url('.'))
479
562
        self.reset_smart_call_log()
480
 
        verb = 'BzrDir.open_branchV2'
 
563
        verb = 'BzrDir.open_branchV3'
481
564
        self.disable_verb(verb)
482
565
        format = a_dir.open_branch()
483
566
        call_count = len([call for call in self.hpss_calls if
493
576
        transport = transport.clone('quack')
494
577
        client = FakeClient(transport.base)
495
578
        client.add_expected_call(
496
 
            'BzrDir.open_branchV2', ('quack/',),
 
579
            'BzrDir.open_branchV3', ('quack/',),
497
580
            'success', ('branch', branch_network_name))
498
581
        client.add_expected_call(
499
582
            'BzrDir.find_repositoryV3', ('quack/',),
506
589
        result = bzrdir.open_branch()
507
590
        self.assertIsInstance(result, RemoteBranch)
508
591
        self.assertEqual(bzrdir, result.bzrdir)
509
 
        client.finished_test()
 
592
        self.assertFinished(client)
510
593
 
511
594
    def test_branch_missing(self):
512
595
        transport = MemoryTransport()
518
601
            _client=client)
519
602
        self.assertRaises(errors.NotBranchError, bzrdir.open_branch)
520
603
        self.assertEqual(
521
 
            [('call', 'BzrDir.open_branchV2', ('quack/',))],
 
604
            [('call', 'BzrDir.open_branchV3', ('quack/',))],
522
605
            client._calls)
523
606
 
524
607
    def test__get_tree_branch(self):
525
608
        # _get_tree_branch is a form of open_branch, but it should only ask for
526
609
        # branch opening, not any other network requests.
527
610
        calls = []
528
 
        def open_branch():
 
611
        def open_branch(name=None):
529
612
            calls.append("Called")
530
613
            return "a-branch"
531
614
        transport = MemoryTransport()
548
631
        network_name = reference_format.network_name()
549
632
        branch_network_name = self.get_branch_format().network_name()
550
633
        client.add_expected_call(
551
 
            'BzrDir.open_branchV2', ('~hello/',),
 
634
            'BzrDir.open_branchV3', ('~hello/',),
552
635
            'success', ('branch', branch_network_name))
553
636
        client.add_expected_call(
554
637
            'BzrDir.find_repositoryV3', ('~hello/',),
559
642
        bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
560
643
            _client=client)
561
644
        result = bzrdir.open_branch()
562
 
        client.finished_test()
 
645
        self.assertFinished(client)
563
646
 
564
647
    def check_open_repository(self, rich_root, subtrees, external_lookup='no'):
565
648
        reference_format = self.get_repo_format()
602
685
        old.
603
686
        """
604
687
        self.assertRaises(errors.NotBranchError,
605
 
            RemoteBzrDirFormat.probe_transport, OldServerTransport())
 
688
            RemoteBzrProber.probe_transport, OldServerTransport())
606
689
 
607
690
 
608
691
class TestBzrDirCreateBranch(TestRemote):
663
746
        network_name = reference_format.network_name()
664
747
        client.add_expected_call(
665
748
            'BzrDir.create_repository', ('quack/',
666
 
                'Bazaar pack repository format 1 (needs bzr 0.92)\n', 'False'),
667
 
            'success', ('ok', 'no', 'no', 'no', network_name))
 
749
                'Bazaar repository format 2a (needs bzr 1.16 or later)\n',
 
750
                'False'),
 
751
            'success', ('ok', 'yes', 'yes', 'yes', network_name))
668
752
        a_bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
669
753
            _client=client)
670
754
        repo = a_bzrdir.create_repository()
672
756
        self.assertIsInstance(repo, remote.RemoteRepository)
673
757
        # its format should have the settings from the response
674
758
        format = repo._format
675
 
        self.assertFalse(format.rich_root_data)
676
 
        self.assertFalse(format.supports_tree_reference)
677
 
        self.assertFalse(format.supports_external_lookups)
 
759
        self.assertTrue(format.rich_root_data)
 
760
        self.assertTrue(format.supports_tree_reference)
 
761
        self.assertTrue(format.supports_external_lookups)
678
762
        self.assertEqual(network_name, format.network_name())
679
763
 
680
764
 
684
768
        # fallback all the way to the first version.
685
769
        reference_format = self.get_repo_format()
686
770
        network_name = reference_format.network_name()
687
 
        client = FakeClient('bzr://example.com/')
 
771
        server_url = 'bzr://example.com/'
 
772
        self.permit_url(server_url)
 
773
        client = FakeClient(server_url)
688
774
        client.add_unknown_method_response('BzrDir.find_repositoryV3')
689
775
        client.add_unknown_method_response('BzrDir.find_repositoryV2')
690
776
        client.add_success_response('ok', '', 'no', 'no')
696
782
            reference_format.get_format_string(), 'ok')
697
783
        # PackRepository wants to do a stat
698
784
        client.add_success_response('stat', '0', '65535')
699
 
        remote_transport = RemoteTransport('bzr://example.com/quack/', medium=False,
 
785
        remote_transport = RemoteTransport(server_url + 'quack/', medium=False,
700
786
            _client=client)
701
787
        bzrdir = RemoteBzrDir(remote_transport, remote.RemoteBzrDirFormat(),
702
788
            _client=client)
716
802
        # fallback to find_repositoryV2
717
803
        reference_format = self.get_repo_format()
718
804
        network_name = reference_format.network_name()
719
 
        client = FakeClient('bzr://example.com/')
 
805
        server_url = 'bzr://example.com/'
 
806
        self.permit_url(server_url)
 
807
        client = FakeClient(server_url)
720
808
        client.add_unknown_method_response('BzrDir.find_repositoryV3')
721
809
        client.add_success_response('ok', '', 'no', 'no', 'no')
722
810
        # A real repository instance will be created to determine the network
727
815
            reference_format.get_format_string(), 'ok')
728
816
        # PackRepository wants to do a stat
729
817
        client.add_success_response('stat', '0', '65535')
730
 
        remote_transport = RemoteTransport('bzr://example.com/quack/', medium=False,
 
818
        remote_transport = RemoteTransport(server_url + 'quack/', medium=False,
731
819
            _client=client)
732
820
        bzrdir = RemoteBzrDir(remote_transport, remote.RemoteBzrDirFormat(),
733
821
            _client=client)
779
867
        # transport connected to a real server.
780
868
        result = fmt._initialize_on_transport_ex_rpc(client, 'path',
781
869
            transport, False, False, False, None, None, None, None, False)
782
 
        client.finished_test()
 
870
        self.assertFinished(client)
783
871
 
784
872
    def test_error(self):
785
873
        """Error responses are translated, e.g. 'PermissionDenied' raises the
803
891
            False, False, False, None, None, None, None, False)
804
892
        self.assertEqual('path', err.path)
805
893
        self.assertEqual(': extra info', err.extra)
806
 
        client.finished_test()
 
894
        self.assertFinished(client)
807
895
 
808
896
    def test_error_from_real_server(self):
809
897
        """Integration test for error translation."""
852
940
 
853
941
class RemoteBranchTestCase(RemoteBzrDirTestCase):
854
942
 
 
943
    def lock_remote_branch(self, branch):
 
944
        """Trick a RemoteBranch into thinking it is locked."""
 
945
        branch._lock_mode = 'w'
 
946
        branch._lock_count = 2
 
947
        branch._lock_token = 'branch token'
 
948
        branch._repo_lock_token = 'repo token'
 
949
        branch.repository._lock_mode = 'w'
 
950
        branch.repository._lock_count = 2
 
951
        branch.repository._lock_token = 'repo token'
 
952
 
855
953
    def make_remote_branch(self, transport, client):
856
954
        """Make a RemoteBranch using 'client' as its _SmartClient.
857
955
 
884
982
        transport = transport.clone('quack')
885
983
        branch = self.make_remote_branch(transport, client)
886
984
        result = branch.get_parent()
887
 
        client.finished_test()
 
985
        self.assertFinished(client)
888
986
        self.assertEqual(None, result)
889
987
 
890
988
    def test_parent_relative(self):
916
1014
        branch = self.make_remote_branch(transport, client)
917
1015
        result = branch.get_parent()
918
1016
        self.assertEqual('http://foo/', result)
919
 
        client.finished_test()
 
1017
        self.assertFinished(client)
920
1018
 
921
1019
 
922
1020
class TestBranchSetParentLocation(RemoteBranchTestCase):
937
1035
        branch._lock_token = 'b'
938
1036
        branch._repo_lock_token = 'r'
939
1037
        branch._set_parent_location(None)
940
 
        client.finished_test()
 
1038
        self.assertFinished(client)
941
1039
 
942
1040
    def test_parent(self):
943
1041
        transport = MemoryTransport()
954
1052
        branch._lock_token = 'b'
955
1053
        branch._repo_lock_token = 'r'
956
1054
        branch._set_parent_location('foo')
957
 
        client.finished_test()
 
1055
        self.assertFinished(client)
958
1056
 
959
1057
    def test_backwards_compat(self):
960
1058
        self.setup_smart_server_with_call_log()
992
1090
        transport = transport.clone('quack')
993
1091
        branch = self.make_remote_branch(transport, client)
994
1092
        result = branch.tags.get_tag_dict()
995
 
        client.finished_test()
 
1093
        self.assertFinished(client)
996
1094
        self.assertEqual({}, result)
997
1095
 
998
1096
 
 
1097
class TestBranchSetTagsBytes(RemoteBranchTestCase):
 
1098
 
 
1099
    def test_trivial(self):
 
1100
        transport = MemoryTransport()
 
1101
        client = FakeClient(transport.base)
 
1102
        client.add_expected_call(
 
1103
            'Branch.get_stacked_on_url', ('quack/',),
 
1104
            'error', ('NotStacked',))
 
1105
        client.add_expected_call(
 
1106
            'Branch.set_tags_bytes', ('quack/', 'branch token', 'repo token'),
 
1107
            'success', ('',))
 
1108
        transport.mkdir('quack')
 
1109
        transport = transport.clone('quack')
 
1110
        branch = self.make_remote_branch(transport, client)
 
1111
        self.lock_remote_branch(branch)
 
1112
        branch._set_tags_bytes('tags bytes')
 
1113
        self.assertFinished(client)
 
1114
        self.assertEqual('tags bytes', client._calls[-1][-1])
 
1115
 
 
1116
    def test_backwards_compatible(self):
 
1117
        transport = MemoryTransport()
 
1118
        client = FakeClient(transport.base)
 
1119
        client.add_expected_call(
 
1120
            'Branch.get_stacked_on_url', ('quack/',),
 
1121
            'error', ('NotStacked',))
 
1122
        client.add_expected_call(
 
1123
            'Branch.set_tags_bytes', ('quack/', 'branch token', 'repo token'),
 
1124
            'unknown', ('Branch.set_tags_bytes',))
 
1125
        transport.mkdir('quack')
 
1126
        transport = transport.clone('quack')
 
1127
        branch = self.make_remote_branch(transport, client)
 
1128
        self.lock_remote_branch(branch)
 
1129
        class StubRealBranch(object):
 
1130
            def __init__(self):
 
1131
                self.calls = []
 
1132
            def _set_tags_bytes(self, bytes):
 
1133
                self.calls.append(('set_tags_bytes', bytes))
 
1134
        real_branch = StubRealBranch()
 
1135
        branch._real_branch = real_branch
 
1136
        branch._set_tags_bytes('tags bytes')
 
1137
        # Call a second time, to exercise the 'remote version already inferred'
 
1138
        # code path.
 
1139
        branch._set_tags_bytes('tags bytes')
 
1140
        self.assertFinished(client)
 
1141
        self.assertEqual(
 
1142
            [('set_tags_bytes', 'tags bytes')] * 2, real_branch.calls)
 
1143
 
 
1144
 
999
1145
class TestBranchLastRevisionInfo(RemoteBranchTestCase):
1000
1146
 
1001
1147
    def test_empty_branch(self):
1012
1158
        transport = transport.clone('quack')
1013
1159
        branch = self.make_remote_branch(transport, client)
1014
1160
        result = branch.last_revision_info()
1015
 
        client.finished_test()
 
1161
        self.assertFinished(client)
1016
1162
        self.assertEqual((0, NULL_REVISION), result)
1017
1163
 
1018
1164
    def test_non_empty_branch(self):
1073
1219
        client = FakeClient(self.get_url())
1074
1220
        branch_network_name = self.get_branch_format().network_name()
1075
1221
        client.add_expected_call(
1076
 
            'BzrDir.open_branchV2', ('stacked/',),
 
1222
            'BzrDir.open_branchV3', ('stacked/',),
1077
1223
            'success', ('branch', branch_network_name))
1078
1224
        client.add_expected_call(
1079
1225
            'BzrDir.find_repositoryV3', ('stacked/',),
1093
1239
        branch = bzrdir.open_branch()
1094
1240
        result = branch.get_stacked_on_url()
1095
1241
        self.assertEqual('../base', result)
1096
 
        client.finished_test()
 
1242
        self.assertFinished(client)
1097
1243
        # it's in the fallback list both for the RemoteRepository and its vfs
1098
1244
        # repository
1099
1245
        self.assertEqual(1, len(branch.repository._fallback_repositories))
1101
1247
            len(branch.repository._real_repository._fallback_repositories))
1102
1248
 
1103
1249
    def test_get_stacked_on_real_branch(self):
1104
 
        base_branch = self.make_branch('base', format='1.6')
1105
 
        stacked_branch = self.make_branch('stacked', format='1.6')
 
1250
        base_branch = self.make_branch('base')
 
1251
        stacked_branch = self.make_branch('stacked')
1106
1252
        stacked_branch.set_stacked_on_url('../base')
1107
1253
        reference_format = self.get_repo_format()
1108
1254
        network_name = reference_format.network_name()
1109
1255
        client = FakeClient(self.get_url())
1110
1256
        branch_network_name = self.get_branch_format().network_name()
1111
1257
        client.add_expected_call(
1112
 
            'BzrDir.open_branchV2', ('stacked/',),
 
1258
            'BzrDir.open_branchV3', ('stacked/',),
1113
1259
            'success', ('branch', branch_network_name))
1114
1260
        client.add_expected_call(
1115
1261
            'BzrDir.find_repositoryV3', ('stacked/',),
1116
 
            'success', ('ok', '', 'no', 'no', 'yes', network_name))
 
1262
            'success', ('ok', '', 'yes', 'no', 'yes', network_name))
1117
1263
        # called twice, once from constructor and then again by us
1118
1264
        client.add_expected_call(
1119
1265
            'Branch.get_stacked_on_url', ('stacked/',),
1126
1272
        branch = bzrdir.open_branch()
1127
1273
        result = branch.get_stacked_on_url()
1128
1274
        self.assertEqual('../base', result)
1129
 
        client.finished_test()
 
1275
        self.assertFinished(client)
1130
1276
        # it's in the fallback list both for the RemoteRepository.
1131
1277
        self.assertEqual(1, len(branch.repository._fallback_repositories))
1132
1278
        # And we haven't had to construct a real repository.
1167
1313
        result = branch.set_revision_history([])
1168
1314
        branch.unlock()
1169
1315
        self.assertEqual(None, result)
1170
 
        client.finished_test()
 
1316
        self.assertFinished(client)
1171
1317
 
1172
1318
    def test_set_nonempty(self):
1173
1319
        # set_revision_history([rev-id1, ..., rev-idN]) is translated to calling
1205
1351
        result = branch.set_revision_history(['rev-id1', 'rev-id2'])
1206
1352
        branch.unlock()
1207
1353
        self.assertEqual(None, result)
1208
 
        client.finished_test()
 
1354
        self.assertFinished(client)
1209
1355
 
1210
1356
    def test_no_such_revision(self):
1211
1357
        transport = MemoryTransport()
1240
1386
        self.assertRaises(
1241
1387
            errors.NoSuchRevision, branch.set_revision_history, ['rev-id'])
1242
1388
        branch.unlock()
1243
 
        client.finished_test()
 
1389
        self.assertFinished(client)
1244
1390
 
1245
1391
    def test_tip_change_rejected(self):
1246
1392
        """TipChangeRejected responses cause a TipChangeRejected exception to
1283
1429
        self.assertIsInstance(err.msg, unicode)
1284
1430
        self.assertEqual(rejection_msg_unicode, err.msg)
1285
1431
        branch.unlock()
1286
 
        client.finished_test()
 
1432
        self.assertFinished(client)
1287
1433
 
1288
1434
 
1289
1435
class TestBranchSetLastRevisionInfo(RemoteBranchTestCase):
1343
1489
            errors.NoSuchRevision, branch.set_last_revision_info, 123, 'revid')
1344
1490
        branch.unlock()
1345
1491
 
1346
 
    def lock_remote_branch(self, branch):
1347
 
        """Trick a RemoteBranch into thinking it is locked."""
1348
 
        branch._lock_mode = 'w'
1349
 
        branch._lock_count = 2
1350
 
        branch._lock_token = 'branch token'
1351
 
        branch._repo_lock_token = 'repo token'
1352
 
        branch.repository._lock_mode = 'w'
1353
 
        branch.repository._lock_count = 2
1354
 
        branch.repository._lock_token = 'repo token'
1355
 
 
1356
1492
    def test_backwards_compatibility(self):
1357
1493
        """If the server does not support the Branch.set_last_revision_info
1358
1494
        verb (which is new in 1.4), then the client falls back to VFS methods.
1399
1535
        self.assertEqual(
1400
1536
            [('set_last_revision_info', 1234, 'a-revision-id')],
1401
1537
            real_branch.calls)
1402
 
        client.finished_test()
 
1538
        self.assertFinished(client)
1403
1539
 
1404
1540
    def test_unexpected_error(self):
1405
1541
        # If the server sends an error the client doesn't understand, it gets
1481
1617
    def test_get_multi_line_branch_conf(self):
1482
1618
        # Make sure that multiple-line branch.conf files are supported
1483
1619
        #
1484
 
        # https://bugs.edge.launchpad.net/bzr/+bug/354075
 
1620
        # https://bugs.launchpad.net/bzr/+bug/354075
1485
1621
        client = FakeClient()
1486
1622
        client.add_expected_call(
1487
1623
            'Branch.get_stacked_on_url', ('memory:///',),
1513
1649
        config = branch._get_config()
1514
1650
        config.set_option('foo', 'bar')
1515
1651
        branch.unlock()
1516
 
        client.finished_test()
 
1652
        self.assertFinished(client)
 
1653
 
 
1654
    def test_set_option_with_dict(self):
 
1655
        client = FakeClient()
 
1656
        client.add_expected_call(
 
1657
            'Branch.get_stacked_on_url', ('memory:///',),
 
1658
            'error', ('NotStacked',),)
 
1659
        client.add_expected_call(
 
1660
            'Branch.lock_write', ('memory:///', '', ''),
 
1661
            'success', ('ok', 'branch token', 'repo token'))
 
1662
        encoded_dict_value = 'd5:ascii1:a11:unicode \xe2\x8c\x9a3:\xe2\x80\xbde'
 
1663
        client.add_expected_call(
 
1664
            'Branch.set_config_option_dict', ('memory:///', 'branch token',
 
1665
            'repo token', encoded_dict_value, 'foo', ''),
 
1666
            'success', ())
 
1667
        client.add_expected_call(
 
1668
            'Branch.unlock', ('memory:///', 'branch token', 'repo token'),
 
1669
            'success', ('ok',))
 
1670
        transport = MemoryTransport()
 
1671
        branch = self.make_remote_branch(transport, client)
 
1672
        branch.lock_write()
 
1673
        config = branch._get_config()
 
1674
        config.set_option(
 
1675
            {'ascii': 'a', u'unicode \N{WATCH}': u'\N{INTERROBANG}'},
 
1676
            'foo')
 
1677
        branch.unlock()
 
1678
        self.assertFinished(client)
1517
1679
 
1518
1680
    def test_backwards_compat_set_option(self):
1519
1681
        self.setup_smart_server_with_call_log()
1527
1689
        self.assertLength(10, self.hpss_calls)
1528
1690
        self.assertEqual('value', branch._get_config().get_option('name'))
1529
1691
 
 
1692
    def test_backwards_compat_set_option_with_dict(self):
 
1693
        self.setup_smart_server_with_call_log()
 
1694
        branch = self.make_branch('.')
 
1695
        verb = 'Branch.set_config_option_dict'
 
1696
        self.disable_verb(verb)
 
1697
        branch.lock_write()
 
1698
        self.addCleanup(branch.unlock)
 
1699
        self.reset_smart_call_log()
 
1700
        config = branch._get_config()
 
1701
        value_dict = {'ascii': 'a', u'unicode \N{WATCH}': u'\N{INTERROBANG}'}
 
1702
        config.set_option(value_dict, 'name')
 
1703
        self.assertLength(10, self.hpss_calls)
 
1704
        self.assertEqual(value_dict, branch._get_config().get_option('name'))
 
1705
 
1530
1706
 
1531
1707
class TestBranchLockWrite(RemoteBranchTestCase):
1532
1708
 
1543
1719
        transport = transport.clone('quack')
1544
1720
        branch = self.make_remote_branch(transport, client)
1545
1721
        self.assertRaises(errors.UnlockableTransport, branch.lock_write)
1546
 
        client.finished_test()
 
1722
        self.assertFinished(client)
1547
1723
 
1548
1724
 
1549
1725
class TestBzrDirGetSetConfig(RemoteBzrDirTestCase):
1674
1850
        return repo, client
1675
1851
 
1676
1852
 
 
1853
def remoted_description(format):
 
1854
    return 'Remote: ' + format.get_format_description()
 
1855
 
 
1856
 
 
1857
class TestBranchFormat(tests.TestCase):
 
1858
 
 
1859
    def test_get_format_description(self):
 
1860
        remote_format = RemoteBranchFormat()
 
1861
        real_format = branch.BranchFormat.get_default_format()
 
1862
        remote_format._network_name = real_format.network_name()
 
1863
        self.assertEqual(remoted_description(real_format),
 
1864
            remote_format.get_format_description())
 
1865
 
 
1866
 
1677
1867
class TestRepositoryFormat(TestRemoteRepository):
1678
1868
 
1679
1869
    def test_fast_delta(self):
1686
1876
        false_format._network_name = false_name
1687
1877
        self.assertEqual(False, false_format.fast_deltas)
1688
1878
 
 
1879
    def test_get_format_description(self):
 
1880
        remote_repo_format = RemoteRepositoryFormat()
 
1881
        real_format = repository.RepositoryFormat.get_default_format()
 
1882
        remote_repo_format._network_name = real_format.network_name()
 
1883
        self.assertEqual(remoted_description(real_format),
 
1884
            remote_repo_format.get_format_description())
 
1885
 
1689
1886
 
1690
1887
class TestRepositoryGatherStats(TestRemoteRepository):
1691
1888
 
1876
2073
        self.assertLength(1, self.hpss_calls)
1877
2074
 
1878
2075
    def disableExtraResults(self):
1879
 
        old_flag = SmartServerRepositoryGetParentMap.no_extra_results
1880
 
        SmartServerRepositoryGetParentMap.no_extra_results = True
1881
 
        def reset_values():
1882
 
            SmartServerRepositoryGetParentMap.no_extra_results = old_flag
1883
 
        self.addCleanup(reset_values)
 
2076
        self.overrideAttr(SmartServerRepositoryGetParentMap,
 
2077
                          'no_extra_results', True)
1884
2078
 
1885
2079
    def test_null_cached_missing_and_stop_key(self):
1886
2080
        self.setup_smart_server_with_call_log()
1945
2139
 
1946
2140
    def test_allows_new_revisions(self):
1947
2141
        """get_parent_map's results can be updated by commit."""
1948
 
        smart_server = server.SmartTCPServer_for_testing()
1949
 
        smart_server.setUp()
1950
 
        self.addCleanup(smart_server.tearDown)
 
2142
        smart_server = test_server.SmartTCPServer_for_testing()
 
2143
        self.start_server(smart_server)
1951
2144
        self.make_branch('branch')
1952
2145
        branch = Branch.open(smart_server.get_url() + '/branch')
1953
2146
        tree = branch.create_checkout('tree', lightweight=True)
2045
2238
            'success', ('ok', 'rev-five'))
2046
2239
        result = repo.get_rev_id_for_revno(5, (42, 'rev-foo'))
2047
2240
        self.assertEqual((True, 'rev-five'), result)
2048
 
        client.finished_test()
 
2241
        self.assertFinished(client)
2049
2242
 
2050
2243
    def test_history_incomplete(self):
2051
2244
        repo, client = self.setup_fake_client_and_repository('quack')
2054
2247
            'success', ('history-incomplete', 10, 'rev-ten'))
2055
2248
        result = repo.get_rev_id_for_revno(5, (42, 'rev-foo'))
2056
2249
        self.assertEqual((False, (10, 'rev-ten')), result)
2057
 
        client.finished_test()
 
2250
        self.assertFinished(client)
2058
2251
 
2059
2252
    def test_history_incomplete_with_fallback(self):
2060
2253
        """A 'history-incomplete' response causes the fallback repository to be
2062
2255
        """
2063
2256
        # Make a repo with a fallback repo, both using a FakeClient.
2064
2257
        format = remote.response_tuple_to_repo_format(
2065
 
            ('yes', 'no', 'yes', 'fake-network-name'))
 
2258
            ('yes', 'no', 'yes', self.get_repo_format().network_name()))
2066
2259
        repo, client = self.setup_fake_client_and_repository('quack')
2067
2260
        repo._format = format
2068
2261
        fallback_repo, ignored = self.setup_fake_client_and_repository(
2069
2262
            'fallback')
2070
2263
        fallback_repo._client = client
 
2264
        fallback_repo._format = format
2071
2265
        repo.add_fallback_repository(fallback_repo)
2072
2266
        # First the client should ask the primary repo
2073
2267
        client.add_expected_call(
2080
2274
            'success', ('ok', 'rev-one'))
2081
2275
        result = repo.get_rev_id_for_revno(1, (42, 'rev-foo'))
2082
2276
        self.assertEqual((True, 'rev-one'), result)
2083
 
        client.finished_test()
 
2277
        self.assertFinished(client)
2084
2278
 
2085
2279
    def test_nosuchrevision(self):
2086
2280
        # 'nosuchrevision' is returned when the known-revid is not found in the
2092
2286
        self.assertRaises(
2093
2287
            errors.NoSuchRevision,
2094
2288
            repo.get_rev_id_for_revno, 5, (42, 'rev-foo'))
2095
 
        client.finished_test()
 
2289
        self.assertFinished(client)
 
2290
 
 
2291
    def test_branch_fallback_locking(self):
 
2292
        """RemoteBranch.get_rev_id takes a read lock, and tries to call the
 
2293
        get_rev_id_for_revno verb.  If the verb is unknown the VFS fallback
 
2294
        will be invoked, which will fail if the repo is unlocked.
 
2295
        """
 
2296
        self.setup_smart_server_with_call_log()
 
2297
        tree = self.make_branch_and_memory_tree('.')
 
2298
        tree.lock_write()
 
2299
        tree.add('')
 
2300
        rev1 = tree.commit('First')
 
2301
        rev2 = tree.commit('Second')
 
2302
        tree.unlock()
 
2303
        branch = tree.branch
 
2304
        self.assertFalse(branch.is_locked())
 
2305
        self.reset_smart_call_log()
 
2306
        verb = 'Repository.get_rev_id_for_revno'
 
2307
        self.disable_verb(verb)
 
2308
        self.assertEqual(rev1, branch.get_rev_id(1))
 
2309
        self.assertLength(1, [call for call in self.hpss_calls if
 
2310
                              call.call.method == verb])
2096
2311
 
2097
2312
 
2098
2313
class TestRepositoryIsShared(TestRemoteRepository):
2126
2341
        transport_path = 'quack'
2127
2342
        repo, client = self.setup_fake_client_and_repository(transport_path)
2128
2343
        client.add_success_response('ok', 'a token')
2129
 
        result = repo.lock_write()
 
2344
        token = repo.lock_write().repository_token
2130
2345
        self.assertEqual(
2131
2346
            [('call', 'Repository.lock_write', ('quack/', ''))],
2132
2347
            client._calls)
2133
 
        self.assertEqual('a token', result)
 
2348
        self.assertEqual('a token', token)
2134
2349
 
2135
2350
    def test_lock_write_already_locked(self):
2136
2351
        transport_path = 'quack'
2215
2430
        self.assertEqual([], client._calls)
2216
2431
 
2217
2432
 
2218
 
class TestRepositoryInsertStream(TestRemoteRepository):
2219
 
 
2220
 
    def test_unlocked_repo(self):
2221
 
        transport_path = 'quack'
2222
 
        repo, client = self.setup_fake_client_and_repository(transport_path)
2223
 
        client.add_expected_call(
2224
 
            'Repository.insert_stream', ('quack/', ''),
2225
 
            'success', ('ok',))
2226
 
        client.add_expected_call(
2227
 
            'Repository.insert_stream', ('quack/', ''),
2228
 
            'success', ('ok',))
2229
 
        sink = repo._get_sink()
2230
 
        fmt = repository.RepositoryFormat.get_default_format()
2231
 
        resume_tokens, missing_keys = sink.insert_stream([], fmt, [])
2232
 
        self.assertEqual([], resume_tokens)
2233
 
        self.assertEqual(set(), missing_keys)
2234
 
        client.finished_test()
2235
 
 
2236
 
    def test_locked_repo_with_no_lock_token(self):
2237
 
        transport_path = 'quack'
2238
 
        repo, client = self.setup_fake_client_and_repository(transport_path)
2239
 
        client.add_expected_call(
2240
 
            'Repository.lock_write', ('quack/', ''),
2241
 
            'success', ('ok', ''))
2242
 
        client.add_expected_call(
2243
 
            'Repository.insert_stream', ('quack/', ''),
2244
 
            'success', ('ok',))
2245
 
        client.add_expected_call(
2246
 
            'Repository.insert_stream', ('quack/', ''),
2247
 
            'success', ('ok',))
2248
 
        repo.lock_write()
2249
 
        sink = repo._get_sink()
2250
 
        fmt = repository.RepositoryFormat.get_default_format()
2251
 
        resume_tokens, missing_keys = sink.insert_stream([], fmt, [])
2252
 
        self.assertEqual([], resume_tokens)
2253
 
        self.assertEqual(set(), missing_keys)
2254
 
        client.finished_test()
2255
 
 
2256
 
    def test_locked_repo_with_lock_token(self):
2257
 
        transport_path = 'quack'
2258
 
        repo, client = self.setup_fake_client_and_repository(transport_path)
2259
 
        client.add_expected_call(
2260
 
            'Repository.lock_write', ('quack/', ''),
2261
 
            'success', ('ok', 'a token'))
2262
 
        client.add_expected_call(
2263
 
            'Repository.insert_stream_locked', ('quack/', '', 'a token'),
2264
 
            'success', ('ok',))
2265
 
        client.add_expected_call(
2266
 
            'Repository.insert_stream_locked', ('quack/', '', 'a token'),
2267
 
            'success', ('ok',))
2268
 
        repo.lock_write()
2269
 
        sink = repo._get_sink()
2270
 
        fmt = repository.RepositoryFormat.get_default_format()
2271
 
        resume_tokens, missing_keys = sink.insert_stream([], fmt, [])
2272
 
        self.assertEqual([], resume_tokens)
2273
 
        self.assertEqual(set(), missing_keys)
2274
 
        client.finished_test()
 
2433
class TestRepositoryInsertStreamBase(TestRemoteRepository):
 
2434
    """Base class for Repository.insert_stream and .insert_stream_1.19
 
2435
    tests.
 
2436
    """
 
2437
    
 
2438
    def checkInsertEmptyStream(self, repo, client):
 
2439
        """Insert an empty stream, checking the result.
 
2440
 
 
2441
        This checks that there are no resume_tokens or missing_keys, and that
 
2442
        the client is finished.
 
2443
        """
 
2444
        sink = repo._get_sink()
 
2445
        fmt = repository.RepositoryFormat.get_default_format()
 
2446
        resume_tokens, missing_keys = sink.insert_stream([], fmt, [])
 
2447
        self.assertEqual([], resume_tokens)
 
2448
        self.assertEqual(set(), missing_keys)
 
2449
        self.assertFinished(client)
 
2450
 
 
2451
 
 
2452
class TestRepositoryInsertStream(TestRepositoryInsertStreamBase):
 
2453
    """Tests for using Repository.insert_stream verb when the _1.19 variant is
 
2454
    not available.
 
2455
 
 
2456
    This test case is very similar to TestRepositoryInsertStream_1_19.
 
2457
    """
 
2458
 
 
2459
    def setUp(self):
 
2460
        TestRemoteRepository.setUp(self)
 
2461
        self.disable_verb('Repository.insert_stream_1.19')
 
2462
 
 
2463
    def test_unlocked_repo(self):
 
2464
        transport_path = 'quack'
 
2465
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
2466
        client.add_expected_call(
 
2467
            'Repository.insert_stream_1.19', ('quack/', ''),
 
2468
            'unknown', ('Repository.insert_stream_1.19',))
 
2469
        client.add_expected_call(
 
2470
            'Repository.insert_stream', ('quack/', ''),
 
2471
            'success', ('ok',))
 
2472
        client.add_expected_call(
 
2473
            'Repository.insert_stream', ('quack/', ''),
 
2474
            'success', ('ok',))
 
2475
        self.checkInsertEmptyStream(repo, client)
 
2476
 
 
2477
    def test_locked_repo_with_no_lock_token(self):
 
2478
        transport_path = 'quack'
 
2479
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
2480
        client.add_expected_call(
 
2481
            'Repository.lock_write', ('quack/', ''),
 
2482
            'success', ('ok', ''))
 
2483
        client.add_expected_call(
 
2484
            'Repository.insert_stream_1.19', ('quack/', ''),
 
2485
            'unknown', ('Repository.insert_stream_1.19',))
 
2486
        client.add_expected_call(
 
2487
            'Repository.insert_stream', ('quack/', ''),
 
2488
            'success', ('ok',))
 
2489
        client.add_expected_call(
 
2490
            'Repository.insert_stream', ('quack/', ''),
 
2491
            'success', ('ok',))
 
2492
        repo.lock_write()
 
2493
        self.checkInsertEmptyStream(repo, client)
 
2494
 
 
2495
    def test_locked_repo_with_lock_token(self):
 
2496
        transport_path = 'quack'
 
2497
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
2498
        client.add_expected_call(
 
2499
            'Repository.lock_write', ('quack/', ''),
 
2500
            'success', ('ok', 'a token'))
 
2501
        client.add_expected_call(
 
2502
            'Repository.insert_stream_1.19', ('quack/', '', 'a token'),
 
2503
            'unknown', ('Repository.insert_stream_1.19',))
 
2504
        client.add_expected_call(
 
2505
            'Repository.insert_stream_locked', ('quack/', '', 'a token'),
 
2506
            'success', ('ok',))
 
2507
        client.add_expected_call(
 
2508
            'Repository.insert_stream_locked', ('quack/', '', 'a token'),
 
2509
            'success', ('ok',))
 
2510
        repo.lock_write()
 
2511
        self.checkInsertEmptyStream(repo, client)
 
2512
 
 
2513
    def test_stream_with_inventory_deltas(self):
 
2514
        """'inventory-deltas' substreams cannot be sent to the
 
2515
        Repository.insert_stream verb, because not all servers that implement
 
2516
        that verb will accept them.  So when one is encountered the RemoteSink
 
2517
        immediately stops using that verb and falls back to VFS insert_stream.
 
2518
        """
 
2519
        transport_path = 'quack'
 
2520
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
2521
        client.add_expected_call(
 
2522
            'Repository.insert_stream_1.19', ('quack/', ''),
 
2523
            'unknown', ('Repository.insert_stream_1.19',))
 
2524
        client.add_expected_call(
 
2525
            'Repository.insert_stream', ('quack/', ''),
 
2526
            'success', ('ok',))
 
2527
        client.add_expected_call(
 
2528
            'Repository.insert_stream', ('quack/', ''),
 
2529
            'success', ('ok',))
 
2530
        # Create a fake real repository for insert_stream to fall back on, so
 
2531
        # that we can directly see the records the RemoteSink passes to the
 
2532
        # real sink.
 
2533
        class FakeRealSink:
 
2534
            def __init__(self):
 
2535
                self.records = []
 
2536
            def insert_stream(self, stream, src_format, resume_tokens):
 
2537
                for substream_kind, substream in stream:
 
2538
                    self.records.append(
 
2539
                        (substream_kind, [record.key for record in substream]))
 
2540
                return ['fake tokens'], ['fake missing keys']
 
2541
        fake_real_sink = FakeRealSink()
 
2542
        class FakeRealRepository:
 
2543
            def _get_sink(self):
 
2544
                return fake_real_sink
 
2545
            def is_in_write_group(self):
 
2546
                return False
 
2547
            def refresh_data(self):
 
2548
                return True
 
2549
        repo._real_repository = FakeRealRepository()
 
2550
        sink = repo._get_sink()
 
2551
        fmt = repository.RepositoryFormat.get_default_format()
 
2552
        stream = self.make_stream_with_inv_deltas(fmt)
 
2553
        resume_tokens, missing_keys = sink.insert_stream(stream, fmt, [])
 
2554
        # Every record from the first inventory delta should have been sent to
 
2555
        # the VFS sink.
 
2556
        expected_records = [
 
2557
            ('inventory-deltas', [('rev2',), ('rev3',)]),
 
2558
            ('texts', [('some-rev', 'some-file')])]
 
2559
        self.assertEqual(expected_records, fake_real_sink.records)
 
2560
        # The return values from the real sink's insert_stream are propagated
 
2561
        # back to the original caller.
 
2562
        self.assertEqual(['fake tokens'], resume_tokens)
 
2563
        self.assertEqual(['fake missing keys'], missing_keys)
 
2564
        self.assertFinished(client)
 
2565
 
 
2566
    def make_stream_with_inv_deltas(self, fmt):
 
2567
        """Make a simple stream with an inventory delta followed by more
 
2568
        records and more substreams to test that all records and substreams
 
2569
        from that point on are used.
 
2570
 
 
2571
        This sends, in order:
 
2572
           * inventories substream: rev1, rev2, rev3.  rev2 and rev3 are
 
2573
             inventory-deltas.
 
2574
           * texts substream: (some-rev, some-file)
 
2575
        """
 
2576
        # Define a stream using generators so that it isn't rewindable.
 
2577
        inv = inventory.Inventory(revision_id='rev1')
 
2578
        inv.root.revision = 'rev1'
 
2579
        def stream_with_inv_delta():
 
2580
            yield ('inventories', inventories_substream())
 
2581
            yield ('inventory-deltas', inventory_delta_substream())
 
2582
            yield ('texts', [
 
2583
                versionedfile.FulltextContentFactory(
 
2584
                    ('some-rev', 'some-file'), (), None, 'content')])
 
2585
        def inventories_substream():
 
2586
            # An empty inventory fulltext.  This will be streamed normally.
 
2587
            text = fmt._serializer.write_inventory_to_string(inv)
 
2588
            yield versionedfile.FulltextContentFactory(
 
2589
                ('rev1',), (), None, text)
 
2590
        def inventory_delta_substream():
 
2591
            # An inventory delta.  This can't be streamed via this verb, so it
 
2592
            # will trigger a fallback to VFS insert_stream.
 
2593
            entry = inv.make_entry(
 
2594
                'directory', 'newdir', inv.root.file_id, 'newdir-id')
 
2595
            entry.revision = 'ghost'
 
2596
            delta = [(None, 'newdir', 'newdir-id', entry)]
 
2597
            serializer = inventory_delta.InventoryDeltaSerializer(
 
2598
                versioned_root=True, tree_references=False)
 
2599
            lines = serializer.delta_to_lines('rev1', 'rev2', delta)
 
2600
            yield versionedfile.ChunkedContentFactory(
 
2601
                ('rev2',), (('rev1',)), None, lines)
 
2602
            # Another delta.
 
2603
            lines = serializer.delta_to_lines('rev1', 'rev3', delta)
 
2604
            yield versionedfile.ChunkedContentFactory(
 
2605
                ('rev3',), (('rev1',)), None, lines)
 
2606
        return stream_with_inv_delta()
 
2607
 
 
2608
 
 
2609
class TestRepositoryInsertStream_1_19(TestRepositoryInsertStreamBase):
 
2610
 
 
2611
    def test_unlocked_repo(self):
 
2612
        transport_path = 'quack'
 
2613
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
2614
        client.add_expected_call(
 
2615
            'Repository.insert_stream_1.19', ('quack/', ''),
 
2616
            'success', ('ok',))
 
2617
        client.add_expected_call(
 
2618
            'Repository.insert_stream_1.19', ('quack/', ''),
 
2619
            'success', ('ok',))
 
2620
        self.checkInsertEmptyStream(repo, client)
 
2621
 
 
2622
    def test_locked_repo_with_no_lock_token(self):
 
2623
        transport_path = 'quack'
 
2624
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
2625
        client.add_expected_call(
 
2626
            'Repository.lock_write', ('quack/', ''),
 
2627
            'success', ('ok', ''))
 
2628
        client.add_expected_call(
 
2629
            'Repository.insert_stream_1.19', ('quack/', ''),
 
2630
            'success', ('ok',))
 
2631
        client.add_expected_call(
 
2632
            'Repository.insert_stream_1.19', ('quack/', ''),
 
2633
            'success', ('ok',))
 
2634
        repo.lock_write()
 
2635
        self.checkInsertEmptyStream(repo, client)
 
2636
 
 
2637
    def test_locked_repo_with_lock_token(self):
 
2638
        transport_path = 'quack'
 
2639
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
2640
        client.add_expected_call(
 
2641
            'Repository.lock_write', ('quack/', ''),
 
2642
            'success', ('ok', 'a token'))
 
2643
        client.add_expected_call(
 
2644
            'Repository.insert_stream_1.19', ('quack/', '', 'a token'),
 
2645
            'success', ('ok',))
 
2646
        client.add_expected_call(
 
2647
            'Repository.insert_stream_1.19', ('quack/', '', 'a token'),
 
2648
            'success', ('ok',))
 
2649
        repo.lock_write()
 
2650
        self.checkInsertEmptyStream(repo, client)
2275
2651
 
2276
2652
 
2277
2653
class TestRepositoryTarball(TestRemoteRepository):
2313
2689
    """RemoteRepository.copy_content_into optimizations"""
2314
2690
 
2315
2691
    def test_copy_content_remote_to_local(self):
2316
 
        self.transport_server = server.SmartTCPServer_for_testing
 
2692
        self.transport_server = test_server.SmartTCPServer_for_testing
2317
2693
        src_repo = self.make_repository('repo1')
2318
2694
        src_repo = repository.Repository.open(self.get_url('repo1'))
2319
2695
        # At the moment the tarball-based copy_content_into can't write back
2362
2738
        client.add_expected_call(
2363
2739
            'PackRepository.autopack', ('quack/',), 'success', ('ok',))
2364
2740
        repo.autopack()
2365
 
        client.finished_test()
 
2741
        self.assertFinished(client)
2366
2742
 
2367
2743
    def test_ok_with_real_repo(self):
2368
2744
        """When the server returns 'ok' and there is a _real_repository, then
2467
2843
        expected_error = errors.NotBranchError(path=bzrdir.root_transport.base)
2468
2844
        self.assertEqual(expected_error, translated_error)
2469
2845
 
 
2846
    def test_nobranch_one_arg(self):
 
2847
        bzrdir = self.make_bzrdir('')
 
2848
        translated_error = self.translateTuple(
 
2849
            ('nobranch', 'extra detail'), bzrdir=bzrdir)
 
2850
        expected_error = errors.NotBranchError(
 
2851
            path=bzrdir.root_transport.base,
 
2852
            detail='extra detail')
 
2853
        self.assertEqual(expected_error, translated_error)
 
2854
 
2470
2855
    def test_LockContention(self):
2471
2856
        translated_error = self.translateTuple(('LockContention',))
2472
2857
        expected_error = errors.LockContention('(remote lock)')
2512
2897
        expected_error = errors.ReadError(path)
2513
2898
        self.assertEqual(expected_error, translated_error)
2514
2899
 
 
2900
    def test_IncompatibleRepositories(self):
 
2901
        translated_error = self.translateTuple(('IncompatibleRepositories',
 
2902
            "repo1", "repo2", "details here"))
 
2903
        expected_error = errors.IncompatibleRepositories("repo1", "repo2",
 
2904
            "details here")
 
2905
        self.assertEqual(expected_error, translated_error)
 
2906
 
2515
2907
    def test_PermissionDenied_no_args(self):
2516
2908
        path = 'a path'
2517
2909
        translated_error = self.translateTuple(('PermissionDenied',), path=path)
2578
2970
        # In addition to re-raising ErrorFromSmartServer, some debug info has
2579
2971
        # been muttered to the log file for developer to look at.
2580
2972
        self.assertContainsRe(
2581
 
            self._get_log(keep_log_file=True),
 
2973
            self.get_log(),
2582
2974
            "Missing key 'branch' in context")
2583
2975
 
2584
2976
    def test_path_missing(self):
2592
2984
        self.assertEqual(server_error, translated_error)
2593
2985
        # In addition to re-raising ErrorFromSmartServer, some debug info has
2594
2986
        # been muttered to the log file for developer to look at.
2595
 
        self.assertContainsRe(
2596
 
            self._get_log(keep_log_file=True), "Missing key 'path' in context")
 
2987
        self.assertContainsRe(self.get_log(), "Missing key 'path' in context")
2597
2988
 
2598
2989
 
2599
2990
class TestStacking(tests.TestCaseWithTransport):
2617
3008
        stacked_branch = self.make_branch('stacked', format='1.9')
2618
3009
        stacked_branch.set_stacked_on_url('../base')
2619
3010
        # start a server looking at this
2620
 
        smart_server = server.SmartTCPServer_for_testing()
2621
 
        smart_server.setUp()
2622
 
        self.addCleanup(smart_server.tearDown)
 
3011
        smart_server = test_server.SmartTCPServer_for_testing()
 
3012
        self.start_server(smart_server)
2623
3013
        remote_bzrdir = BzrDir.open(smart_server.get_url() + '/stacked')
2624
3014
        # can get its branch and repository
2625
3015
        remote_branch = remote_bzrdir.open_branch()
2647
3037
        tree1.commit('rev1', rev_id='rev1')
2648
3038
        tree2 = tree1.branch.bzrdir.sprout('tree2', stacked=True
2649
3039
            ).open_workingtree()
2650
 
        tree2.commit('local changes make me feel good.')
 
3040
        local_tree = tree2.branch.create_checkout('local')
 
3041
        local_tree.commit('local changes make me feel good.')
2651
3042
        branch2 = Branch.open(self.get_url('tree2'))
2652
3043
        branch2.lock_read()
2653
3044
        self.addCleanup(branch2.unlock)
2675
3066
                    result.append(content.key[-1])
2676
3067
        return result
2677
3068
 
2678
 
    def get_ordered_revs(self, format, order):
 
3069
    def get_ordered_revs(self, format, order, branch_factory=None):
2679
3070
        """Get a list of the revisions in a stream to format format.
2680
3071
 
2681
3072
        :param format: The format of the target.
2682
3073
        :param order: the order that target should have requested.
 
3074
        :param branch_factory: A callable to create a trunk and stacked branch
 
3075
            to fetch from. If none, self.prepare_stacked_remote_branch is used.
2683
3076
        :result: The revision ids in the stream, in the order seen,
2684
3077
            the topological order of revisions in the source.
2685
3078
        """
2687
3080
        target_repository_format = unordered_format.repository_format
2688
3081
        # Cross check
2689
3082
        self.assertEqual(order, target_repository_format._fetch_order)
2690
 
        trunk, stacked = self.prepare_stacked_remote_branch()
 
3083
        if branch_factory is None:
 
3084
            branch_factory = self.prepare_stacked_remote_branch
 
3085
        _, stacked = branch_factory()
2691
3086
        source = stacked.repository._get_source(target_repository_format)
2692
3087
        tip = stacked.last_revision()
2693
3088
        revs = stacked.repository.get_ancestry(tip)
2712
3107
        # from the server, then one from the backing branch.
2713
3108
        self.assertLength(2, self.hpss_calls)
2714
3109
 
 
3110
    def test_stacked_on_stacked_get_stream_unordered(self):
 
3111
        # Repository._get_source.get_stream() from a stacked repository which
 
3112
        # is itself stacked yields the full data from all three sources.
 
3113
        def make_stacked_stacked():
 
3114
            _, stacked = self.prepare_stacked_remote_branch()
 
3115
            tree = stacked.bzrdir.sprout('tree3', stacked=True
 
3116
                ).open_workingtree()
 
3117
            local_tree = tree.branch.create_checkout('local-tree3')
 
3118
            local_tree.commit('more local changes are better')
 
3119
            branch = Branch.open(self.get_url('tree3'))
 
3120
            branch.lock_read()
 
3121
            self.addCleanup(branch.unlock)
 
3122
            return None, branch
 
3123
        rev_ord, expected_revs = self.get_ordered_revs('1.9', 'unordered',
 
3124
            branch_factory=make_stacked_stacked)
 
3125
        self.assertEqual(set(expected_revs), set(rev_ord))
 
3126
        # Getting unordered results should have made a streaming data request
 
3127
        # from the server, and one from each backing repo
 
3128
        self.assertLength(3, self.hpss_calls)
 
3129
 
2715
3130
    def test_stacked_get_stream_topological(self):
2716
3131
        # Repository._get_source.get_stream() from a stacked repository with
2717
3132
        # topological sorting yields the full data from both stacked and
2718
3133
        # stacked upon sources in topological order.
2719
3134
        rev_ord, expected_revs = self.get_ordered_revs('knit', 'topological')
2720
3135
        self.assertEqual(expected_revs, rev_ord)
2721
 
        # Getting topological sort requires VFS calls still
2722
 
        self.assertLength(12, self.hpss_calls)
 
3136
        # Getting topological sort requires VFS calls still - one of which is
 
3137
        # pushing up from the bound branch.
 
3138
        self.assertLength(13, self.hpss_calls)
2723
3139
 
2724
3140
    def test_stacked_get_stream_groupcompress(self):
2725
3141
        # Repository._get_source.get_stream() from a stacked repository with
2754
3170
        super(TestRemoteBranchEffort, self).setUp()
2755
3171
        # Create a smart server that publishes whatever the backing VFS server
2756
3172
        # does.
2757
 
        self.smart_server = server.SmartTCPServer_for_testing()
2758
 
        self.smart_server.setUp(self.get_server())
2759
 
        self.addCleanup(self.smart_server.tearDown)
 
3173
        self.smart_server = test_server.SmartTCPServer_for_testing()
 
3174
        self.start_server(self.smart_server, self.get_server())
2760
3175
        # Log all HPSS calls into self.hpss_calls.
2761
3176
        _SmartClient.hooks.install_named_hook(
2762
3177
            'call', self.capture_hpss_call, None)