~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-02-11 04:02:41 UTC
  • mfrom: (5017.2.2 tariff)
  • Revision ID: pqm@pqm.ubuntu.com-20100211040241-w6n021dz0uus341n
(mbp) add import-tariff tests

Show diffs side-by-side

added added

removed removed

Lines of Context:
27
27
from cStringIO import StringIO
28
28
 
29
29
from bzrlib import (
 
30
    branch,
30
31
    bzrdir,
31
32
    config,
32
33
    errors,
33
34
    graph,
 
35
    inventory,
 
36
    inventory_delta,
34
37
    pack,
35
38
    remote,
36
39
    repository,
37
 
    smart,
38
40
    tests,
39
41
    treebuilder,
40
42
    urlutils,
 
43
    versionedfile,
41
44
    )
42
45
from bzrlib.branch import Branch
43
46
from bzrlib.bzrdir import BzrDir, BzrDirFormat
58
61
    condition_isinstance,
59
62
    split_suite_by_condition,
60
63
    multiply_tests,
61
 
    KnownFailure,
62
64
    )
63
 
from bzrlib.transport import get_transport, http
 
65
from bzrlib.transport import get_transport
64
66
from bzrlib.transport.memory import MemoryTransport
65
67
from bzrlib.transport.remote import (
66
68
    RemoteTransport,
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)
343
 
 
344
347
    def assertFinished(self, fake_client):
345
348
        """Assert that all of a FakeClient's expected calls have occurred."""
346
349
        fake_client.finished_test()
441
444
            'BzrDir.cloning_metadir', ('quack/', 'False'),
442
445
            'error', ('BranchReference',)),
443
446
        client.add_expected_call(
444
 
            'BzrDir.open_branchV2', ('quack/',),
 
447
            'BzrDir.open_branchV3', ('quack/',),
445
448
            'success', ('ref', self.get_url('referenced'))),
446
449
        a_bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
447
450
            _client=client)
474
477
        self.assertFinished(client)
475
478
 
476
479
 
 
480
class TestBzrDirOpen(TestRemote):
 
481
 
 
482
    def make_fake_client_and_transport(self, path='quack'):
 
483
        transport = MemoryTransport()
 
484
        transport.mkdir(path)
 
485
        transport = transport.clone(path)
 
486
        client = FakeClient(transport.base)
 
487
        return client, transport
 
488
 
 
489
    def test_absent(self):
 
490
        client, transport = self.make_fake_client_and_transport()
 
491
        client.add_expected_call(
 
492
            'BzrDir.open_2.1', ('quack/',), 'success', ('no',))
 
493
        self.assertRaises(errors.NotBranchError, RemoteBzrDir, transport,
 
494
                remote.RemoteBzrDirFormat(), _client=client, _force_probe=True)
 
495
        self.assertFinished(client)
 
496
 
 
497
    def test_present_without_workingtree(self):
 
498
        client, transport = self.make_fake_client_and_transport()
 
499
        client.add_expected_call(
 
500
            'BzrDir.open_2.1', ('quack/',), 'success', ('yes', 'no'))
 
501
        bd = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
 
502
            _client=client, _force_probe=True)
 
503
        self.assertIsInstance(bd, RemoteBzrDir)
 
504
        self.assertFalse(bd.has_workingtree())
 
505
        self.assertRaises(errors.NoWorkingTree, bd.open_workingtree)
 
506
        self.assertFinished(client)
 
507
 
 
508
    def test_present_with_workingtree(self):
 
509
        client, transport = self.make_fake_client_and_transport()
 
510
        client.add_expected_call(
 
511
            'BzrDir.open_2.1', ('quack/',), 'success', ('yes', 'yes'))
 
512
        bd = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
 
513
            _client=client, _force_probe=True)
 
514
        self.assertIsInstance(bd, RemoteBzrDir)
 
515
        self.assertTrue(bd.has_workingtree())
 
516
        self.assertRaises(errors.NotLocalUrl, bd.open_workingtree)
 
517
        self.assertFinished(client)
 
518
 
 
519
    def test_backwards_compat(self):
 
520
        client, transport = self.make_fake_client_and_transport()
 
521
        client.add_expected_call(
 
522
            'BzrDir.open_2.1', ('quack/',), 'unknown', ('BzrDir.open_2.1',))
 
523
        client.add_expected_call(
 
524
            'BzrDir.open', ('quack/',), 'success', ('yes',))
 
525
        bd = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
 
526
            _client=client, _force_probe=True)
 
527
        self.assertIsInstance(bd, RemoteBzrDir)
 
528
        self.assertFinished(client)
 
529
 
 
530
 
477
531
class TestBzrDirOpenBranch(TestRemote):
478
532
 
479
533
    def test_backwards_compat(self):
481
535
        self.make_branch('.')
482
536
        a_dir = BzrDir.open(self.get_url('.'))
483
537
        self.reset_smart_call_log()
484
 
        verb = 'BzrDir.open_branchV2'
 
538
        verb = 'BzrDir.open_branchV3'
485
539
        self.disable_verb(verb)
486
540
        format = a_dir.open_branch()
