~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: 2009-04-09 20:23:07 UTC
  • mfrom: (4265.1.4 bbc-merge)
  • Revision ID: pqm@pqm.ubuntu.com-20090409202307-n0depb16qepoe21o
(jam) Change _fetch_uses_deltas = False for CHK repos until we can
        write a better fix.

Show diffs side-by-side

added added

removed removed

Lines of Context:
31
31
    config,
32
32
    errors,
33
33
    graph,
34
 
    inventory,
35
 
    inventory_delta,
36
34
    pack,
37
35
    remote,
38
36
    repository,
40
38
    tests,
41
39
    treebuilder,
42
40
    urlutils,
43
 
    versionedfile,
44
41
    )
45
42
from bzrlib.branch import Branch
46
43
from bzrlib.bzrdir import BzrDir, BzrDirFormat
61
58
    condition_isinstance,
62
59
    split_suite_by_condition,
63
60
    multiply_tests,
64
 
    KnownFailure,
65
61
    )
66
62
from bzrlib.transport import get_transport, http
67
63
from bzrlib.transport.memory import MemoryTransport
157
153
        r = BzrDir.open_from_transport(t.clone('stackable')).open_repository()
158
154
        self.assertTrue(r._format.supports_external_lookups)
159
155
 
160
 
    def test_remote_branch_set_append_revisions_only(self):
161
 
        # Make a format 1.9 branch, which supports append_revisions_only
162
 
        branch = self.make_branch('branch', format='1.9')
163
 
        config = branch.get_config()
164
 
        branch.set_append_revisions_only(True)
165
 
        self.assertEqual(
166
 
            'True', config.get_user_option('append_revisions_only'))
167
 
        branch.set_append_revisions_only(False)
168
 
        self.assertEqual(
169
 
            'False', config.get_user_option('append_revisions_only'))
170
 
 
171
 
    def test_remote_branch_set_append_revisions_only_upgrade_reqd(self):
172
 
        branch = self.make_branch('branch', format='knit')
173
 
        config = branch.get_config()
174
 
        self.assertRaises(
175
 
            errors.UpgradeRequired, branch.set_append_revisions_only, True)
176
 
 
177
156
 
178
157
class FakeProtocol(object):
179
158
    """Lookalike SmartClientRequestProtocolOne allowing body reading tests."""
335
314
        reference_bzrdir_format = bzrdir.format_registry.get('default')()
336
315
        return reference_bzrdir_format.repository_format
337
316
 
338
 
    def assertFinished(self, fake_client):
339
 
        """Assert that all of a FakeClient's expected calls have occurred."""
340
 
        fake_client.finished_test()
 
317
    def disable_verb(self, verb):
 
318
        """Disable a verb for one test."""
 
319
        request_handlers = smart.request.request_handlers
 
320
        orig_method = request_handlers.get(verb)
 
321
        request_handlers.remove(verb)
 
322
        def restoreVerb():
 
323
            request_handlers.register(verb, orig_method)
 
324
        self.addCleanup(restoreVerb)
341
325
 
342
326
 
343
327
class Test_ClientMedium_remote_path_from_transport(tests.TestCase):
444
428
        self.assertEqual(bzrdir.BzrDirMetaFormat1, type(result))
445
429
        self.assertEqual(expected._repository_format, result._repository_format)
446
430
        self.assertEqual(expected._branch_format, result._branch_format)
447
 
        self.assertFinished(client)
 
431
        client.finished_test()
448
432
 
449
433
    def test_current_server(self):
450
434
        transport = self.get_transport('.')
465
449
        self.assertEqual(bzrdir.BzrDirMetaFormat1, type(result))
466
450
        self.assertEqual(None, result._repository_format)
467
451
        self.assertEqual(None, result._branch_format)
468
 
        self.assertFinished(client)
 
452
        client.finished_test()
469
453
 
470
454
 
471
455
class TestBzrDirOpenBranch(TestRemote):
504
488
        result = bzrdir.open_branch()
505
489
        self.assertIsInstance(result, RemoteBranch)
506
490
        self.assertEqual(bzrdir, result.bzrdir)
507
 
        self.assertFinished(client)
 
491
        client.finished_test()
508
492
 
509
493
    def test_branch_missing(self):
510
494
        transport = MemoryTransport()
557
541
        bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
558
542
            _client=client)
559
543
        result = bzrdir.open_branch()
560
 
        self.assertFinished(client)
 
544
        client.finished_test()
561
545
 
562
546
    def check_open_repository(self, rich_root, subtrees, external_lookup='no'):
563
547
        reference_format = self.get_repo_format()
661
645
        network_name = reference_format.network_name()
662
646
        client.add_expected_call(
663
647
            'BzrDir.create_repository', ('quack/',
664
 
                'Bazaar repository format 2a (needs bzr 1.16 or later)\n',
665
 
                'False'),
666
 
            'success', ('ok', 'yes', 'yes', 'yes', network_name))
 
648
                'Bazaar pack repository format 1 (needs bzr 0.92)\n', 'False'),
 
649
            'success', ('ok', 'no', 'no', 'no', network_name))
667
650
        a_bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
668
651
            _client=client)
669
652
        repo = a_bzrdir.create_repository()
671
654
        self.assertIsInstance(repo, remote.RemoteRepository)
672
655
        # its format should have the settings from the response
673
656
        format = repo._format
674
 
        self.assertTrue(format.rich_root_data)
675
 
        self.assertTrue(format.supports_tree_reference)
