~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_remote.py

  • Committer: Jelmer Vernooij
  • Date: 2011-10-14 12:49:33 UTC
  • mto: This revision was merged to the branch mainline in revision 6216.
  • Revision ID: jelmer@samba.org-20111014124933-732wmdc39ebzga0b
Fix last locking issues.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2010 Canonical Ltd
 
1
# Copyright (C) 2006-2011 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
30
30
    branch,
31
31
    bzrdir,
32
32
    config,
 
33
    controldir,
33
34
    errors,
34
 
    graph,
 
35
    graph as _mod_graph,
35
36
    inventory,
36
37
    inventory_delta,
37
 
    pack,
38
38
    remote,
39
39
    repository,
40
40
    tests,
 
41
    transport,
41
42
    treebuilder,
42
 
    urlutils,
43
43
    versionedfile,
44
44
    )
45
45
from bzrlib.branch import Branch
46
 
from bzrlib.bzrdir import BzrDir, BzrDirFormat
 
46
from bzrlib.bzrdir import (
 
47
    BzrDir,
 
48
    BzrDirFormat,
 
49
    RemoteBzrProber,
 
50
    )
47
51
from bzrlib.remote import (
48
52
    RemoteBranch,
49
53
    RemoteBranchFormat,
52
56
    RemoteRepository,
53
57
    RemoteRepositoryFormat,
54
58
    )
55
 
from bzrlib.repofmt import groupcompress_repo, pack_repo
 
59
from bzrlib.repofmt import groupcompress_repo, knitpack_repo
56
60
from bzrlib.revision import NULL_REVISION
57
 
from bzrlib.smart import medium
 
61
from bzrlib.smart import medium, request
58
62
from bzrlib.smart.client import _SmartClient
59
 
from bzrlib.smart.repository import SmartServerRepositoryGetParentMap
 
63
from bzrlib.smart.repository import (
 
64
    SmartServerRepositoryGetParentMap,
 
65
    SmartServerRepositoryGetStream_1_19,
 
66
    )
 
67
from bzrlib.symbol_versioning import deprecated_in
60
68
from bzrlib.tests import (
61
 
    condition_isinstance,
62
 
    split_suite_by_condition,
63
 
    multiply_tests,
64
69
    test_server,
65
70
    )
66
 
from bzrlib.transport import get_transport
 
71
from bzrlib.tests.scenarios import load_tests_apply_scenarios
67
72
from bzrlib.transport.memory import MemoryTransport
68
73
from bzrlib.transport.remote import (
69
74
    RemoteTransport,
70
75
    RemoteSSHTransport,
71
76
    RemoteTCPTransport,
72
 
)
73
 
 
74
 
def load_tests(standard_tests, module, loader):
75
 
    to_adapt, result = split_suite_by_condition(
76
 
        standard_tests, condition_isinstance(BasicRemoteObjectTests))
77
 
    smart_server_version_scenarios = [
 
77
    )
 
78
 
 
79
 
 
80
load_tests = load_tests_apply_scenarios
 
81
 
 
82
 
 
83
class BasicRemoteObjectTests(tests.TestCaseWithTransport):
 
84
 
 
85
    scenarios = [
78
86
        ('HPSS-v2',
79
 
         {'transport_server': test_server.SmartTCPServer_for_testing_v2_only}),
 
87
            {'transport_server': test_server.SmartTCPServer_for_testing_v2_only}),
80
88
        ('HPSS-v3',
81
 
         {'transport_server': test_server.SmartTCPServer_for_testing})]
82
 
    return multiply_tests(to_adapt, smart_server_version_scenarios, result)
83
 
 
84
 
 
85
 
class BasicRemoteObjectTests(tests.TestCaseWithTransport):
 
89
            {'transport_server': test_server.SmartTCPServer_for_testing})]
 
90
 
86
91
 
87
92
    def setUp(self):
88
93
        super(BasicRemoteObjectTests, self).setUp()
89
94
        self.transport = self.get_transport()
90
95
        # make a branch that can be opened over the smart transport
91
96
        self.local_wt = BzrDir.create_standalone_workingtree('.')
92
 
 
93
 
    def tearDown(self):
94
 
        self.transport.disconnect()
95
 
        tests.TestCaseWithTransport.tearDown(self)
 
97
        self.addCleanup(self.transport.disconnect)
96
98
 
97
99
    def test_create_remote_bzrdir(self):
98
 
        b = remote.RemoteBzrDir(self.transport, remote.RemoteBzrDirFormat())
 
100
        b = remote.RemoteBzrDir(self.transport, RemoteBzrDirFormat())
99
101
        self.assertIsInstance(b, BzrDir)
100
102
 
101
103
    def test_open_remote_branch(self):
102
104
        # open a standalone branch in the working directory
103
 
        b = remote.RemoteBzrDir(self.transport, remote.RemoteBzrDirFormat())
 
105
        b = remote.RemoteBzrDir(self.transport, RemoteBzrDirFormat())
104
106
        branch = b.open_branch()
105
107
        self.assertIsInstance(branch, Branch)
106
108
 
114
116
 
115
117
    def test_remote_branch_revision_history(self):
116
118
        b = BzrDir.open_from_transport(self.transport).open_branch()
117
 
        self.assertEqual([], b.revision_history())
 
119
        self.assertEqual([],
 
120
            self.applyDeprecated(deprecated_in((2, 5, 0)), b.revision_history))
118
121
        r1 = self.local_wt.commit('1st commit')
119
122
        r2 = self.local_wt.commit('1st commit', rev_id=u'\xc8'.encode('utf8'))
120
 
        self.assertEqual([r1, r2], b.revision_history())
 
123
        self.assertEqual([r1, r2],
 
124
            self.applyDeprecated(deprecated_in((2, 5, 0)), b.revision_history))
121
125
 
122
126
    def test_find_correct_format(self):
123
127
        """Should open a RemoteBzrDir over a RemoteTransport"""
124
128
        fmt = BzrDirFormat.find_format(self.transport)
125
 
        self.assertTrue(RemoteBzrDirFormat
126
 
                        in BzrDirFormat._control_server_formats)
127
 
        self.assertIsInstance(fmt, remote.RemoteBzrDirFormat)
 
129
        self.assertTrue(bzrdir.RemoteBzrProber
 
130
                        in controldir.ControlDirFormat._server_probers)
 
131
        self.assertIsInstance(fmt, RemoteBzrDirFormat)
128
132
 
129
133
    def test_open_detected_smart_format(self):
130
134
        fmt = BzrDirFormat.find_format(self.transport)
359
363
        a given client_base and transport_base.