487
541
        call_count = len([call for call in self.hpss_calls if
497
551
        transport = transport.clone('quack')
498
552
        client = FakeClient(transport.base)
499
553
        client.add_expected_call(
500
 
            'BzrDir.open_branchV2', ('quack/',),
 
554
            'BzrDir.open_branchV3', ('quack/',),
501
555
            'success', ('branch', branch_network_name))
502
556
        client.add_expected_call(
503
557
            'BzrDir.find_repositoryV3', ('quack/',),
522
576
            _client=client)
523
577
        self.assertRaises(errors.NotBranchError, bzrdir.open_branch)
524
578
        self.assertEqual(
525
 
            [('call', 'BzrDir.open_branchV2', ('quack/',))],
 
579
            [('call', 'BzrDir.open_branchV3', ('quack/',))],
526
580
            client._calls)
527
581
 
528
582
    def test__get_tree_branch(self):
552
606
        network_name = reference_format.network_name()
553
607
        branch_network_name = self.get_branch_format().network_name()
554
608
        client.add_expected_call(
555
 
            'BzrDir.open_branchV2', ('~hello/',),
 
609
            'BzrDir.open_branchV3', ('~hello/',),
556
610
            'success', ('branch', branch_network_name))
557
611
        client.add_expected_call(
558
612
            'BzrDir.find_repositoryV3', ('~hello/',),
667
721
        network_name = reference_format.network_name()
668
722
        client.add_expected_call(
669
723
            'BzrDir.create_repository', ('quack/',
670
 
                'Bazaar pack repository format 1 (needs bzr 0.92)\n', 'False'),
671
 
            'success', ('ok', 'no', 'no', 'no', network_name))
 
724
                'Bazaar repository format 2a (needs bzr 1.16 or later)\n',
 
725
                'False'),
 
726
            'success', ('ok', 'yes', 'yes', 'yes', network_name))
672
727
        a_bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
673
728
            _client=client)
674
729
        repo = a_bzrdir.create_repository()
676
731
        self.assertIsInstance(repo, remote.RemoteRepository)
677
732
        # its format should have the settings from the response
678
733
        format = repo._format
679
 
        self.assertFalse(format.rich_root_data)
680
 
        self.assertFalse(format.supports_tree_reference)
681
 
        self.assertFalse(format.supports_external_lookups)
 
734
        self.assertTrue(format.rich_root_data)
 
735
        self.assertTrue(format.supports_tree_reference)
 
736
        self.assertTrue(format.supports_external_lookups)
682
737
        self.assertEqual(network_name, format.network_name())
683
738
 
684
739
 
688
743
        # fallback all the way to the first version.
689
744
        reference_format = self.get_repo_format()
690
745
        network_name = reference_format.network_name()
691
 
        client = FakeClient('bzr://example.com/')
 
746
        server_url = 'bzr://example.com/'
 
747
        self.permit_url(server_url)
 
748
        client = FakeClient(server_url)
692
749
        client.add_unknown_method_response('BzrDir.find_repositoryV3')
693
750
        client.add_unknown_method_response('BzrDir.find_repositoryV2')
694
751
        client.add_success_response('ok', '', 'no', 'no')
700
757
            reference_format.get_format_string(), 'ok')
701
758
        # PackRepository wants to do a stat
702
759
        client.add_success_response('stat', '0', '65535')
703
 
        remote_transport = RemoteTransport('bzr://example.com/quack/', medium=False,
 
760
        remote_transport = RemoteTransport(server_url + 'quack/', medium=False,
704
761
            _client=client)
705
762
        bzrdir = RemoteBzrDir(remote_transport, remote.RemoteBzrDirFormat(),
706
763
            _client=client)
720
777
        # fallback to find_repositoryV2
721
778
        reference_format = self.get_repo_format()
722
779
        network_name = reference_format.network_name()
723
 
        client = FakeClient('bzr://example.com/')
 
780
        server_url = 'bzr://example.com/'
 
781
        self.permit_url(server_url)
 
782
        client = FakeClient(server_url)
724
783
        client.add_unknown_method_response('BzrDir.find_repositoryV3')
725
784
        client.add_success_response('ok', '', 'no', 'no', 'no')
726
785
        # A real repository instance will be created to determine the network
731
790
            reference_format.get_format_string(), 'ok')
732
791
        # PackRepository wants to do a stat
733
792
        client.add_success_response('stat', '0', '65535')
