~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_remote.py

  • Committer: Martin Pool
  • Date: 2010-02-27 01:34:49 UTC
  • mto: This revision was merged to the branch mainline in revision 5064.
  • Revision ID: mbp@canonical.com-20100227013449-zxostilwfoendxfv
Handle "Directory not empty" from ftp as DirectoryNotEmpty.

FtpTransport._translate_ftp_error can handle all ftp errors; there's no clear
distinction between 'temporary' and 'permament'.

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