360
364
        """
361
365
        client_medium = medium.SmartClientMedium(client_base)
362
 
        transport = get_transport(transport_base)
363
 
        result = client_medium.remote_path_from_transport(transport)
 
366
        t = transport.get_transport(transport_base)
 
367
        result = client_medium.remote_path_from_transport(t)
364
368
        self.assertEqual(expected, result)
365
369
 
366
370
    def test_remote_path_from_transport(self):
377
381
        a given transport_base and relpath of that transport.  (Note that
378
382
        HttpTransportBase is a subclass of SmartClientMedium)
379
383
        """
380
 
        base_transport = get_transport(transport_base)
 
384
        base_transport = transport.get_transport(transport_base)
381
385
        client_medium = base_transport.get_smart_medium()
382
386
        cloned_transport = base_transport.clone(relpath)
383
387
        result = client_medium.remote_path_from_transport(cloned_transport)
450
454
        client.add_expected_call(
451
455
            'BzrDir.open_branchV3', ('quack/',),
452
456
            'success', ('ref', self.get_url('referenced'))),
453
 
        a_bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
 
457
        a_bzrdir = RemoteBzrDir(transport, RemoteBzrDirFormat(),
454
458
            _client=client)
455
459
        result = a_bzrdir.cloning_metadir()
456
460
        # We should have got a control dir matching the referenced branch.
469
473
        client.add_expected_call(
470
474
            'BzrDir.cloning_metadir', ('quack/', 'False'),
471
475
            'success', (control_name, '', ('branch', ''))),
472
 
        a_bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
 
476
        a_bzrdir = RemoteBzrDir(transport, RemoteBzrDirFormat(),
473
477
            _client=client)
474
478
        result = a_bzrdir.cloning_metadir()
475
479
        # We should have got a reference control dir with default branch and
495
499
        client.add_expected_call(
496
500
            'BzrDir.open_2.1', ('quack/',), 'success', ('no',))
497
501
        self.assertRaises(errors.NotBranchError, RemoteBzrDir, transport,
498
 
                remote.RemoteBzrDirFormat(), _client=client, _force_probe=True)
 
502
                RemoteBzrDirFormat(), _client=client, _force_probe=True)
499
503
        self.assertFinished(client)
500
504
 
501
505
    def test_present_without_workingtree(self):
502
506
        client, transport = self.make_fake_client_and_transport()
503
507
        client.add_expected_call(
504
508
            'BzrDir.open_2.1', ('quack/',), 'success', ('yes', 'no'))
505
 
        bd = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
 
509
        bd = RemoteBzrDir(transport, RemoteBzrDirFormat(),
506
510
            _client=client, _force_probe=True)
507
511
        self.assertIsInstance(bd, RemoteBzrDir)
508
512
        self.assertFalse(bd.has_workingtree())
513
517
        client, transport = self.make_fake_client_and_transport()
514
518
        client.add_expected_call(
515
519
            'BzrDir.open_2.1', ('quack/',), 'success', ('yes', 'yes'))
516
 
        bd = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
 
520
        bd = RemoteBzrDir(transport, RemoteBzrDirFormat(),
517
521
            _client=client, _force_probe=True)
518
522
        self.assertIsInstance(bd, RemoteBzrDir)
519
523
        self.assertTrue(bd.has_workingtree())
526
530
            'BzrDir.open_2.1', ('quack/',), 'unknown', ('BzrDir.open_2.1',))
527
531
        client.add_expected_call(
528
532
            'BzrDir.open', ('quack/',), 'success', ('yes',))
529
 
        bd = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
 
533
        bd = RemoteBzrDir(transport, RemoteBzrDirFormat(),
530
534
            _client=client, _force_probe=True)
531
535
        self.assertIsInstance(bd, RemoteBzrDir)
532
536
        self.assertFinished(client)
548
552
            'BzrDir.open_2.1', ('quack/',), 'unknown', ('BzrDir.open_2.1',))
549
553
        client.add_expected_call(
550
554
            'BzrDir.open', ('quack/',), 'success', ('yes',))
551
 
        bd = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
 
555
        bd = RemoteBzrDir(transport, RemoteBzrDirFormat(),
552
556
            _client=client, _force_probe=True)
553
557
        self.assertIsInstance(bd, RemoteBzrDir)
554
558
        self.assertFinished(client)
585
589
        client.add_expected_call(
586
590
            'Branch.get_stacked_on_url', ('quack/',),
587
591
            'error', ('NotStacked',))
588
 
        bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
 
592
        bzrdir = RemoteBzrDir(transport, RemoteBzrDirFormat(),
589
593
            _client=client)
590
594
        result = bzrdir.open_branch()
591
595
        self.assertIsInstance(result, RemoteBranch)
598
602
        transport = transport.clone('quack')
599
603
        client = FakeClient(transport.base)
600
604
        client.add_error_response('nobranch')
601
 
        bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
 
605
        bzrdir = RemoteBzrDir(transport, RemoteBzrDirFormat(),
602
606
            _client=client)
603
607
        self.assertRaises(errors.NotBranchError, bzrdir.open_branch)