734
 
        remote_transport = RemoteTransport('bzr://example.com/quack/', medium=False,
 
793
        remote_transport = RemoteTransport(server_url + 'quack/', medium=False,
735
794
            _client=client)
736
795
        bzrdir = RemoteBzrDir(remote_transport, remote.RemoteBzrDirFormat(),
737
796
            _client=client)
856
915
 
857
916
class RemoteBranchTestCase(RemoteBzrDirTestCase):
858
917
 
 
918
    def lock_remote_branch(self, branch):
 
919
        """Trick a RemoteBranch into thinking it is locked."""
 
920
        branch._lock_mode = 'w'
 
921
        branch._lock_count = 2
 
922
        branch._lock_token = 'branch token'
 
923
        branch._repo_lock_token = 'repo token'
 
924
        branch.repository._lock_mode = 'w'
 
925
        branch.repository._lock_count = 2
 
926
        branch.repository._lock_token = 'repo token'
 
927
 
859
928
    def make_remote_branch(self, transport, client):
860
929
        """Make a RemoteBranch using 'client' as its _SmartClient.
861
930
 
1000
1069
        self.assertEqual({}, result)
1001
1070
 
1002
1071
 
 
1072
class TestBranchSetTagsBytes(RemoteBranchTestCase):
 
1073
 
 
1074
    def test_trivial(self):
 
1075
        transport = MemoryTransport()
 
1076
        client = FakeClient(transport.base)
 
1077
        client.add_expected_call(
 
1078
            'Branch.get_stacked_on_url', ('quack/',),
 
1079
            'error', ('NotStacked',))
 
1080
        client.add_expected_call(
 
1081
            'Branch.set_tags_bytes', ('quack/', 'branch token', 'repo token'),
 
1082
            'success', ('',))
 
1083
        transport.mkdir('quack')
 
1084
        transport = transport.clone('quack')
 
1085
        branch = self.make_remote_branch(transport, client)
 
1086
        self.lock_remote_branch(branch)
 
1087
        branch._set_tags_bytes('tags bytes')
 
1088
        self.assertFinished(client)
 
1089
        self.assertEqual('tags bytes', client._calls[-1][-1])
 
1090
 
 
1091
    def test_backwards_compatible(self):
 
1092
        transport = MemoryTransport()
 
1093
        client = FakeClient(transport.base)
 
1094
        client.add_expected_call(
 
1095
            'Branch.get_stacked_on_url', ('quack/',),
 
1096
            'error', ('NotStacked',))
 
1097
        client.add_expected_call(
 
1098
            'Branch.set_tags_bytes', ('quack/', 'branch token', 'repo token'),
 
1099
            'unknown', ('Branch.set_tags_bytes',))
 
1100
        transport.mkdir('quack')
 
1101
        transport = transport.clone('quack')
 
1102
        branch = self.make_remote_branch(transport, client)
 
1103
        self.lock_remote_branch(branch)
 
1104
        class StubRealBranch(object):
 
1105
            def __init__(self):
 
1106
                self.calls = []
 
1107
            def _set_tags_bytes(self, bytes):
 
1108
                self.calls.append(('set_tags_bytes', bytes))
 
1109
        real_branch = StubRealBranch()
 
1110
        branch._real_branch = real_branch
 
1111
        branch._set_tags_bytes('tags bytes')
 
1112
        # Call a second time, to exercise the 'remote version already inferred'
 
1113
        # code path.
 
1114
        branch._set_tags_bytes('tags bytes')
 
1115
        self.assertFinished(client)
 
1116
        self.assertEqual(
 
1117
            [('set_tags_bytes', 'tags bytes')] * 2, real_branch.calls)
 
1118
 
 
1119
 
1003
1120
class TestBranchLastRevisionInfo(RemoteBranchTestCase):
1004
1121
 
1005
1122
    def test_empty_branch(self):
1077
1194
        client = FakeClient(self.get_url())
1078
1195
        branch_network_name = self.get_branch_format().network_name()
1079
1196
        client.add_expected_call(
1080
 
            'BzrDir.open_branchV2', ('stacked/',),
 
1197
            'BzrDir.open_branchV3', ('stacked/',),
1081
1198
            'success', ('branch', branch_network_name))
1082
1199
        client.add_expected_call(
1083
1200
            'BzrDir.find_repositoryV3', ('stacked/',),
1113
1230
        client = FakeClient(self.get_url())
1114
1231
        branch_network_name = self.get_branch_format().network_name()
1115
1232
        client.add_expected_call(
1116
 
            'BzrDir.open_branchV2', ('stacked/',),
 
1233
            'BzrDir.open_branchV3', ('stacked/',),
1117
1234
            'success', ('branch', branch_network_name))
1118
1235
        client.add_expected_call(
1119
1236
            'BzrDir.find_repositoryV3', ('stacked/',),
1347
1464
            errors.NoSuchRevision, branch.set_last_revision_info, 123, 'revid')
1348
1465
        branch.unlock()
1349
1466
 
1350
 
    def lock_remote_branch(self, branch):
1351
 
        """Trick a RemoteBranch into thinking it is locked."""
1352
 
        branch._lock_mode = 'w'
1353
 
        branch._lock_count = 2
1354
 
        branch._lock_token = 'branch token'
1355
 
        branch._repo_lock_token = 'repo token'
1356
 
        branch.repository._lock_mode = 'w'
1357
 
        branch.repository._lock_count = 2
1358
 
        branch.repository._lock_token = 'repo token'
1359
 
 
1360
1467
    def test_backwards_compatibility(self):
1361
1468
        """If the server does not support the Branch.set_last_revision_info
1362
1469
        verb (which is new in 1.4), then the client falls back to VFS methods.
1678
1785
        return repo, client
1679
1786
 
1680
1787
 
 
1788
def remoted_description(format):
 
1789
    return 'Remote: ' + format.get_format_description()
 
1790
 
 
1791
 
 
1792
class TestBranchFormat(tests.TestCase):
 
1793
 
 
1794
    def test_get_format_description(self):
 
1795
        remote_format = RemoteBranchFormat()
 
1796
        real_format = branch.BranchFormat.get_default_format()
 
1797
        remote_format._network_name = real_format.network_name()
 
1798
        self.assertEqual(remoted_description(real_format),
 
1799
            remote_format.get_format_description())
 
1800
 
 
1801
 
1681
1802
class TestRepositoryFormat(TestRemoteRepository):
1682
1803
 
1683
1804
    def test_fast_delta(self):
1690
1811
        false_format._network_name = false_name
1691
1812
        self.assertEqual(False, false_format.fast_deltas)
1692
1813
 
 
1814
    def test_get_format_description(self):
 
1815
        remote_repo_format = RemoteRepositoryFormat()
 
1816
        real_format = repository.RepositoryFormat.get_default_format()
 
1817
        remote_repo_format._network_name = real_format.network_name()
 
1818
        self.assertEqual(remoted_description(real_format),
 
1819
            remote_repo_format.get_format_description())
 
1820
 
1693
1821
 
1694
1822
class TestRepositoryGatherStats(TestRemoteRepository):
1695
1823
 
1880
2008
        self.assertLength(1, self.hpss_calls)
1881
2009
 
1882
2010
    def disableExtraResults(self):
1883
 
        old_flag = SmartServerRepositoryGetParentMap.no_extra_results
1884
 
        SmartServerRepositoryGetParentMap.no_extra_results = True
1885
 
        def reset_values():
1886
 
            SmartServerRepositoryGetParentMap.no_extra_results = old_flag
1887
 
        self.addCleanup(reset_values)
 
2011
        self.overrideAttr(SmartServerRepositoryGetParentMap,
 
2012
                          'no_extra_results', True)
1888
2013
 
1889
2014
    def test_null_cached_missing_and_stop_key(self):
1890
2015
        self.setup_smart_server_with_call_log()
1950
2075
    def test_allows_new_revisions(self):
1951
2076
        """get_parent_map's results can be updated by commit."""
1952
2077
        smart_server = server.SmartTCPServer_for_testing()
1953
 
        smart_server.setUp()
1954
 
        self.addCleanup(smart_server.tearDown)
 
2078
        self.start_server(smart_server)
1955
2079
        self.make_branch('branch')
1956
2080
        branch = Branch.open(smart_server.get_url() + '/branch')
1957
2081
        tree = branch.create_checkout('tree', lightweight=True)
2098
2222
            repo.get_rev_id_for_revno, 5, (42, 'rev-foo'))