676
 
        self.assertTrue(format.supports_external_lookups)
 
657
        self.assertFalse(format.rich_root_data)
 
658
        self.assertFalse(format.supports_tree_reference)
 
659
        self.assertFalse(format.supports_external_lookups)
677
660
        self.assertEqual(network_name, format.network_name())
678
661
 
679
662
 
758
741
        self.assertEqual(network_name, repo._format.network_name())
759
742
 
760
743
 
761
 
class TestBzrDirFormatInitializeEx(TestRemote):
762
 
 
763
 
    def test_success(self):
764
 
        """Simple test for typical successful call."""
765
 
        fmt = bzrdir.RemoteBzrDirFormat()
766
 
        default_format_name = BzrDirFormat.get_default_format().network_name()
767
 
        transport = self.get_transport()
768
 
        client = FakeClient(transport.base)
769
 
        client.add_expected_call(
770
 
            'BzrDirFormat.initialize_ex_1.16',
771
 
                (default_format_name, 'path', 'False', 'False', 'False', '',
772
 
                 '', '', '', 'False'),
773
 
            'success',
774
 
                ('.', 'no', 'no', 'yes', 'repo fmt', 'repo bzrdir fmt',
775
 
                 'bzrdir fmt', 'False', '', '', 'repo lock token'))
776
 
        # XXX: It would be better to call fmt.initialize_on_transport_ex, but
777
 
        # it's currently hard to test that without supplying a real remote
778
 
        # transport connected to a real server.
779
 
        result = fmt._initialize_on_transport_ex_rpc(client, 'path',
780
 
            transport, False, False, False, None, None, None, None, False)
781
 
        self.assertFinished(client)
782
 
 
783
 
    def test_error(self):
784
 
        """Error responses are translated, e.g. 'PermissionDenied' raises the
785
 
        corresponding error from the client.
786
 
        """
787
 
        fmt = bzrdir.RemoteBzrDirFormat()
788
 
        default_format_name = BzrDirFormat.get_default_format().network_name()
789
 
        transport = self.get_transport()
790
 
        client = FakeClient(transport.base)
791
 
        client.add_expected_call(
792
 
            'BzrDirFormat.initialize_ex_1.16',
793
 
                (default_format_name, 'path', 'False', 'False', 'False', '',
794
 
                 '', '', '', 'False'),
795
 
            'error',
796
 
                ('PermissionDenied', 'path', 'extra info'))
797
 
        # XXX: It would be better to call fmt.initialize_on_transport_ex, but
798
 
        # it's currently hard to test that without supplying a real remote
799
 
        # transport connected to a real server.
800
 
        err = self.assertRaises(errors.PermissionDenied,
801
 
            fmt._initialize_on_transport_ex_rpc, client, 'path', transport,
802
 
            False, False, False, None, None, None, None, False)
803
 
        self.assertEqual('path', err.path)
804
 
        self.assertEqual(': extra info', err.extra)
805
 
        self.assertFinished(client)
806
 
 
807
 
    def test_error_from_real_server(self):
808
 
        """Integration test for error translation."""
809
 
        transport = self.make_smart_server('foo')
810
 
        transport = transport.clone('no-such-path')
811
 
        fmt = bzrdir.RemoteBzrDirFormat()
812
 
        err = self.assertRaises(errors.NoSuchFile,
813
 
            fmt.initialize_on_transport_ex, transport, create_prefix=False)
814
 
 
815
 
 
816
744
class OldSmartClient(object):
817
745
    """A fake smart client for test_old_version that just returns a version one
818
746
    response to the 'hello' (query version) command.
841
769
        return OldSmartClient()
842
770
 
843
771
 
844
 
class RemoteBzrDirTestCase(TestRemote):
845
 
 
846
 
    def make_remote_bzrdir(self, transport, client):
847
 
        """Make a RemotebzrDir using 'client' as the _client."""
848
 
        return RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
849
 
            _client=client)
850
 
 
851
 
 
852
 
class RemoteBranchTestCase(RemoteBzrDirTestCase):
 
772
class RemoteBranchTestCase(TestRemote):
853
773
 
854
774
    def make_remote_branch(self, transport, client):
855
775
        """Make a RemoteBranch using 'client' as its _SmartClient.
860
780
        # we do not want bzrdir to make any remote calls, so use False as its
861
781
        # _client.  If it tries to make a remote call, this will fail
862
782
        # immediately.
863
 
        bzrdir = self.make_remote_bzrdir(transport, False)
 
783
        bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
 
784
            _client=False)
864
785
        repo = RemoteRepository(bzrdir, None, _client=client)
865
786
        branch_format = self.get_branch_format()
866
787
        format = RemoteBranchFormat(network_name=branch_format.network_name())
883
804
        transport = transport.clone('quack')
884
805
        branch = self.make_remote_branch(transport, client)
885
806
        result = branch.get_parent()
886
 
        self.assertFinished(client)
 
807
        client.finished_test()
887
808
        self.assertEqual(None, result)
888
809
 
889
810
    def test_parent_relative(self):
915
836
        branch = self.make_remote_branch(transport, client)
916
837
        result = branch.get_parent()
917
838
        self.assertEqual('http://foo/', result)
918
 
        self.assertFinished(client)
919
 
 
920
 
 
921
 
class TestBranchSetParentLocation(RemoteBranchTestCase):
922
 
 
923
 
    def test_no_parent(self):
924
 
        # We call the verb when setting parent to None