604
608
        self.assertEqual(
609
613
        # _get_tree_branch is a form of open_branch, but it should only ask for
610
614
        # branch opening, not any other network requests.
611
615
        calls = []
612
 
        def open_branch():
 
616
        def open_branch(name=None):
613
617
            calls.append("Called")
614
618
            return "a-branch"
615
619
        transport = MemoryTransport()
616
620
        # no requests on the network - catches other api calls being made.
617
621
        client = FakeClient(transport.base)
618
 
        bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
 
622
        bzrdir = RemoteBzrDir(transport, RemoteBzrDirFormat(),
619
623
            _client=client)
620
624
        # patch the open_branch call to record that it was called.
621
625
        bzrdir.open_branch = open_branch
640
644
        client.add_expected_call(
641
645
            'Branch.get_stacked_on_url', ('~hello/',),
642
646
            'error', ('NotStacked',))
643
 
        bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
 
647
        bzrdir = RemoteBzrDir(transport, RemoteBzrDirFormat(),
644
648
            _client=client)
645
649
        result = bzrdir.open_branch()
646
650
        self.assertFinished(client)
663
667
        client.add_success_response(
664
668
            'ok', '', rich_response, subtree_response, external_lookup,
665
669
            network_name)
666
 
        bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
 
670
        bzrdir = RemoteBzrDir(transport, RemoteBzrDirFormat(),
667
671
            _client=client)
668
672
        result = bzrdir.open_repository()
669
673
        self.assertEqual(
686
690
        old.
687
691
        """
688
692
        self.assertRaises(errors.NotBranchError,
689
 
            RemoteBzrDirFormat.probe_transport, OldServerTransport())
 
693
            RemoteBzrProber.probe_transport, OldServerTransport())
690
694
 
691
695
 
692
696
class TestBzrDirCreateBranch(TestRemote):
715
719
            'BzrDir.create_branch', ('quack/', network_name),
716
720
            'success', ('ok', network_name, '', 'no', 'no', 'yes',
717
721
            reference_repo_name))
718
 
        a_bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
 
722
        a_bzrdir = RemoteBzrDir(transport, RemoteBzrDirFormat(),
719
723
            _client=client)
720
724
        branch = a_bzrdir.create_branch()
721
725
        # We should have got a remote branch
724
728
        format = branch._format
725
729
        self.assertEqual(network_name, format.network_name())
726
730
 
 
731
    def test_already_open_repo_and_reused_medium(self):
 
732
        """Bug 726584: create_branch(..., repository=repo) should work
 
733
        regardless of what the smart medium's base URL is.
 
734
        """
 
735
        self.transport_server = test_server.SmartTCPServer_for_testing
 
736
        transport = self.get_transport('.')
 
737
        repo = self.make_repository('quack')
 
738
        # Client's medium rooted a transport root (not at the bzrdir)
 
739
        client = FakeClient(transport.base)
 
740
        transport = transport.clone('quack')
 
741
        reference_bzrdir_format = bzrdir.format_registry.get('default')()
 
742
        reference_format = reference_bzrdir_format.get_branch_format()
 
743
        network_name = reference_format.network_name()
 
744
        reference_repo_fmt = reference_bzrdir_format.repository_format
 
745
        reference_repo_name = reference_repo_fmt.network_name()
 
746
        client.add_expected_call(
 
747
            'BzrDir.create_branch', ('extra/quack/', network_name),
 
748
            'success', ('ok', network_name, '', 'no', 'no', 'yes',
 
749
            reference_repo_name))
 
750
        a_bzrdir = RemoteBzrDir(transport, RemoteBzrDirFormat(),
 
751
            _client=client)
 
752
        branch = a_bzrdir.create_branch(repository=repo)
 
753
        # We should have got a remote branch
 
754
        self.assertIsInstance(branch, remote.RemoteBranch)
 
755
        # its format should have the settings from the response
 
756
        format = branch._format
 
757
        self.assertEqual(network_name, format.network_name())
 
758
 
727
759
 
728
760
class TestBzrDirCreateRepository(TestRemote):
729
761
 
750
782
                'Bazaar repository format 2a (needs bzr 1.16 or later)\n',
751
783
                'False'),
752
784
            'success', ('ok', 'yes', 'yes', 'yes', network_name))
753
 
        a_bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
 
785
        a_bzrdir = RemoteBzrDir(transport, RemoteBzrDirFormat(),
754
786
            _client=client)
755
787
        repo = a_bzrdir.create_repository()
756
788
        # We should have got a remote repository
785
817
        client.add_success_response('stat', '0', '65535')
786
818
        remote_transport = RemoteTransport(server_url + 'quack/', medium=False,
787
819
            _client=client)
788
 
        bzrdir = RemoteBzrDir(remote_transport, remote.RemoteBzrDirFormat(),
 
820
        bzrdir = RemoteBzrDir(remote_transport, RemoteBzrDirFormat(),
789
821
            _client=client)
790
822
        repo = bzrdir.open_repository()
791
823
        self.assertEqual(
818
850
        client.add_success_response('stat', '0', '65535')
819
851
        remote_transport = RemoteTransport(server_url + 'quack/', medium=False,
820
852
            _client=client)
821
 
        bzrdir = RemoteBzrDir(remote_transport, remote.RemoteBzrDirFormat(),
 
853
        bzrdir = RemoteBzrDir(remote_transport, RemoteBzrDirFormat(),
822
854
            _client=client)
823
855
        repo = bzrdir.open_repository()
824
856
        self.assertEqual(
839
871
        transport = transport.clone('quack')
840
872
        client = FakeClient(transport.base)
841
873
        client.add_success_response('ok', '', 'no', 'no', 'no', network_name)
842
 
        bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
 
874
        bzrdir = RemoteBzrDir(transport, RemoteBzrDirFormat(),
843
875
            _client=client)
844
876
        repo = bzrdir.open_repository()
845
877
        self.assertEqual(
852
884
 
853
885
    def test_success(self):
854
886
        """Simple test for typical successful call."""
855
 
        fmt = bzrdir.RemoteBzrDirFormat()
 
887
        fmt = RemoteBzrDirFormat()
856
888
        default_format_name = BzrDirFormat.get_default_format().network_name()
857
889
        transport = self.get_transport()
858
890
        client = FakeClient(transport.base)
874
906
        """Error responses are translated, e.g. 'PermissionDenied' raises the
875
907
        corresponding error from the client.
876
908
        """
877
 
        fmt = bzrdir.RemoteBzrDirFormat()
 
909
        fmt = RemoteBzrDirFormat()
878
910
        default_format_name = BzrDirFormat.get_default_format().network_name()
879
911
        transport = self.get_transport()
880
912
        client = FakeClient(transport.base)
898
930
        """Integration test for error translation."""
899
931
        transport = self.make_smart_server('foo')
900
932
        transport = transport.clone('no-such-path')
901
 
        fmt = bzrdir.RemoteBzrDirFormat()
 
933
        fmt = RemoteBzrDirFormat()
902
934
        err = self.assertRaises(errors.NoSuchFile,
903
935
            fmt.initialize_on_transport_ex, transport, create_prefix=False)
904
936
 
935
967
 
936
968
    def make_remote_bzrdir(self, transport, client):
937
969
        """Make a RemotebzrDir using 'client' as the _client."""
938
 
        return RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
 
970
        return RemoteBzrDir(transport, RemoteBzrDirFormat(),
939
971
            _client=client)
940
972
 
941
973
 
1143
1175
            [('set_tags_bytes', 'tags bytes')] * 2, real_branch.calls)
1144
1176
 
1145
1177
 
 
1178
class TestBranchHeadsToFetch(RemoteBranchTestCase):
 
1179
 
 
1180
    def test_uses_last_revision_info_and_tags_by_default(self):
 
1181
        transport = MemoryTransport()
 
1182
        client = FakeClient(transport.base)
 
1183
        client.add_expected_call(
 
1184
            'Branch.get_stacked_on_url', ('quack/',),
 
1185
            'error', ('NotStacked',))
 
1186
        client.add_expected_call(
 
1187
            'Branch.last_revision_info', ('quack/',),
 
1188
            'success', ('ok', '1', 'rev-tip'))
 
1189
        client.add_expected_call(
 
1190
            'Branch.get_config_file', ('quack/',),
 
1191
            'success', ('ok',), '')
 
1192
        transport.mkdir('quack')
 
1193
        transport = transport.clone('quack')
 
1194
        branch = self.make_remote_branch(transport, client)
 
1195
        result = branch.heads_to_fetch()
 
1196
        self.assertFinished(client)
 
1197
        self.assertEqual((set(['rev-tip']), set()), result)
 
1198
 
 
1199
    def test_uses_last_revision_info_and_tags_when_set(self):
 
1200
        transport = MemoryTransport()
 
1201
        client = FakeClient(transport.base)
 
1202
        client.add_expected_call(
 
1203
            'Branch.get_stacked_on_url', ('quack/',),
 
1204
            'error', ('NotStacked',))
 
1205
        client.add_expected_call(
 
1206
            'Branch.last_revision_info', ('quack/',),
 
1207
            'success', ('ok', '1', 'rev-tip'))
 
1208
        client.add_expected_call(
 
1209
            'Branch.get_config_file', ('quack/',),
 
1210
            'success', ('ok',), 'branch.fetch_tags = True')
 
1211
        # XXX: this will break if the default format's serialization of tags
 
1212
        # changes, or if the RPC for fetching tags changes from get_tags_bytes.
 
1213
        client.add_expected_call(
 
1214
            'Branch.get_tags_bytes', ('quack/',),
 
1215
            'success', ('d5:tag-17:rev-foo5:tag-27:rev-bare',))
 
1216
        transport.mkdir('quack')
 
1217
        transport = transport.clone('quack')
 
1218
        branch = self.make_remote_branch(transport, client)
 
1219
        result = branch.heads_to_fetch()
 
1220
        self.assertFinished(client)
 
1221
        self.assertEqual(
 
1222
            (set(['rev-tip']), set(['rev-foo', 'rev-bar'])), result)
 
1223
 
 
1224
    def test_uses_rpc_for_formats_with_non_default_heads_to_fetch(self):
 
1225
        transport = MemoryTransport()
 
1226
        client = FakeClient(transport.base)
 
1227
        client.add_expected_call(
 
1228
            'Branch.get_stacked_on_url', ('quack/',),
 
1229
            'error', ('NotStacked',))
 
1230
        client.add_expected_call(
 
1231
            'Branch.heads_to_fetch', ('quack/',),
 
1232
            'success', (['tip'], ['tagged-1', 'tagged-2']))
 
1233
        transport.mkdir('quack')
 
1234
        transport = transport.clone('quack')
 
1235
        branch = self.make_remote_branch(transport, client)
 
1236
        branch._format._use_default_local_heads_to_fetch = lambda: False
 
1237
        result = branch.heads_to_fetch()
 
1238
        self.assertFinished(client)
 
1239
        self.assertEqual((set(['tip']), set(['tagged-1', 'tagged-2'])), result)
 
1240
 
 
1241
    def make_branch_with_tags(self):
 
1242
        self.setup_smart_server_with_call_log()
 
1243
        # Make a branch with a single revision.
 
1244
        builder = self.make_branch_builder('foo')
 
1245
        builder.start_series()
 
1246
        builder.build_snapshot('tip', None, [
 
1247
            ('add', ('', 'root-id', 'directory', ''))])
 
1248
        builder.finish_series()
 
1249
        branch = builder.get_branch()
 
1250
        # Add two tags to that branch
 
1251
        branch.tags.set_tag('tag-1', 'rev-1')
 
1252
        branch.tags.set_tag('tag-2', 'rev-2')
 
1253
        return branch
 
1254
 
 
1255
    def test_backwards_compatible(self):
 
1256
        branch = self.make_branch_with_tags()
 
1257
        c = branch.get_config()
 
1258
        c.set_user_option('branch.fetch_tags', 'True')
 
1259
        self.addCleanup(branch.lock_read().unlock)
 
1260
        # Disable the heads_to_fetch verb
 
1261
        verb = 'Branch.heads_to_fetch'
 
1262
        self.disable_verb(verb)
 
1263
        self.reset_smart_call_log()
 
1264
        result = branch.heads_to_fetch()
 
1265
        self.assertEqual((set(['tip']), set(['rev-1', 'rev-2'])), result)
 
1266
        self.assertEqual(
 
1267
            ['Branch.last_revision_info', 'Branch.get_config_file',
 
1268
             'Branch.get_tags_bytes'],
 
1269
            [call.call.method for call in self.hpss_calls])
 
1270
 
 
1271
    def test_backwards_compatible_no_tags(self):
 
1272
        branch = self.make_branch_with_tags()
 
1273
        c = branch.get_config()
 
1274
        c.set_user_option('branch.fetch_tags', 'False')
 
1275
        self.addCleanup(branch.lock_read().unlock)
 
1276
        # Disable the heads_to_fetch verb
 
1277
        verb = 'Branch.heads_to_fetch'
 
1278
        self.disable_verb(verb)
 
1279
        self.reset_smart_call_log()
 
1280
        result = branch.heads_to_fetch()
 
1281
        self.assertEqual((set(['tip']), set()), result)
 
1282
        self.assertEqual(
 
1283
            ['Branch.last_revision_info', 'Branch.get_config_file'],
 
1284
            [call.call.method for call in self.hpss_calls])
 
1285
 
 
1286
 
1146
1287
class TestBranchLastRevisionInfo(RemoteBranchTestCase):
1147
1288
 
1148
1289
    def test_empty_branch(self):
1203
1344
        client.add_expected_call(
1204
1345
            'Branch.get_stacked_on_url', ('stacked/',),
1205
1346
            'success', ('ok', vfs_url))
1206
 
        bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
 
1347
        bzrdir = RemoteBzrDir(transport, RemoteBzrDirFormat(),
1207
1348
            _client=client)
1208
1349
        repo_fmt = remote.RemoteRepositoryFormat()
1209
1350
        repo_fmt._custom_format = stacked_branch.repository._format
1236
1377
        # this will also do vfs access, but that goes direct to the transport
1237
1378
        # and isn't seen by the FakeClient.
1238
1379
        bzrdir = RemoteBzrDir(self.get_transport('stacked'),
1239
 
            remote.RemoteBzrDirFormat(), _client=client)
 
1380
            RemoteBzrDirFormat(), _client=client)
1240
1381
        branch = bzrdir.open_branch()
1241
1382
        result = branch.get_stacked_on_url()
1242
1383
        self.assertEqual('../base', result)
1269
1410
            'Branch.get_stacked_on_url', ('stacked/',),
1270
1411
            'success', ('ok', '../base'))
1271
1412
        bzrdir = RemoteBzrDir(self.get_transport('stacked'),
1272
 
            remote.RemoteBzrDirFormat(), _client=client)
 
1413
            RemoteBzrDirFormat(), _client=client)
1273
1414
        branch = bzrdir.open_branch()
1274
1415
        result = branch.get_stacked_on_url()
1275
1416
        self.assertEqual('../base', result)
1283
1424
class TestBranchSetLastRevision(RemoteBranchTestCase):
1284
1425
 
1285
1426
    def test_set_empty(self):
1286
 
        # set_revision_history([]) is translated to calling
 
1427
        # _set_last_revision_info('null:') is translated to calling
1287
1428
        # Branch.set_last_revision(path, '') on the wire.
1288
1429
        transport = MemoryTransport()
1289
1430
        transport.mkdir('branch')
1311
1452
        # unnecessarily invokes _ensure_real upon a call to lock_write.
1312
1453
        branch._ensure_real = lambda: None
1313
1454
        branch.lock_write()
1314
 
        result = branch.set_revision_history([])
 
1455
        result = branch._set_last_revision(NULL_REVISION)
1315
1456
        branch.unlock()
1316
1457
        self.assertEqual(None, result)
1317
1458
        self.assertFinished(client)
1318
1459
 
1319
1460
    def test_set_nonempty(self):
1320
 
        # set_revision_history([rev-id1, ..., rev-idN]) is translated to calling
 
1461
        # set_last_revision_info(N, rev-idN) is translated to calling
1321
1462
        # Branch.set_last_revision(path, rev-idN) on the wire.
1322
1463
        transport = MemoryTransport()
1323
1464
        transport.mkdir('branch')
1349
1490
        branch._ensure_real = lambda: None
1350
1491
        # Lock the branch, reset the record of remote calls.
1351
1492
        branch.lock_write()
1352
 
        result = branch.set_revision_history(['rev-id1', 'rev-id2'])
 
1493
        result = branch._set_last_revision('rev-id2')
1353
1494
        branch.unlock()
1354
1495
        self.assertEqual(None, result)
1355
1496
        self.assertFinished(client)
1385
1526
        branch = self.make_remote_branch(transport, client)
1386
1527
        branch.lock_write()
1387
1528
        self.assertRaises(
1388
 
            errors.NoSuchRevision, branch.set_revision_history, ['rev-id'])
 
1529
            errors.NoSuchRevision, branch._set_last_revision, 'rev-id')
1389
1530
        branch.unlock()
1390
1531
        self.assertFinished(client)
1391
1532
 
1422
1563
        branch._ensure_real = lambda: None
1423
1564
        branch.lock_write()
1424
1565
        # The 'TipChangeRejected' error response triggered by calling
1425
 
        # set_revision_history causes a TipChangeRejected exception.
 
1566
        # set_last_revision_info causes a TipChangeRejected exception.
1426
1567
        err = self.assertRaises(
1427
 
            errors.TipChangeRejected, branch.set_revision_history, ['rev-id'])
 
1568
            errors.TipChangeRejected,
 
1569
            branch._set_last_revision, 'rev-id')
1428
1570
        # The UTF-8 message from the response has been decoded into a unicode
1429
1571
        # object.
1430
1572
        self.assertIsInstance(err.msg, unicode)
1618
1760
    def test_get_multi_line_branch_conf(self):
1619
1761
        # Make sure that multiple-line branch.conf files are supported
1620
1762
        #
1621
 
        # https://bugs.edge.launchpad.net/bzr/+bug/354075
 
1763
        # https://bugs.launchpad.net/bzr/+bug/354075
1622
1764
        client = FakeClient()
1623
1765
        client.add_expected_call(
1624
1766
            'Branch.get_stacked_on_url', ('memory:///',),
1652
1794
        branch.unlock()
1653
1795
        self.assertFinished(client)
1654
1796
 
 
1797
    def test_set_option_with_dict(self):
 
1798
        client = FakeClient()
 
1799
        client.add_expected_call(
 
1800
            'Branch.get_stacked_on_url', ('memory:///',),
 
1801
            'error', ('NotStacked',),)
 
1802
        client.add_expected_call(
 
1803
            'Branch.lock_write', ('memory:///', '', ''),
 
1804
            'success', ('ok', 'branch token', 'repo token'))
 
1805
        encoded_dict_value = 'd5:ascii1:a11:unicode \xe2\x8c\x9a3:\xe2\x80\xbde'
 
1806
        client.add_expected_call(
 
1807
            'Branch.set_config_option_dict', ('memory:///', 'branch token',
 
1808
            'repo token', encoded_dict_value, 'foo', ''),
 
1809
            'success', ())
 
1810
        client.add_expected_call(
 
1811
            'Branch.unlock', ('memory:///', 'branch token', 'repo token'),
 
1812
            'success', ('ok',))
 
1813
        transport = MemoryTransport()
 
1814
        branch = self.make_remote_branch(transport, client)
 
1815
        branch.lock_write()
 
1816
        config = branch._get_config()
 
1817
        config.set_option(
 
1818
            {'ascii': 'a', u'unicode \N{WATCH}': u'\N{INTERROBANG}'},
 
1819
            'foo')
 
1820
        branch.unlock()
 
1821
        self.assertFinished(client)
 
1822
 
1655
1823
    def test_backwards_compat_set_option(self):
1656
1824
        self.setup_smart_server_with_call_log()
1657
1825
        branch = self.make_branch('.')
1664
1832
        self.assertLength(10, self.hpss_calls)
1665
1833
        self.assertEqual('value', branch._get_config().get_option('name'))
1666
1834
 
 
1835
    def test_backwards_compat_set_option_with_dict(self):
 
1836
        self.setup_smart_server_with_call_log()
 
1837
        branch = self.make_branch('.')
 
1838
        verb = 'Branch.set_config_option_dict'
 
1839
        self.disable_verb(verb)
 
1840
        branch.lock_write()
 
1841
        self.addCleanup(branch.unlock)
 
1842
        self.reset_smart_call_log()
 
1843
        config = branch._get_config()
 
1844
        value_dict = {'ascii': 'a', u'unicode \N{WATCH}': u'\N{INTERROBANG}'}
 
1845
        config.set_option(value_dict, 'name')
 
1846
        self.assertLength(10, self.hpss_calls)
 
1847
        self.assertEqual(value_dict, branch._get_config().get_option('name'))
 
1848
 
1667
1849
 
1668
1850
class TestBranchLockWrite(RemoteBranchTestCase):
1669
1851
 
1805
1987
        client = FakeClient(transport.base)
1806
1988
        transport = transport.clone(transport_path)
1807
1989
        # we do not want bzrdir to make any remote calls
1808
 
        bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
 
1990
        bzrdir = RemoteBzrDir(transport, RemoteBzrDirFormat(),
1809
1991
            _client=False)
1810
1992
        repo = RemoteRepository(bzrdir, None, _client=client)
1811
1993
        return repo, client
1819
2001
 
1820
2002
    def test_get_format_description(self):
1821
2003
        remote_format = RemoteBranchFormat()
1822
 
        real_format = branch.BranchFormat.get_default_format()
 
2004
        real_format = branch.format_registry.get_default()
1823
2005
        remote_format._network_name = real_format.network_name()
1824
2006
        self.assertEqual(remoted_description(real_format),
1825
2007
            remote_format.get_format_description())
1828
2010
class TestRepositoryFormat(TestRemoteRepository):
1829
2011
 
1830
2012
    def test_fast_delta(self):
1831
 
        true_name = groupcompress_repo.RepositoryFormatCHK1().network_name()
 
2013
        true_name = groupcompress_repo.RepositoryFormat2a().network_name()
1832
2014
        true_format = RemoteRepositoryFormat()
1833
2015
        true_format._network_name = true_name
1834
2016
        self.assertEqual(True, true_format.fast_deltas)
1835
 
        false_name = pack_repo.RepositoryFormatKnitPack1().network_name()
 
2017
        false_name = knitpack_repo.RepositoryFormatKnitPack1().network_name()
1836
2018
        false_format = RemoteRepositoryFormat()
1837
2019
        false_format._network_name = false_name
1838
2020
        self.assertEqual(False, false_format.fast_deltas)
1839
2021
 
1840
2022
    def test_get_format_description(self):
1841
2023
        remote_repo_format = RemoteRepositoryFormat()
1842
 
        real_format = repository.RepositoryFormat.get_default_format()
 
2024
        real_format = repository.format_registry.get_default()
1843
2025
        remote_repo_format._network_name = real_format.network_name()
1844
2026
        self.assertEqual(remoted_description(real_format),
1845
2027
            remote_repo_format.get_format_description())
1968
2150
        parents = repo.get_parent_map([rev_id])
1969
2151
        self.assertEqual(
1970
2152
            [('call_with_body_bytes_expecting_body',
1971
 
              'Repository.get_parent_map', ('quack/', 'include-missing:',
1972
 
              rev_id), '\n\n0'),
 
2153
              'Repository.get_parent_map',
 
2154
              ('quack/', 'include-missing:', rev_id), '\n\n0'),
1973
2155
             ('disconnect medium',),
1974
2156
             ('call_expecting_body', 'Repository.get_revision_graph',
1975
2157
              ('quack/', ''))],
2095
2277
        self.assertEqual({}, repo.get_parent_map(['non-existant']))
2096
2278
        self.assertLength(0, self.hpss_calls)
2097
2279
 
 
2280
    def test_exposes_get_cached_parent_map(self):
 
2281
        """RemoteRepository exposes get_cached_parent_map from
 
2282
        _unstacked_provider
 
2283
        """
 
2284
        r1 = u'\u0e33'.encode('utf8')
 
2285
        r2 = u'\u0dab'.encode('utf8')
 
2286
        lines = [' '.join([r2, r1]), r1]
 
2287
        encoded_body = bz2.compress('\n'.join(lines))
 
2288
 
 
2289
        transport_path = 'quack'
 
2290
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
2291
        client.add_success_response_with_body(encoded_body, 'ok')
 
2292
        repo.lock_read()
 
2293
        # get_cached_parent_map should *not* trigger an RPC
 
2294
        self.assertEqual({}, repo.get_cached_parent_map([r1]))
 
2295
        self.assertEqual([], client._calls)
 
2296
        self.assertEqual({r2: (r1,)}, repo.get_parent_map([r2]))
 
2297
        self.assertEqual({r1: (NULL_REVISION,)},
 
2298
            repo.get_cached_parent_map([r1]))
 
2299
        self.assertEqual(
 
2300
            [('call_with_body_bytes_expecting_body',
 
2301
              'Repository.get_parent_map', ('quack/', 'include-missing:', r2),
 
2302
              '\n\n0')],
 
2303
            client._calls)
 
2304
        repo.unlock()
 
2305
 
2098
2306
 
2099
2307
class TestGetParentMapAllowsNew(tests.TestCaseWithTransport):
2100
2308
 
2257
2465
        self.setup_smart_server_with_call_log()
2258
2466
        tree = self.make_branch_and_memory_tree('.')
2259
2467
        tree.lock_write()
 
2468
        tree.add('')
2260
2469
        rev1 = tree.commit('First')
2261
2470
        rev2 = tree.commit('Second')
2262
2471
        tree.unlock()
2301
2510
        transport_path = 'quack'
2302
2511
        repo, client = self.setup_fake_client_and_repository(transport_path)
2303
2512
        client.add_success_response('ok', 'a token')
2304
 
        result = repo.lock_write()
 
2513
        token = repo.lock_write().repository_token
2305
2514
        self.assertEqual(
2306
2515
            [('call', 'Repository.lock_write', ('quack/', ''))],
2307
2516
            client._calls)
2308
 
        self.assertEqual('a token', result)
 
2517
        self.assertEqual('a token', token)
2309
2518
 
2310
2519
    def test_lock_write_already_locked(self):
2311
2520
        transport_path = 'quack'
2402
2611
        the client is finished.
2403
2612
        """
2404
2613
        sink = repo._get_sink()
2405
 
        fmt = repository.RepositoryFormat.get_default_format()
 
2614
        fmt = repository.format_registry.get_default()
2406
2615
        resume_tokens, missing_keys = sink.insert_stream([], fmt, [])
2407
2616
        self.assertEqual([], resume_tokens)
2408
2617
        self.assertEqual(set(), missing_keys)
2508
2717
                return True
2509
2718
        repo._real_repository = FakeRealRepository()
2510
2719
        sink = repo._get_sink()
2511
 
        fmt = repository.RepositoryFormat.get_default_format()
 
2720
        fmt = repository.format_registry.get_default()
2512
2721
        stream = self.make_stream_with_inv_deltas(fmt)
2513
2722
        resume_tokens, missing_keys = sink.insert_stream(stream, fmt, [])
2514
2723
        # Every record from the first inventory delta should have been sent to
2734
2943
             ('pack collection autopack',)],
2735
2944
            client._calls)