2099
2223
        self.assertFinished(client)
2100
2224
 
 
2225
    def test_branch_fallback_locking(self):
 
2226
        """RemoteBranch.get_rev_id takes a read lock, and tries to call the
 
2227
        get_rev_id_for_revno verb.  If the verb is unknown the VFS fallback
 
2228
        will be invoked, which will fail if the repo is unlocked.
 
2229
        """
 
2230
        self.setup_smart_server_with_call_log()
 
2231
        tree = self.make_branch_and_memory_tree('.')
 
2232
        tree.lock_write()
 
2233
        rev1 = tree.commit('First')
 
2234
        rev2 = tree.commit('Second')
 
2235
        tree.unlock()
 
2236
        branch = tree.branch
 
2237
        self.assertFalse(branch.is_locked())
 
2238
        self.reset_smart_call_log()
 
2239
        verb = 'Repository.get_rev_id_for_revno'
 
2240
        self.disable_verb(verb)
 
2241
        self.assertEqual(rev1, branch.get_rev_id(1))
 
2242
        self.assertLength(1, [call for call in self.hpss_calls if
 
2243
                              call.call.method == verb])
 
2244
 
2101
2245
 
2102
2246
class TestRepositoryIsShared(TestRemoteRepository):
2103
2247
 
2219
2363
        self.assertEqual([], client._calls)
2220
2364
 
2221
2365
 
2222
 
class TestRepositoryInsertStream(TestRemoteRepository):
2223
 
 
2224
 
    def test_unlocked_repo(self):
2225
 
        transport_path = 'quack'
2226
 
        repo, client = self.setup_fake_client_and_repository(transport_path)
2227
 
        client.add_expected_call(
2228
 
            'Repository.insert_stream', ('quack/', ''),
2229
 
            'success', ('ok',))
2230
 
        client.add_expected_call(
2231
 
            'Repository.insert_stream', ('quack/', ''),
2232
 
            'success', ('ok',))
2233
 
        sink = repo._get_sink()
2234
 
        fmt = repository.RepositoryFormat.get_default_format()
2235
 
        resume_tokens, missing_keys = sink.insert_stream([], fmt, [])
2236
 
        self.assertEqual([], resume_tokens)
2237
 
        self.assertEqual(set(), missing_keys)
2238
 
        self.assertFinished(client)
2239
 
 
2240
 
    def test_locked_repo_with_no_lock_token(self):
2241
 
        transport_path = 'quack'
2242
 
        repo, client = self.setup_fake_client_and_repository(transport_path)
2243
 
        client.add_expected_call(
2244
 
            'Repository.lock_write', ('quack/', ''),
2245
 
            'success', ('ok', ''))
2246
 
        client.add_expected_call(
2247
 
            'Repository.insert_stream', ('quack/', ''),
2248
 
            'success', ('ok',))