925
 
        transport = MemoryTransport()
926
 
        client = FakeClient(transport.base)
927
 
        client.add_expected_call(
928
 
            'Branch.get_stacked_on_url', ('quack/',),
929
 
            'error', ('NotStacked',))
930
 
        client.add_expected_call(
931
 
            'Branch.set_parent_location', ('quack/', 'b', 'r', ''),
932
 
            'success', ())
933
 
        transport.mkdir('quack')
934
 
        transport = transport.clone('quack')
935
 
        branch = self.make_remote_branch(transport, client)
936
 
        branch._lock_token = 'b'
937
 
        branch._repo_lock_token = 'r'
938
 
        branch._set_parent_location(None)
939
 
        self.assertFinished(client)
940
 
 
941
 
    def test_parent(self):
942
 
        transport = MemoryTransport()
943
 
        client = FakeClient(transport.base)
944
 
        client.add_expected_call(
945
 
            'Branch.get_stacked_on_url', ('kwaak/',),
946
 
            'error', ('NotStacked',))
947
 
        client.add_expected_call(
948
 
            'Branch.set_parent_location', ('kwaak/', 'b', 'r', 'foo'),
949
 
            'success', ())
950
 
        transport.mkdir('kwaak')
951
 
        transport = transport.clone('kwaak')
952
 
        branch = self.make_remote_branch(transport, client)
953
 
        branch._lock_token = 'b'
954
 
        branch._repo_lock_token = 'r'
955
 
        branch._set_parent_location('foo')
956
 
        self.assertFinished(client)
957
 
 
958
 
    def test_backwards_compat(self):
959
 
        self.setup_smart_server_with_call_log()
960
 
        branch = self.make_branch('.')
961
 
        self.reset_smart_call_log()
962
 
        verb = 'Branch.set_parent_location'
963
 
        self.disable_verb(verb)
964
 
        branch.set_parent('http://foo/')
965
 
        self.assertLength(12, self.hpss_calls)
966
839
 
967
840
 
968
841
class TestBranchGetTagsBytes(RemoteBranchTestCase):
991
864
        transport = transport.clone('quack')
992
865
        branch = self.make_remote_branch(transport, client)
993
866
        result = branch.tags.get_tag_dict()
994
 
        self.assertFinished(client)
 
867
        client.finished_test()
995
868
        self.assertEqual({}, result)
996
869
 
997
870
 
1011
884
        transport = transport.clone('quack')
1012
885
        branch = self.make_remote_branch(transport, client)
1013
886
        result = branch.last_revision_info()
1014
 
        self.assertFinished(client)
 
887
        client.finished_test()
1015
888
        self.assertEqual((0, NULL_REVISION), result)
1016
889
 
1017
890
    def test_non_empty_branch(self):
1092
965
        branch = bzrdir.open_branch()
1093
966
        result = branch.get_stacked_on_url()
1094
967
        self.assertEqual('../base', result)
1095
 
        self.assertFinished(client)
 
968
        client.finished_test()
1096
969
        # it's in the fallback list both for the RemoteRepository and its vfs
1097
970
        # repository
1098
971
        self.assertEqual(1, len(branch.repository._fallback_repositories))
1125
998
        branch = bzrdir.open_branch()
1126
999
        result = branch.get_stacked_on_url()
1127
1000
        self.assertEqual('../base', result)
1128
 
        self.assertFinished(client)
 
1001
        client.finished_test()
1129
1002
        # it's in the fallback list both for the RemoteRepository.
1130
1003
        self.assertEqual(1, len(branch.repository._fallback_repositories))
1131
1004
        # And we haven't had to construct a real repository.
1166
1039
        result = branch.set_revision_history([])
1167
1040
        branch.unlock()
1168
1041
        self.assertEqual(None, result)
1169
 
        self.assertFinished(client)
 
1042
        client.finished_test()
1170
1043
 
1171
1044
    def test_set_nonempty(self):
1172
1045
        # set_revision_history([rev-id1, ..., rev-idN]) is translated to calling
1204
1077
        result = branch.set_revision_history(['rev-id1', 'rev-id2'])
1205
1078
        branch.unlock()
1206
1079
        self.assertEqual(None, result)
1207
 
        self.assertFinished(client)
 
1080
        client.finished_test()
1208
1081
 
1209
1082
    def test_no_such_revision(self):
1210
1083
        transport = MemoryTransport()
1239
1112
        self.assertRaises(
1240
1113
            errors.NoSuchRevision, branch.set_revision_history, ['rev-id'])
1241
1114
        branch.unlock()
1242
 
        self.assertFinished(client)
 
1115
        client.finished_test()
1243
1116
 
1244
1117
    def test_tip_change_rejected(self):