2736
2945
 
 
2946
    def test_oom_error_reporting(self):
 
2947
        """An out-of-memory condition on the server is reported clearly"""
 
2948
        transport_path = 'quack'
 
2949
        repo, client = self.setup_fake_client_and_repository(transport_path)
 
2950
        client.add_expected_call(
 
2951
            'PackRepository.autopack', ('quack/',),
 
2952
            'error', ('MemoryError',))
 
2953
        err = self.assertRaises(errors.BzrError, repo.autopack)
 
2954
        self.assertContainsRe(str(err), "^remote server out of mem")
 
2955
 
2737
2956
 
2738
2957
class TestErrorTranslationBase(tests.TestCaseWithMemoryTransport):
2739
2958
    """Base class for unit tests for bzrlib.remote._translate_error."""
2812
3031
            detail='extra detail')
2813
3032
        self.assertEqual(expected_error, translated_error)
2814
3033
 
 
3034
    def test_norepository(self):
 
3035
        bzrdir = self.make_bzrdir('')
 
3036
        translated_error = self.translateTuple(('norepository',),
 
3037
            bzrdir=bzrdir)
 
3038
        expected_error = errors.NoRepositoryPresent(bzrdir)
 
3039
        self.assertEqual(expected_error, translated_error)
 
3040
 