2249
 
        client.add_expected_call(
2250
 
            'Repository.insert_stream', ('quack/', ''),
2251
 
            'success', ('ok',))
2252
 
        repo.lock_write()
2253
 
        sink = repo._get_sink()
2254
 
        fmt = repository.RepositoryFormat.get_default_format()
2255
 
        resume_tokens, missing_keys = sink.insert_stream([], fmt, [])
2256
 
        self.assertEqual([], resume_tokens)
2257
 
        self.assertEqual(set(), missing_keys)
2258
 
        self.assertFinished(client)
2259
 
 
2260
 
    def test_locked_repo_with_lock_token(self):
2261
 
        transport_path = 'quack'
2262
 
        repo, client = self.setup_fake_client_and_repository(transport_path)
2263
 
        client.add_expected_call(
2264
 
            'Repository.lock_write', ('quack/', ''),
2265
 
            'success', ('ok', 'a token'))
2266
 
        client.add_expected_call(
2267
 
            'Repository.insert_stream_locked', ('quack/', '', 'a token'),
2268
 
            'success', ('ok',))
2269
 
        client.add_expected_call(
2270
 
            'Repository.insert_stream_locked', ('quack/', '', 'a token'),
2271
 
            'success', ('ok',))
2272
 
        repo.lock_write()
2273
 
        sink = repo._get_sink()
2274
 
        fmt = repository.RepositoryFormat.get_default_format()
2275
 
        resume_tokens, missing_keys = sink.insert_stream([], fmt, [])
2276
 
        self.assertEqual([], resume_tokens)
2277
 
        self.assertEqual(set(), missing_keys)
2278
 
        self.assertFinished(client)
 
2366
class TestRepositoryInsertStreamBase(TestRemoteRepository):
 
2367
    """Base class for Repository.insert_stream and .insert_stream_1.19
 
2368
    tests.
 
2369
    """
 
2370
    
 
2371
    def checkInsertEmptyStream(self, repo, client):
 
2372
        """Insert an empty stream, checking the result.
 
2373
 
 
2374
        This checks that there are no resume_tokens or missing_keys, and that
 
2375
        the client is finished.
 
2376
        """
 
2377
        sink = repo._get_sink()
 
2378
        fmt = repository.RepositoryFormat.get_default_format()
 
2379
        resume_tokens, missing_keys = sink.insert_stream([], fmt, [])
 
2380
        self.assertEqual([], resume_tokens)
 
2381
        self.assertEqual(set(), missing_keys)
 
2382
        self.assertFinished(client)
 
2383
 
 
2384
 
 
2385
class TestRepositoryInsertStream(TestRepositoryInsertStreamBase):
 
2386
    """Tests for using Repository.insert_stream verb when the _1.19 variant is
 
2387
    not available.
 
2388
 
 
2389
    This test case is very similar to TestRepositoryInsertStream_1_19.
 
2390
    """
 
2391
 
 
2392
    def setUp(self):
 
2393
        TestRemoteRepository.setUp(self)
 
2394
        self.disable_verb('Repository.insert_stream_1.19')
 
2395
 
 
2396
    def test_unlocked_repo(self):
 
2397
        transport_path = 'quack'
 
2398
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
2399
        client.add_expected_call(
 
2400
            'Repository.insert_stream_1.19', ('quack/', ''),
 
2401
            'unknown', ('Repository.insert_stream_1.19',))
 
2402
        client.add_expected_call(
 
2403
            'Repository.insert_stream', ('quack/', ''),
 
2404
            'success', ('ok',))
 
2405
        client.add_expected_call(
 
2406
            'Repository.insert_stream', ('quack/', ''),
 
2407
            'success', ('ok',))
 
2408
        self.checkInsertEmptyStream(repo, client)
 
2409
 
 
2410
    def test_locked_repo_with_no_lock_token(self):
 
2411
        transport_path = 'quack'
 
2412
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
2413
        client.add_expected_call(
 
2414
            'Repository.lock_write', ('quack/', ''),
 
2415
            'success', ('ok', ''))
 
2416
        client.add_expected_call(
 
2417
            'Repository.insert_stream_1.19', ('quack/', ''),
 
2418
            'unknown', ('Repository.insert_stream_1.19',))
 
2419
        client.add_expected_call(
 
2420
            'Repository.insert_stream', ('quack/', ''),
 
2421
            'success', ('ok',))
 
2422
        client.add_expected_call(
 
2423
            'Repository.insert_stream', ('quack/', ''),
 
2424
            'success', ('ok',))
 
2425
        repo.lock_write()
 
2426
        self.checkInsertEmptyStream(repo, client)
 
2427
 
 
2428
    def test_locked_repo_with_lock_token(self):
 
2429
        transport_path = 'quack'
 
2430
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
2431
        client.add_expected_call(
 
2432
            'Repository.lock_write', ('quack/', ''),
 
2433
            'success', ('ok', 'a token'))
 
2434
        client.add_expected_call(
 
2435
            'Repository.insert_stream_1.19', ('quack/', '', 'a token'),
 
2436
            'unknown', ('Repository.insert_stream_1.19',))
 