1245
1118
        """TipChangeRejected responses cause a TipChangeRejected exception to
1282
1155
        self.assertIsInstance(err.msg, unicode)
1283
1156
        self.assertEqual(rejection_msg_unicode, err.msg)
1284
1157
        branch.unlock()
1285
 
        self.assertFinished(client)
 
1158
        client.finished_test()
1286
1159
 
1287
1160
 
1288
1161
class TestBranchSetLastRevisionInfo(RemoteBranchTestCase):
1398
1271
        self.assertEqual(
1399
1272
            [('set_last_revision_info', 1234, 'a-revision-id')],
1400
1273
            real_branch.calls)
1401
 
        self.assertFinished(client)
 
1274
        client.finished_test()
1402
1275
 
1403
1276
    def test_unexpected_error(self):
1404
1277
        # If the server sends an error the client doesn't understand, it gets
1512
1385
        config = branch._get_config()
1513
1386
        config.set_option('foo', 'bar')
1514
1387
        branch.unlock()
1515
 
        self.assertFinished(client)
 
1388
        client.finished_test()
1516
1389
 
1517
1390
    def test_backwards_compat_set_option(self):
1518
1391
        self.setup_smart_server_with_call_log()
1542
1415
        transport = transport.clone('quack')
1543
1416
        branch = self.make_remote_branch(transport, client)
1544
1417
        self.assertRaises(errors.UnlockableTransport, branch.lock_write)
1545
 
        self.assertFinished(client)
1546
 
 
1547
 
 
1548
 
class TestBzrDirGetSetConfig(RemoteBzrDirTestCase):
1549
 
 
1550
 
    def test__get_config(self):
1551
 
        client = FakeClient()
1552
 
        client.add_success_response_with_body('default_stack_on = /\n', 'ok')
1553
 
        transport = MemoryTransport()
1554
 
        bzrdir = self.make_remote_bzrdir(transport, client)
1555
 
        config = bzrdir.get_config()
1556
 
        self.assertEqual('/', config.get_default_stack_on())
1557
 
        self.assertEqual(
1558
 
            [('call_expecting_body', 'BzrDir.get_config_file', ('memory:///',))],
1559
 
            client._calls)
1560
 
 
1561
 
    def test_set_option_uses_vfs(self):
1562
 
        self.setup_smart_server_with_call_log()
1563
 
        bzrdir = self.make_bzrdir('.')
1564
 
        self.reset_smart_call_log()
1565
 
        config = bzrdir.get_config()
1566
 
        config.set_default_stack_on('/')
1567
 
        self.assertLength(3, self.hpss_calls)
1568
 
 
1569
 
    def test_backwards_compat_get_option(self):
1570
 
        self.setup_smart_server_with_call_log()
1571
 
        bzrdir = self.make_bzrdir('.')
1572
 
        verb = 'BzrDir.get_config_file'
1573
 
        self.disable_verb(verb)
1574
 
        self.reset_smart_call_log()
1575
 
        self.assertEqual(None,
1576
 
            bzrdir._get_config().get_option('default_stack_on'))
1577
 
        self.assertLength(3, self.hpss_calls)
 
1418
        client.finished_test()
1578
1419
 
1579
1420
 
1580
1421
class TestTransportIsReadonly(tests.TestCase):
2035
1876
        self.assertEqual(('AnUnexpectedError',), e.error_tuple)
2036
1877
 
2037
1878
 
2038
 
class TestRepositoryGetRevIdForRevno(TestRemoteRepository):
2039
 
 
2040
 
    def test_ok(self):
2041
 
        repo, client = self.setup_fake_client_and_repository('quack')
2042
 
        client.add_expected_call(
2043
 
            'Repository.get_rev_id_for_revno', ('quack/', 5, (42, 'rev-foo')),
2044
 
            'success', ('ok', 'rev-five'))
2045
 
        result = repo.get_rev_id_for_revno(5, (42, 'rev-foo'))
2046
 
        self.assertEqual((True, 'rev-five'), result)
2047
 
        self.assertFinished(client)
2048
 
 
2049
 
    def test_history_incomplete(self):
2050
 
        repo, client = self.setup_fake_client_and_repository('quack')
2051
 
        client.add_expected_call(
2052
 
            'Repository.get_rev_id_for_revno', ('quack/', 5, (42, 'rev-foo')),
2053
 
            'success', ('history-incomplete', 10, 'rev-ten'))
2054
 
        result = repo.get_rev_id_for_revno(5, (42, 'rev-foo'))
2055
 
        self.assertEqual((False, (10, 'rev-ten')), result)
2056
 
        self.assertFinished(client)
2057
 
 
2058
 
    def test_history_incomplete_with_fallback(self):
2059
 
        """A 'history-incomplete' response causes the fallback repository to be
2060
 
        queried too, if one is set.
2061
 
        """
2062
 
        # Make a repo with a fallback repo, both using a FakeClient.
2063
 
        format = remote.response_tuple_to_repo_format(
2064
 
            ('yes', 'no', 'yes', 'fake-network-name'))
2065
 
        repo, client = self.setup_fake_client_and_repository('quack')
2066
 
        repo._format = format
2067
 
        fallback_repo, ignored = self.setup_fake_client_and_repository(
2068
 
            'fallback')
2069
 
        fallback_repo._client = client
2070
 
        repo.add_fallback_repository(fallback_repo)
2071
 
        # First the client should ask the primary repo
2072
 
        client.add_expected_call(
2073
 
            'Repository.get_rev_id_for_revno', ('quack/', 1, (42, 'rev-foo')),
2074
 
            'success', ('history-incomplete', 2, 'rev-two'))
2075
 
        # Then it should ask the fallback, using revno/revid from the
2076
 
        # history-incomplete response as the known revno/revid.
2077
 
        client.add_expected_call(
2078
 
            'Repository.get_rev_id_for_revno',('fallback/', 1, (2, 'rev-two')),
2079
 
            'success', ('ok', 'rev-one'))
2080
 
        result = repo.get_rev_id_for_revno(1, (42, 'rev-foo'))
2081
 
        self.assertEqual((True, 'rev-one'), result)
2082
 
        self.assertFinished(client)
2083
 
 
2084
 
    def test_nosuchrevision(self):
2085
 
        # 'nosuchrevision' is returned when the known-revid is not found in the
2086
 
        # remote repo.  The client translates that response to NoSuchRevision.
2087
 
        repo, client = self.setup_fake_client_and_repository('quack')
2088
 
        client.add_expected_call(
2089
 
            'Repository.get_rev_id_for_revno', ('quack/', 5, (42, 'rev-foo')),
2090
 
            'error', ('nosuchrevision', 'rev-foo'))
2091
 
        self.assertRaises(
2092
 
            errors.NoSuchRevision,
2093
 
            repo.get_rev_id_for_revno, 5, (42, 'rev-foo'))
2094
 
        self.assertFinished(client)
2095
 
 
2096
 
 
2097
1879
class TestRepositoryIsShared(TestRemoteRepository):
2098
1880
 
2099
1881
    def test_is_shared(self):
2214
1996
        self.assertEqual([], client._calls)
2215
1997
 
2216
1998
 
2217
 
class TestRepositoryInsertStreamBase(TestRemoteRepository):
2218
 
    """Base class for Repository.insert_stream and .insert_stream_1.19