2815
3041
    def test_LockContention(self):
2816
3042
        translated_error = self.translateTuple(('LockContention',))
2817
3043
        expected_error = errors.LockContention('(remote lock)')
2845
3071
        expected_error = errors.DivergedBranches(branch, other_branch)
2846
3072
        self.assertEqual(expected_error, translated_error)
2847
3073
 
 
3074
    def test_NotStacked(self):
 
3075
        branch = self.make_branch('')
 
3076
        translated_error = self.translateTuple(('NotStacked',), branch=branch)
 
3077
        expected_error = errors.NotStacked(branch)
 
3078
        self.assertEqual(expected_error, translated_error)
 
3079
 
2848
3080
    def test_ReadError_no_args(self):
2849
3081
        path = 'a path'
2850
3082
        translated_error = self.translateTuple(('ReadError',), path=path)
2866
3098
 
2867
3099
    def test_PermissionDenied_no_args(self):
2868
3100
        path = 'a path'
2869
 
        translated_error = self.translateTuple(('PermissionDenied',), path=path)
 
3101
        translated_error = self.translateTuple(('PermissionDenied',),
 
3102
            path=path)
2870
3103
        expected_error = errors.PermissionDenied(path)
2871
3104
        self.assertEqual(expected_error, translated_error)
2872
3105
 