2437
        client.add_expected_call(
 
2438
            'Repository.insert_stream_locked', ('quack/', '', 'a token'),
 
2439
            'success', ('ok',))
 
2440
        client.add_expected_call(
 
2441
            'Repository.insert_stream_locked', ('quack/', '', 'a token'),
 
2442
            'success', ('ok',))
 
2443
        repo.lock_write()
 
2444
        self.checkInsertEmptyStream(repo, client)
 
2445
 
 
2446
    def test_stream_with_inventory_deltas(self):
 
2447
        """'inventory-deltas' substreams cannot be sent to the
 
2448
        Repository.insert_stream verb, because not all servers that implement
 
2449
        that verb will accept them.  So when one is encountered the RemoteSink
 
2450
        immediately stops using that verb and falls back to VFS insert_stream.
 
2451
        """
 
2452
        transport_path = 'quack'
 
2453
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
2454
        client.add_expected_call(
 
2455
            'Repository.insert_stream_1.19', ('quack/', ''),
 
2456
            'unknown', ('Repository.insert_stream_1.19',))
 
2457
        client.add_expected_call(
 
2458
            'Repository.insert_stream', ('quack/', ''),
 
2459
            'success', ('ok',))
 
2460
        client.add_expected_call(
 
2461
            'Repository.insert_stream', ('quack/', ''),
 
2462
            'success', ('ok',))
 
2463
        # Create a fake real repository for insert_stream to fall back on, so
 
2464
        # that we can directly see the records the RemoteSink passes to the
 
2465
        # real sink.
 
2466
        class FakeRealSink:
 
2467
            def __init__(self):
 
2468
                self.records = []
 
2469
            def insert_stream(self, stream, src_format, resume_tokens):
 
2470
                for substream_kind, substream in stream:
 
2471
                    self.records.append(
 
2472
                        (substream_kind, [record.key for record in substream]))
 
2473
                return ['fake tokens'], ['fake missing keys']
 
2474
        fake_real_sink = FakeRealSink()
 
2475
        class FakeRealRepository:
 
2476
            def _get_sink(self):
 
2477
                return fake_real_sink
 
2478
            def is_in_write_group(self):
 
2479
                return False
 
2480
            def refresh_data(self):
 
2481
                return True
 
2482
        repo._real_repository = FakeRealRepository()
 
2483
        sink = repo._get_sink()
 
2484
        fmt = repository.RepositoryFormat.get_default_format()
 
2485
        stream = self.make_stream_with_inv_deltas(fmt)
 
2486
        resume_tokens, missing_keys = sink.insert_stream(stream, fmt, [])
 
2487
        # Every record from the first inventory delta should have been sent to
 
2488
        # the VFS sink.
 
2489
        expected_records = [
 
2490
            ('inventory-deltas', [('rev2',), ('rev3',)]),
 
2491
            ('texts', [('some-rev', 'some-file')])]
 
2492
        self.assertEqual(expected_records, fake_real_sink.records)
 
2493
        # The return values from the real sink's insert_stream are propagated
 
2494
        # back to the original caller.
 
2495
        self.assertEqual(['fake tokens'], resume_tokens)
 
2496
        self.assertEqual(['fake missing keys'], missing_keys)
 
2497
        self.assertFinished(client)
 
2498
 
 
2499
    def make_stream_with_inv_deltas(self, fmt):
 
2500
        """Make a simple stream with an inventory delta followed by more
 
2501
        records and more substreams to test that all records and substreams
 
2502
        from that point on are used.
 
2503
 
 
2504
        This sends, in order:
 
2505
           * inventories substream: rev1, rev2, rev3.  rev2 and rev3 are
 
2506
             inventory-deltas.
 
2507
           * texts substream: (some-rev, some-file)
 
2508
        """
 
2509
        # Define a stream using generators so that it isn't rewindable.
 
2510
        inv = inventory.Inventory(revision_id='rev1')
 
2511
        inv.root.revision = 'rev1'
 
2512
        def stream_with_inv_delta():
 
2513
            yield ('inventories', inventories_substream())
 
2514
            yield ('inventory-deltas', inventory_delta_substream())
 
2515
            yield ('texts', [
 
2516
                versionedfile.FulltextContentFactory(
 
2517
                    ('some-rev', 'some-file'), (), None, 'content')])
 
2518
        def inventories_substream():
 
2519
            # An empty inventory fulltext.  This will be streamed normally.
 
2520
            text = fmt._serializer.write_inventory_to_string(inv)
 
2521
            yield versionedfile.FulltextContentFactory(
 
2522
                ('rev1',), (), None, text)
 
2523
        def inventory_delta_substream():
 
2524
            # An inventory delta.  This can't be streamed via this verb, so it
 
2525
            # will trigger a fallback to VFS insert_stream.
 
2526
            entry = inv.make_entry(
 
2527
                'directory', 'newdir', inv.root.file_id, 'newdir-id')
 
2528
            entry.revision = 'ghost'
 
2529
            delta = [(None, 'newdir', 'newdir-id', entry)]
 
2530
            serializer = inventory_delta.InventoryDeltaSerializer(
 
2531
                versioned_root=True, tree_references=False)
 
2532
            lines = serializer.delta_to_lines('rev1', 'rev2', delta)
 