2219
 
    tests.
2220
 
    """
2221
 
    
2222
 
    def checkInsertEmptyStream(self, repo, client):
2223
 
        """Insert an empty stream, checking the result.
2224
 
 
2225
 
        This checks that there are no resume_tokens or missing_keys, and that
2226
 
        the client is finished.
2227
 
        """
2228
 
        sink = repo._get_sink()
2229
 
        fmt = repository.RepositoryFormat.get_default_format()
2230
 
        resume_tokens, missing_keys = sink.insert_stream([], fmt, [])
2231
 
        self.assertEqual([], resume_tokens)
2232
 
        self.assertEqual(set(), missing_keys)
2233
 
        self.assertFinished(client)
2234
 
 
2235
 
 
2236
 
class TestRepositoryInsertStream(TestRepositoryInsertStreamBase):
2237
 
    """Tests for using Repository.insert_stream verb when the _1.19 variant is
2238
 
    not available.
2239
 
 
2240
 
    This test case is very similar to TestRepositoryInsertStream_1_19.
2241
 
    """
2242
 
 
2243
 
    def setUp(self):
2244
 
        TestRemoteRepository.setUp(self)
2245
 
        self.disable_verb('Repository.insert_stream_1.19')
2246
 
 
2247
 
    def test_unlocked_repo(self):
2248
 
        transport_path = 'quack'
2249
 
        repo, client = self.setup_fake_client_and_repository(transport_path)
2250
 
        client.add_expected_call(
2251
 
            'Repository.insert_stream_1.19', ('quack/', ''),
2252
 
            'unknown', ('Repository.insert_stream_1.19',))
2253
 
        client.add_expected_call(
2254
 
            'Repository.insert_stream', ('quack/', ''),
2255
 
            'success', ('ok',))
2256
 
        client.add_expected_call(
2257
 
            'Repository.insert_stream', ('quack/', ''),
2258
 
            'success', ('ok',))
2259
 
        self.checkInsertEmptyStream(repo, client)
2260
 
 
2261
 
    def test_locked_repo_with_no_lock_token(self):
2262
 
        transport_path = 'quack'
2263
 
        repo, client = self.setup_fake_client_and_repository(transport_path)
2264
 
        client.add_expected_call(
2265
 
            'Repository.lock_write', ('quack/', ''),
2266
 
            'success', ('ok', ''))
2267
 
        client.add_expected_call(
2268
 
            'Repository.insert_stream_1.19', ('quack/', ''),
2269
 
            'unknown', ('Repository.insert_stream_1.19',))
2270
 
        client.add_expected_call(
2271
 
            'Repository.insert_stream', ('quack/', ''),
2272
 
            'success', ('ok',))
2273
 
        client.add_expected_call(
2274
 
            'Repository.insert_stream', ('quack/', ''),
2275
 
            'success', ('ok',))
2276
 
        repo.lock_write()
2277
 
        self.checkInsertEmptyStream(repo, client)
2278
 
 
2279
 
    def test_locked_repo_with_lock_token(self):
2280
 
        transport_path = 'quack'
2281
 
        repo, client = self.setup_fake_client_and_repository(transport_path)
2282
 
        client.add_expected_call(
2283
 
            'Repository.lock_write', ('quack/', ''),
2284
 
            'success', ('ok', 'a token'))
2285
 
        client.add_expected_call(
2286
 
            'Repository.insert_stream_1.19', ('quack/', '', 'a token'),
2287
 
            'unknown', ('Repository.insert_stream_1.19',))
2288
 
        client.add_expected_call(
2289
 
            'Repository.insert_stream_locked', ('quack/', '', 'a token'),
2290
 
            'success', ('ok',))
2291
 
        client.add_expected_call(
2292
 
            'Repository.insert_stream_locked', ('quack/', '', 'a token'),
2293
 
            'success', ('ok',))
2294
 
        repo.lock_write()
2295
 
        self.checkInsertEmptyStream(repo, client)
2296
 
 
2297
 
    def test_stream_with_inventory_deltas(self):
2298
 
        """'inventory-deltas' substreams cannot be sent to the
2299
 
        Repository.insert_stream verb, because not all servers that implement
2300
 
        that verb will accept them.  So when one is encountered the RemoteSink
2301
 
        immediately stops using that verb and falls back to VFS insert_stream.