2895
3128
        expected_error = errors.PermissionDenied(path, extra)
2896
3129
        self.assertEqual(expected_error, translated_error)
2897
3130
 
 
3131
    # GZ 2011-03-02: TODO test for PermissionDenied with non-ascii 'extra'
 
3132
 
 
3133
    def test_NoSuchFile_context_path(self):
 
3134
        local_path = "local path"
 
3135
        translated_error = self.translateTuple(('ReadError', "remote path"),
 
3136
            path=local_path)
 
3137
        expected_error = errors.ReadError(local_path)
 
3138
        self.assertEqual(expected_error, translated_error)
 
3139
 
 
3140
    def test_NoSuchFile_without_context(self):
 
3141
        remote_path = "remote path"
 
3142
        translated_error = self.translateTuple(('ReadError', remote_path))
 
3143
        expected_error = errors.ReadError(remote_path)
 
3144
        self.assertEqual(expected_error, translated_error)
 
3145
 
 
3146
    def test_ReadOnlyError(self):
 
3147
        translated_error = self.translateTuple(('ReadOnlyError',))
 
3148
        expected_error = errors.TransportNotPossible("readonly transport")
 
3149
        self.assertEqual(expected_error, translated_error)
 
3150
 
 
3151
    def test_MemoryError(self):
 