2533
            yield versionedfile.ChunkedContentFactory(
 
2534
                ('rev2',), (('rev1',)), None, lines)
 
2535
            # Another delta.
 
2536
            lines = serializer.delta_to_lines('rev1', 'rev3', delta)
 
2537
            yield versionedfile.ChunkedContentFactory(
 
2538
                ('rev3',), (('rev1',)), None, lines)
 
2539
        return stream_with_inv_delta()
 
2540
 
 
2541
 
 
2542
class TestRepositoryInsertStream_1_19(TestRepositoryInsertStreamBase):
 
2543
 
 
2544
    def test_unlocked_repo(self):
 
2545
        transport_path = 'quack'
 
2546
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
2547
        client.add_expected_call(
 
2548
            'Repository.insert_stream_1.19', ('quack/', ''),
 
2549
            'success', ('ok',))
 
2550
        client.add_expected_call(
 
2551
            'Repository.insert_stream_1.19', ('quack/', ''),
 
2552
            'success', ('ok',))
 
2553
        self.checkInsertEmptyStream(repo, client)
 
2554
 
 
2555
    def test_locked_repo_with_no_lock_token(self):
 
2556
        transport_path = 'quack'
 
2557
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
2558
        client.add_expected_call(
 
2559
            'Repository.lock_write', ('quack/', ''),
 
2560
            'success', ('ok', ''))
 
2561
        client.add_expected_call(
 
2562
            'Repository.insert_stream_1.19', ('quack/', ''),
 
2563
            'success', ('ok',))
 
2564
        client.add_expected_call(
 
2565
            'Repository.insert_stream_1.19', ('quack/', ''),
 
2566
            'success', ('ok',))
 
2567
        repo.lock_write()
 
2568
        self.checkInsertEmptyStream(repo, client)
 
2569
 
 
2570
    def test_locked_repo_with_lock_token(self):
 
2571
        transport_path = 'quack'
 
2572
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
2573
        client.add_expected_call(
 
2574
            'Repository.lock_write', ('quack/', ''),
 
2575
            'success', ('ok', 'a token'))
 
2576
        client.add_expected_call(
 
2577
            'Repository.insert_stream_1.19', ('quack/', '', 'a token'),
 
2578
            'success', ('ok',))
 
2579
        client.add_expected_call(
 
2580
            'Repository.insert_stream_1.19', ('quack/', '', 'a token'),
 
2581
            'success', ('ok',))
 
2582
        repo.lock_write()
 
2583
        self.checkInsertEmptyStream(repo, client)
2279
2584
 
2280
2585
 
2281
2586
class TestRepositoryTarball(TestRemoteRepository):
2471
2776
        expected_error = errors.NotBranchError(path=bzrdir.root_transport.base)
2472
2777
        self.assertEqual(expected_error, translated_error)
2473
2778
 
 
2779
    def test_nobranch_one_arg(self):
 
2780
        bzrdir = self.make_bzrdir('')
 
2781
        translated_error = self.translateTuple(
 
2782
            ('nobranch', 'extra detail'), bzrdir=bzrdir)
 
2783
        expected_error = errors.NotBranchError(
 
2784
            path=bzrdir.root_transport.base,
 
2785
            detail='extra detail')
 
2786
        self.assertEqual(expected_error, translated_error)
 
2787
 
2474
2788
    def test_LockContention(self):
2475
2789
        translated_error = self.translateTuple(('LockContention',))
2476
2790
        expected_error = errors.LockContention('(remote lock)')
2516
2830
        expected_error = errors.ReadError(path)
2517
2831
        self.assertEqual(expected_error, translated_error)
2518
2832
 
 
2833
    def test_IncompatibleRepositories(self):
 
2834
        translated_error = self.translateTuple(('IncompatibleRepositories',
 
2835
            "repo1", "repo2", "details here"))
 
2836
        expected_error = errors.IncompatibleRepositories("repo1", "repo2",
 
2837
            "details here")
 
2838
        self.assertEqual(expected_error, translated_error)
 
2839
 
2519
2840
    def test_PermissionDenied_no_args(self):
2520
2841
        path = 'a path'
2521
2842
        translated_error = self.translateTuple(('PermissionDenied',), path=path)
2582
2903
        # In addition to re-raising ErrorFromSmartServer, some debug info has
2583
2904
        # been muttered to the log file for developer to look at.
2584
2905
        self.assertContainsRe(
2585
 
            self._get_log(keep_log_file=True),
 
2906
            self.get_log(),
2586
2907
            "Missing key 'branch' in context")
2587
2908
 
2588
2909
    def test_path_missing(self):
2596
2917
        self.assertEqual(server_error, translated_error)
2597
2918
        # In addition to re-raising ErrorFromSmartServer, some debug info has
2598
2919
        # been muttered to the log file for developer to look at.
2599
 
        self.assertContainsRe(
2600
 
            self._get_log(keep_log_file=True), "Missing key 'path' in context")
 
2920
        self.assertContainsRe(self.get_log(), "Missing key 'path' in context")
2601
2921
 
2602
2922
 
2603
2923
class TestStacking(tests.TestCaseWithTransport):
2622
2942
        stacked_branch.set_stacked_on_url('../base')