2302
 
        """
2303
 
        transport_path = 'quack'
2304
 
        repo, client = self.setup_fake_client_and_repository(transport_path)
2305
 
        client.add_expected_call(
2306
 
            'Repository.insert_stream_1.19', ('quack/', ''),
2307
 
            'unknown', ('Repository.insert_stream_1.19',))
2308
 
        client.add_expected_call(
2309
 
            'Repository.insert_stream', ('quack/', ''),
2310
 
            'success', ('ok',))
2311
 
        client.add_expected_call(
2312
 
            'Repository.insert_stream', ('quack/', ''),
2313
 
            'success', ('ok',))
2314
 
        # Create a fake real repository for insert_stream to fall back on, so
2315
 
        # that we can directly see the records the RemoteSink passes to the
2316
 
        # real sink.
2317
 
        class FakeRealSink:
2318
 
            def __init__(self):
2319
 
                self.records = []
2320
 
            def insert_stream(self, stream, src_format, resume_tokens):
2321
 
                for substream_kind, substream in stream:
2322
 
                    self.records.append(
2323
 
                        (substream_kind, [record.key for record in substream]))
2324
 
                return ['fake tokens'], ['fake missing keys']
2325
 
        fake_real_sink = FakeRealSink()
2326
 
        class FakeRealRepository:
2327
 
            def _get_sink(self):
2328
 
                return fake_real_sink
2329
 
        repo._real_repository = FakeRealRepository()
2330
 
        sink = repo._get_sink()
2331
 
        fmt = repository.RepositoryFormat.get_default_format()
2332
 
        stream = self.make_stream_with_inv_deltas(fmt)
2333
 
        resume_tokens, missing_keys = sink.insert_stream(stream, fmt, [])
2334
 
        # Every record from the first inventory delta should have been sent to
2335
 
        # the VFS sink.
2336
 
        expected_records = [
2337
 
            ('inventory-deltas', [('rev2',), ('rev3',)]),
2338
 
            ('texts', [('some-rev', 'some-file')])]
2339
 
        self.assertEqual(expected_records, fake_real_sink.records)
2340
 
        # The return values from the real sink's insert_stream are propagated
2341
 
        # back to the original caller.
2342
 
        self.assertEqual(['fake tokens'], resume_tokens)
2343
 
        self.assertEqual(['fake missing keys'], missing_keys)
2344
 
        self.assertFinished(client)
2345
 
 
2346
 
    def make_stream_with_inv_deltas(self, fmt):
2347
 
        """Make a simple stream with an inventory delta followed by more
2348
 
        records and more substreams to test that all records and substreams
2349
 
        from that point on are used.
2350
 
 
2351
 
        This sends, in order:
2352
 
           * inventories substream: rev1, rev2, rev3.  rev2 and rev3 are
2353
 
             inventory-deltas.
2354
 
           * texts substream: (some-rev, some-file)