3152
        translated_error = self.translateTuple(('MemoryError',))
 
3153
        self.assertStartsWith(str(translated_error),
 
3154
            "remote server out of memory")
 
3155
 
 
3156
    def test_generic_IndexError_no_classname(self):
 
3157
        err = errors.ErrorFromSmartServer(('error', "list index out of range"))
 
3158
        translated_error = self.translateErrorFromSmartServer(err)
 
3159
        expected_error = errors.UnknownErrorFromSmartServer(err)
 
3160
        self.assertEqual(expected_error, translated_error)
 
3161
 
 
3162
    # GZ 2011-03-02: TODO test generic non-ascii error string
 
3163
 
 
3164
    def test_generic_KeyError(self):
 
3165
        err = errors.ErrorFromSmartServer(('error', 'KeyError', "1"))
 
3166
        translated_error = self.translateErrorFromSmartServer(err)
 
3167
        expected_error = errors.UnknownErrorFromSmartServer(err)
 
3168
        self.assertEqual(expected_error, translated_error)
 
3169
 
2898
3170
 
2899
3171
class TestErrorTranslationRobustness(TestErrorTranslationBase):
2900
3172
    """Unit tests for bzrlib.remote._translate_error's robustness.
3045
3317
        _, stacked = branch_factory()
3046
3318
        source = stacked.repository._get_source(target_repository_format)
3047
3319
        tip = stacked.last_revision()
3048
 
        revs = stacked.repository.get_ancestry(tip)
3049
 
        search = graph.PendingAncestryResult([tip], stacked.repository)
 
3320
        stacked.repository._ensure_real()
 
3321
        graph = stacked.repository.get_graph()
 
3322
        revs = [r for (r,ps) in graph.iter_ancestry([tip])
 
3323
                if r != NULL_REVISION]
 
3324
        revs.reverse()
 
3325
        search = _mod_graph.PendingAncestryResult([tip], stacked.repository)
3050
3326
        self.reset_smart_call_log()
3051
3327
        stream = source.get_stream(search)
3052
 
        if None in revs:
3053
 
            revs.remove(None)
3054
3328
        # We trust that if a revision is in the stream the rest of the new
3055
3329
        # content for it is too, as per our main fetch tests; here we are
3056
3330
        # checking that the revisions are actually included at all, and their
3095
3369
        self.assertEqual(expected_revs, rev_ord)
3096
3370
        # Getting topological sort requires VFS calls still - one of which is
3097
3371
        # pushing up from the bound branch.
3098
 
        self.assertLength(13, self.hpss_calls)
 
3372
        self.assertLength(14, self.hpss_calls)
3099
3373
 
3100
3374
    def test_stacked_get_stream_groupcompress(self):
3101
3375
        # Repository._get_source.get_stream() from a stacked repository with
3142
3416
 
3143
3417
    def test_copy_content_into_avoids_revision_history(self):
3144
3418
        local = self.make_branch('local')
3145
 
        remote_backing_tree = self.make_branch_and_tree('remote')
3146
 
        remote_backing_tree.commit("Commit.")
 
3419
        builder = self.make_branch_builder('remote')
 
3420
        builder.build_commit(message="Commit.")
3147
3421
        remote_branch_url = self.smart_server.get_url() + 'remote'
3148
3422
        remote_branch = bzrdir.BzrDir.open(remote_branch_url).open_branch()
3149
3423
        local.repository.fetch(remote_branch.repository)
3150
3424
        self.hpss_calls = []
3151
3425
        remote_branch.copy_content_into(local)
3152
3426
        self.assertFalse('Branch.revision_history' in self.hpss_calls)
 
3427
 
 
3428
    def test_fetch_everything_needs_just_one_call(self):
 
3429
        local = self.make_branch('local')
 
3430
        builder = self.make_branch_builder('remote')
 
3431
        builder.build_commit(message="Commit.")
 
3432
        remote_branch_url = self.smart_server.get_url() + 'remote'
 
3433
        remote_branch = bzrdir.BzrDir.open(remote_branch_url).open_branch()
 
3434
        self.hpss_calls = []
 
3435
        local.repository.fetch(
 
3436
            remote_branch.repository,
 
3437
            fetch_spec=_mod_graph.EverythingResult(remote_branch.repository))
 
3438
        self.assertEqual(['Repository.get_stream_1.19'], self.hpss_calls)
 
3439
 
 
3440
    def override_verb(self, verb_name, verb):
 
3441
        request_handlers = request.request_handlers
 
3442
        orig_verb = request_handlers.get(verb_name)
 
3443
        request_handlers.register(verb_name, verb, override_existing=True)
 
3444
        self.addCleanup(request_handlers.register, verb_name, orig_verb,
 
3445
                override_existing=True)
 
3446
 
 
3447
    def test_fetch_everything_backwards_compat(self):
 
3448
        """Can fetch with EverythingResult even with pre 2.4 servers.
 