2623
2943
        # start a server looking at this
2624
2944
        smart_server = server.SmartTCPServer_for_testing()
2625
 
        smart_server.setUp()
2626
 
        self.addCleanup(smart_server.tearDown)
 
2945
        self.start_server(smart_server)
2627
2946
        remote_bzrdir = BzrDir.open(smart_server.get_url() + '/stacked')
2628
2947
        # can get its branch and repository
2629
2948
        remote_branch = remote_bzrdir.open_branch()
2651
2970
        tree1.commit('rev1', rev_id='rev1')
2652
2971
        tree2 = tree1.branch.bzrdir.sprout('tree2', stacked=True
2653
2972
            ).open_workingtree()
2654
 
        tree2.commit('local changes make me feel good.')
 
2973
        local_tree = tree2.branch.create_checkout('local')
 
2974
        local_tree.commit('local changes make me feel good.')
2655
2975
        branch2 = Branch.open(self.get_url('tree2'))
2656
2976
        branch2.lock_read()
2657
2977
        self.addCleanup(branch2.unlock)
2679
2999
                    result.append(content.key[-1])
2680
3000
        return result
2681
3001
 
2682
 
    def get_ordered_revs(self, format, order):
 
3002
    def get_ordered_revs(self, format, order, branch_factory=None):
2683
3003
        """Get a list of the revisions in a stream to format format.
2684
3004
 
2685
3005
        :param format: The format of the target.
2686
3006
        :param order: the order that target should have requested.
 
3007
        :param branch_factory: A callable to create a trunk and stacked branch
 
3008
            to fetch from. If none, self.prepare_stacked_remote_branch is used.
2687
3009
        :result: The revision ids in the stream, in the order seen,
2688
3010
            the topological order of revisions in the source.
2689
3011
        """
2691
3013
        target_repository_format = unordered_format.repository_format
2692
3014
        # Cross check
2693
3015
        self.assertEqual(order, target_repository_format._fetch_order)
2694
 
        trunk, stacked = self.prepare_stacked_remote_branch()
 
3016
        if branch_factory is None:
 
3017
            branch_factory = self.prepare_stacked_remote_branch
 
3018
        _, stacked = branch_factory()
2695
3019
        source = stacked.repository._get_source(target_repository_format)
2696
3020
        tip = stacked.last_revision()
2697
3021
        revs = stacked.repository.get_ancestry(tip)
2716
3040
        # from the server, then one from the backing branch.
2717
3041
        self.assertLength(2, self.hpss_calls)
2718
3042
 
 
3043
    def test_stacked_on_stacked_get_stream_unordered(self):
 
3044
        # Repository._get_source.get_stream() from a stacked repository which
 
3045
        # is itself stacked yields the full data from all three sources.
 
3046
        def make_stacked_stacked():
 
3047
            _, stacked = self.prepare_stacked_remote_branch()
 
3048
            tree = stacked.bzrdir.sprout('tree3', stacked=True
 
3049
                ).open_workingtree()
 
3050
            local_tree = tree.branch.create_checkout('local-tree3')
 
3051
            local_tree.commit('more local changes are better')
 
3052
            branch = Branch.open(self.get_url('tree3'))
 
3053
            branch.lock_read()
 
3054
            self.addCleanup(branch.unlock)
 
3055
            return None, branch
 
3056
        rev_ord, expected_revs = self.get_ordered_revs('1.9', 'unordered',
 
3057
            branch_factory=make_stacked_stacked)
 
3058
        self.assertEqual(set(expected_revs), set(rev_ord))
 
3059
        # Getting unordered results should have made a streaming data request
 
3060
        # from the server, and one from each backing repo
 
3061
        self.assertLength(3, self.hpss_calls)
 
3062
 
2719
3063
    def test_stacked_get_stream_topological(self):
2720
3064
        # Repository._get_source.get_stream() from a stacked repository with
2721
3065
        # topological sorting yields the full data from both stacked and
2722
3066
        # stacked upon sources in topological order.
2723
3067
        rev_ord, expected_revs = self.get_ordered_revs('knit', 'topological')
2724
3068
        self.assertEqual(expected_revs, rev_ord)
2725
 
        # Getting topological sort requires VFS calls still
2726
 
        self.assertLength(12, self.hpss_calls)
 
3069
        # Getting topological sort requires VFS calls still - one of which is
 
3070
        # pushing up from the bound branch.
 
3071
        self.assertLength(13, self.hpss_calls)
2727
3072
 
2728
3073
    def test_stacked_get_stream_groupcompress(self):
2729
3074
        # Repository._get_source.get_stream() from a stacked repository with
2759
3104
        # Create a smart server that publishes whatever the backing VFS server
2760
3105
        # does.
2761
3106
        self.smart_server = server.SmartTCPServer_for_testing()
2762
 
        self.smart_server.setUp(self.get_server())
2763
 
        self.addCleanup(self.smart_server.tearDown)
 
3107
        self.start_server(self.smart_server, self.get_server())
2764
3108
        # Log all HPSS calls into self.hpss_calls.
2765
3109
        _SmartClient.hooks.install_named_hook(
2766
3110
            'call', self.capture_hpss_call, None)