2355
 
        """
2356
 
        # Define a stream using generators so that it isn't rewindable.
2357
 
        inv = inventory.Inventory(revision_id='rev1')
2358
 
        inv.root.revision = 'rev1'
2359
 
        def stream_with_inv_delta():
2360
 
            yield ('inventories', inventories_substream())
2361
 
            yield ('inventory-deltas', inventory_delta_substream())
2362
 
            yield ('texts', [
2363
 
                versionedfile.FulltextContentFactory(
2364
 
                    ('some-rev', 'some-file'), (), None, 'content')])
2365
 
        def inventories_substream():
2366
 
            # An empty inventory fulltext.  This will be streamed normally.
2367
 
            text = fmt._serializer.write_inventory_to_string(inv)
2368
 
            yield versionedfile.FulltextContentFactory(
2369
 
                ('rev1',), (), None, text)
2370
 
        def inventory_delta_substream():
2371
 
            # An inventory delta.  This can't be streamed via this verb, so it
2372
 
            # will trigger a fallback to VFS insert_stream.
2373
 
            entry = inv.make_entry(
2374
 
                'directory', 'newdir', inv.root.file_id, 'newdir-id')
2375
 
            entry.revision = 'ghost'
2376
 
            delta = [(None, 'newdir', 'newdir-id', entry)]
2377
 
            serializer = inventory_delta.InventoryDeltaSerializer(
2378
 
                versioned_root=True, tree_references=False)
2379
 
            lines = serializer.delta_to_lines('rev1', 'rev2', delta)
2380
 
            yield versionedfile.ChunkedContentFactory(
2381
 
                ('rev2',), (('rev1',)), None, lines)
2382
 
            # Another delta.
2383
 
            lines = serializer.delta_to_lines('rev1', 'rev3', delta)
2384
 
            yield versionedfile.ChunkedContentFactory(
2385
 
                ('rev3',), (('rev1',)), None, lines)
2386
 
        return stream_with_inv_delta()
2387
 
 
2388
 
 
2389
 
class TestRepositoryInsertStream_1_19(TestRepositoryInsertStreamBase):
2390
 
 
2391
 
    def test_unlocked_repo(self):
2392
 
        transport_path = 'quack'
2393
 
        repo, client = self.setup_fake_client_and_repository(transport_path)
2394
 
        client.add_expected_call(
2395
 
            'Repository.insert_stream_1.19', ('quack/', ''),
2396
 
            'success', ('ok',))
2397
 
        client.add_expected_call(
2398
 
            'Repository.insert_stream_1.19', ('quack/', ''),
2399
 
            'success', ('ok',))
2400
 
        self.checkInsertEmptyStream(repo, client)
2401
 
 
2402
 
    def test_locked_repo_with_no_lock_token(self):
2403
 
        transport_path = 'quack'
2404
 
        repo, client = self.setup_fake_client_and_repository(transport_path)
2405
 
        client.add_expected_call(
2406
 
            'Repository.lock_write', ('quack/', ''),
2407
 
            'success', ('ok', ''))
2408
 
        client.add_expected_call(
2409
 
            'Repository.insert_stream_1.19', ('quack/', ''),
2410
 
            'success', ('ok',))
2411
 
        client.add_expected_call(
2412
 
            'Repository.insert_stream_1.19', ('quack/', ''),
2413
 
            'success', ('ok',))
2414
 
        repo.lock_write()
2415
 
        self.checkInsertEmptyStream(repo, client)
2416
 
 
2417
 
    def test_locked_repo_with_lock_token(self):
2418
 
        transport_path = 'quack'
2419
 
        repo, client = self.setup_fake_client_and_repository(transport_path)
2420
 
        client.add_expected_call(
2421
 
            'Repository.lock_write', ('quack/', ''),
2422
 
            'success', ('ok', 'a token'))
2423
 
        client.add_expected_call(
2424
 
            'Repository.insert_stream_1.19', ('quack/', '', 'a token'),
2425
 
            'success', ('ok',))
2426
 
        client.add_expected_call(
2427
 
            'Repository.insert_stream_1.19', ('quack/', '', 'a token'),
2428
 
            'success', ('ok',))
2429
 
        repo.lock_write()
2430
 
        self.checkInsertEmptyStream(repo, client)
 
1999
class TestRepositoryInsertStream(TestRemoteRepository):
 
2000
 
 
2001
    def test_unlocked_repo(self):
 
2002
        transport_path = 'quack'
 
2003
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
2004
        client.add_expected_call(
 
2005
            'Repository.insert_stream', ('quack/', ''),
 
2006
            'success', ('ok',))
 
2007
        client.add_expected_call(
 
2008
            'Repository.insert_stream', ('quack/', ''),
 
2009
            'success', ('ok',))
 
2010
        sink = repo._get_sink()
 
2011
        fmt = repository.RepositoryFormat.get_default_format()
 
2012
        resume_tokens, missing_keys = sink.insert_stream([], fmt, [])
 
2013
        self.assertEqual([], resume_tokens)
 
2014
        self.assertEqual(set(), missing_keys)
 
2015
        client.finished_test()
 
2016
 
 
2017
    def test_locked_repo_with_no_lock_token(self):
 
2018
        transport_path = 'quack'
 
2019
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
2020
        client.add_expected_call(
 
2021
            'Repository.lock_write', ('quack/', ''),
 
2022
            'success', ('ok', ''))
 
2023
        client.add_expected_call(
 
2024
            'Repository.insert_stream', ('quack/', ''),
 
2025
            'success', ('ok',))
 
2026
        client.add_expected_call(
 
2027
            'Repository.insert_stream', ('quack/', ''),
 
2028
            'success', ('ok',))
 
2029
        repo.lock_write()
 
2030
        sink = repo._get_sink()
 
2031
        fmt = repository.RepositoryFormat.get_default_format()
 
2032
        resume_tokens, missing_keys = sink.insert_stream([], fmt, [])
 
2033
        self.assertEqual([], resume_tokens)
 
2034
        self.assertEqual(set(), missing_keys)
 
2035
        client.finished_test()
 
2036
 
 
2037
    def test_locked_repo_with_lock_token(self):
 
2038
        transport_path = 'quack'
 
2039
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
2040
        client.add_expected_call(
 
2041
            'Repository.lock_write', ('quack/', ''),
 
2042
            'success', ('ok', 'a token'))
 
2043
        client.add_expected_call(
 
2044
            'Repository.insert_stream_locked', ('quack/', '', 'a token'),
 
2045
            'success', ('ok',))
 
2046
        client.add_expected_call(
 
2047
            'Repository.insert_stream_locked', ('quack/', '', 'a token'),
 
2048
            'success', ('ok',))
 
2049
        repo.lock_write()
 
2050
        sink = repo._get_sink()
 
2051
        fmt = repository.RepositoryFormat.get_default_format()
 
2052
        resume_tokens, missing_keys = sink.insert_stream([], fmt, [])
 
2053
        self.assertEqual([], resume_tokens)
 
2054
        self.assertEqual(set(), missing_keys)
 
2055
        client.finished_test()
2431
2056
 
2432
2057
 
2433
2058
class TestRepositoryTarball(TestRemoteRepository):
2518
2143
        client.add_expected_call(
2519
2144
            'PackRepository.autopack', ('quack/',), 'success', ('ok',))
2520
2145
        repo.autopack()
2521
 
        self.assertFinished(client)
 
2146
        client.finished_test()
2522
2147
 
2523
2148
    def test_ok_with_real_repo(self):
2524
2149
        """When the server returns 'ok' and there is a _real_repository, then
2784
2409
        try:
2785
2410
            # it should have an appropriate fallback repository, which should also
2786
2411
            # be a RemoteRepository
2787
 
            self.assertLength(1, remote_repo._fallback_repositories)
 
2412
            self.assertEquals(len(remote_repo._fallback_repositories), 1)
2788
2413
            self.assertIsInstance(remote_repo._fallback_repositories[0],
2789
2414
                RemoteRepository)