3449
        
 
3450
        Pre-2.4 do not support 'everything' searches with the
 
3451
        Repository.get_stream_1.19 verb.
 
3452
        """
 
3453
        verb_log = []
 
3454
        class OldGetStreamVerb(SmartServerRepositoryGetStream_1_19):
 
3455
            """A version of the Repository.get_stream_1.19 verb patched to
 
3456
            reject 'everything' searches the way 2.3 and earlier do.
 
3457
            """
 
3458
            def recreate_search(self, repository, search_bytes,
 
3459
                                discard_excess=False):
 
3460
                verb_log.append(search_bytes.split('\n', 1)[0])
 
3461
                if search_bytes == 'everything':
 
3462
                    return (None,
 
3463
                            request.FailedSmartServerResponse(('BadSearch',)))
 
3464
                return super(OldGetStreamVerb,
 
3465
                        self).recreate_search(repository, search_bytes,
 
3466
                            discard_excess=discard_excess)
 
3467
        self.override_verb('Repository.get_stream_1.19', OldGetStreamVerb)
 
3468
        local = self.make_branch('local')
 
3469
        builder = self.make_branch_builder('remote')
 
3470
        builder.build_commit(message="Commit.")
 
3471
        remote_branch_url = self.smart_server.get_url() + 'remote'
 
3472
        remote_branch = bzrdir.BzrDir.open(remote_branch_url).open_branch()
 
3473
        self.hpss_calls = []
 
3474
        local.repository.fetch(
 
3475
            remote_branch.repository,
 
3476
            fetch_spec=_mod_graph.EverythingResult(remote_branch.repository))
 
3477
        # make sure the overridden verb was used
 
3478
        self.assertLength(1, verb_log)
 
3479
        # more than one HPSS call is needed, but because it's a VFS callback
 
3480
        # its hard to predict exactly how many.
 
3481
        self.assertTrue(len(self.hpss_calls) > 1)
 
3482
 
 
3483
 
 
3484
class TestUpdateBoundBranchWithModifiedBoundLocation(
 
3485
    tests.TestCaseWithTransport):
 
3486
    """Ensure correct handling of bound_location modifications.
 
3487
 
 
3488
    This is tested against a smart server as http://pad.lv/786980 was about a
 
3489
    ReadOnlyError (write attempt during a read-only transaction) which can only
 
3490
    happen in this context.
 
3491
    """
 
3492
 
 
3493
    def setUp(self):
 
3494
        super(TestUpdateBoundBranchWithModifiedBoundLocation, self).setUp()
 
3495
        self.transport_server = test_server.SmartTCPServer_for_testing
 
3496
 
 
3497
    def make_master_and_checkout(self, master_name, checkout_name):
 
3498
        # Create the master branch and its associated checkout
 
3499
        self.master = self.make_branch_and_tree(master_name)
 
3500
        self.checkout = self.master.branch.create_checkout(checkout_name)
 
3501
        # Modify the master branch so there is something to update
 
3502
        self.master.commit('add stuff')
 
3503
        self.last_revid = self.master.commit('even more stuff')
 
3504
        self.bound_location = self.checkout.branch.get_bound_location()
 
3505
 
 
3506
    def assertUpdateSucceeds(self, new_location):
 
3507
        self.checkout.branch.set_bound_location(new_location)
 
3508
        self.checkout.update()
 
3509
        self.assertEquals(self.last_revid, self.checkout.last_revision())
 
3510
 
 
3511
    def test_without_final_slash(self):
 
3512
        self.make_master_and_checkout('master', 'checkout')
 
3513
        # For unclear reasons some users have a bound_location without a final
 
3514
        # '/', simulate that by forcing such a value
 
3515
        self.assertEndsWith(self.bound_location, '/')
 
3516
        self.assertUpdateSucceeds(self.bound_location.rstrip('/'))
 
3517
 
 
3518
    def test_plus_sign(self):
 
3519
        self.make_master_and_checkout('+master', 'checkout')
 
3520
        self.assertUpdateSucceeds(self.bound_location.replace('%2B', '+', 1))
 
3521
 
 
3522
    def test_tilda(self):
 
3523
        # Embed ~ in the middle of the path just to avoid any $HOME
 
3524
        # interpretation
 
3525
        self.make_master_and_checkout('mas~ter', 'checkout')
 
3526
        self.assertUpdateSucceeds(self.bound_location.replace('%2E', '~', 1))