2790
2415
            # and it has the revision committed to the underlying repository;
2803
2428
        tree1.commit('rev1', rev_id='rev1')
2804
2429
        tree2 = tree1.branch.bzrdir.sprout('tree2', stacked=True
2805
2430
            ).open_workingtree()
2806
 
        local_tree = tree2.branch.create_checkout('local')
2807
 
        local_tree.commit('local changes make me feel good.')
 
2431
        tree2.commit('local changes make me feel good.')
2808
2432
        branch2 = Branch.open(self.get_url('tree2'))
2809
2433
        branch2.lock_read()
2810
2434
        self.addCleanup(branch2.unlock)
2832
2456
                    result.append(content.key[-1])
2833
2457
        return result
2834
2458
 
2835
 
    def get_ordered_revs(self, format, order, branch_factory=None):
 
2459
    def get_ordered_revs(self, format, order):
2836
2460
        """Get a list of the revisions in a stream to format format.
2837
2461
 
2838
2462
        :param format: The format of the target.
2839
2463
        :param order: the order that target should have requested.
2840
 
        :param branch_factory: A callable to create a trunk and stacked branch
2841
 
            to fetch from. If none, self.prepare_stacked_remote_branch is used.
2842
2464
        :result: The revision ids in the stream, in the order seen,
2843
2465
            the topological order of revisions in the source.
2844
2466
        """
2846
2468
        target_repository_format = unordered_format.repository_format
2847
2469
        # Cross check
2848
2470
        self.assertEqual(order, target_repository_format._fetch_order)
2849
 
        if branch_factory is None:
2850
 
            branch_factory = self.prepare_stacked_remote_branch
2851
 
        _, stacked = branch_factory()
 
2471
        trunk, stacked = self.prepare_stacked_remote_branch()
2852
2472
        source = stacked.repository._get_source(target_repository_format)
2853
2473
        tip = stacked.last_revision()
2854
2474
        revs = stacked.repository.get_ancestry(tip)
2873
2493
        # from the server, then one from the backing branch.
2874
2494
        self.assertLength(2, self.hpss_calls)
2875
2495
 
2876
 
    def test_stacked_on_stacked_get_stream_unordered(self):
2877
 
        # Repository._get_source.get_stream() from a stacked repository which
2878
 
        # is itself stacked yields the full data from all three sources.
2879
 
        def make_stacked_stacked():
2880
 
            _, stacked = self.prepare_stacked_remote_branch()
2881
 
            tree = stacked.bzrdir.sprout('tree3', stacked=True
2882
 
                ).open_workingtree()
2883
 
            local_tree = tree.branch.create_checkout('local-tree3')
2884
 
            local_tree.commit('more local changes are better')
2885
 
            branch = Branch.open(self.get_url('tree3'))
2886
 
            branch.lock_read()
2887
 
            return None, branch
2888
 
        rev_ord, expected_revs = self.get_ordered_revs('1.9', 'unordered',
2889
 
            branch_factory=make_stacked_stacked)
2890
 
        self.assertEqual(set(expected_revs), set(rev_ord))
2891
 
        # Getting unordered results should have made a streaming data request
2892
 
        # from the server, and one from each backing repo
2893
 
        self.assertLength(3, self.hpss_calls)
2894
 
 
2895
2496
    def test_stacked_get_stream_topological(self):
2896
2497
        # Repository._get_source.get_stream() from a stacked repository with
2897
2498
        # topological sorting yields the full data from both stacked and
2898
2499
        # stacked upon sources in topological order.
2899
2500
        rev_ord, expected_revs = self.get_ordered_revs('knit', 'topological')
2900
2501
        self.assertEqual(expected_revs, rev_ord)
2901
 
        # Getting topological sort requires VFS calls still - one of which is
2902
 
        # pushing up from the bound branch.
2903
 
        self.assertLength(13, self.hpss_calls)
 
2502
        # Getting topological sort requires VFS calls still
 
2503
        self.assertLength(12, self.hpss_calls)
2904
2504
 
2905
2505
    def test_stacked_get_stream_groupcompress(self):
2906
2506
        # Repository._get_source.get_stream() from a stacked repository with
2913
2513
        # from the backing branch, and one from the stacked on branch.
2914
2514
        self.assertLength(2, self.hpss_calls)
2915
2515
 
2916
 
    def test_stacked_pull_more_than_stacking_has_bug_360791(self):
2917
 
        # When pulling some fixed amount of content that is more than the
2918
 
        # source has (because some is coming from a fallback branch, no error
2919
 
        # should be received. This was reported as bug 360791.
2920
 
        # Need three branches: a trunk, a stacked branch, and a preexisting
2921
 
        # branch pulling content from stacked and trunk.
2922
 
        self.setup_smart_server_with_call_log()
2923
 
        trunk = self.make_branch_and_tree('trunk', format="1.9-rich-root")
2924
 
        r1 = trunk.commit('start')
2925
 
        stacked_branch = trunk.branch.create_clone_on_transport(
2926
 
            self.get_transport('stacked'), stacked_on=trunk.branch.base)
2927
 
        local = self.make_branch('local', format='1.9-rich-root')
2928
 
        local.repository.fetch(stacked_branch.repository,
2929
 
            stacked_branch.last_revision())
2930
 
 
2931
2516
 
2932
2517
class TestRemoteBranchEffort(tests.TestCaseWithTransport):
2933
2518