112
132
b = BzrDir.open_from_transport(self.transport).open_branch()
113
133
self.assertStartsWith(str(b), 'RemoteBranch(')
116
class FakeRemoteTransport(object):
117
"""This class provides the minimum support for use in place of a RemoteTransport.
119
It doesn't actually transmit requests, but rather expects them to be
120
handled by a FakeClient which holds canned responses. It does not allow
121
any vfs access, therefore is not suitable for testing any operation that
122
will fallback to vfs access. Backing the test by an instance of this
123
class guarantees that it's - done using non-vfs operations.
126
_default_url = 'fakeremotetransport://host/path/'
128
def __init__(self, url=None):
130
url = self._default_url
134
return "%r(%r)" % (self.__class__.__name__,
137
def clone(self, relpath):
138
return FakeRemoteTransport(urlutils.join(self.base, relpath))
140
def get(self, relpath):
141
# only get is specifically stubbed out, because it's usually the first
142
# thing we do. anything else will fail with an AttributeError.
143
raise AssertionError("%r doesn't support file access to %r"
135
def test_remote_branch_format_supports_stacking(self):
137
self.make_branch('unstackable', format='pack-0.92')
138
b = BzrDir.open_from_transport(t.clone('unstackable')).open_branch()
139
self.assertFalse(b._format.supports_stacking())
140
self.make_branch('stackable', format='1.9')
141
b = BzrDir.open_from_transport(t.clone('stackable')).open_branch()
142
self.assertTrue(b._format.supports_stacking())
144
def test_remote_repo_format_supports_external_references(self):
146
bd = self.make_bzrdir('unstackable', format='pack-0.92')
147
r = bd.create_repository()
148
self.assertFalse(r._format.supports_external_lookups)
149
r = BzrDir.open_from_transport(t.clone('unstackable')).open_repository()
150
self.assertFalse(r._format.supports_external_lookups)
151
bd = self.make_bzrdir('stackable', format='1.9')
152
r = bd.create_repository()
153
self.assertTrue(r._format.supports_external_lookups)
154
r = BzrDir.open_from_transport(t.clone('stackable')).open_repository()
155
self.assertTrue(r._format.supports_external_lookups)
157
def test_remote_branch_set_append_revisions_only(self):
158
# Make a format 1.9 branch, which supports append_revisions_only
159
branch = self.make_branch('branch', format='1.9')
160
config = branch.get_config()
161
branch.set_append_revisions_only(True)
163
'True', config.get_user_option('append_revisions_only'))
164
branch.set_append_revisions_only(False)
166
'False', config.get_user_option('append_revisions_only'))
168
def test_remote_branch_set_append_revisions_only_upgrade_reqd(self):
169
branch = self.make_branch('branch', format='knit')
170
config = branch.get_config()
172
errors.UpgradeRequired, branch.set_append_revisions_only, True)
148
175
class FakeProtocol(object):
359
415
AssertionError, client_medium._remember_remote_is_before, (1, 9))
362
class TestBzrDirOpenBranch(tests.TestCase):
418
class TestBzrDirCloningMetaDir(TestRemote):
420
def test_backwards_compat(self):
421
self.setup_smart_server_with_call_log()
422
a_dir = self.make_bzrdir('.')
423
self.reset_smart_call_log()
424
verb = 'BzrDir.cloning_metadir'
425
self.disable_verb(verb)
426
format = a_dir.cloning_metadir()
427
call_count = len([call for call in self.hpss_calls if
428
call.call.method == verb])
429
self.assertEqual(1, call_count)
431
def test_branch_reference(self):
432
transport = self.get_transport('quack')
433
referenced = self.make_branch('referenced')
434
expected = referenced.bzrdir.cloning_metadir()
435
client = FakeClient(transport.base)
436
client.add_expected_call(
437
'BzrDir.cloning_metadir', ('quack/', 'False'),
438
'error', ('BranchReference',)),
439
client.add_expected_call(
440
'BzrDir.open_branchV2', ('quack/',),
441
'success', ('ref', self.get_url('referenced'))),
442
a_bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
444
result = a_bzrdir.cloning_metadir()
445
# We should have got a control dir matching the referenced branch.
446
self.assertEqual(bzrdir.BzrDirMetaFormat1, type(result))
447
self.assertEqual(expected._repository_format, result._repository_format)
448
self.assertEqual(expected._branch_format, result._branch_format)
449
client.finished_test()
451
def test_current_server(self):
452
transport = self.get_transport('.')
453
transport = transport.clone('quack')
454
self.make_bzrdir('quack')
455
client = FakeClient(transport.base)
456
reference_bzrdir_format = bzrdir.format_registry.get('default')()
457
control_name = reference_bzrdir_format.network_name()
458
client.add_expected_call(
459
'BzrDir.cloning_metadir', ('quack/', 'False'),
460
'success', (control_name, '', ('branch', ''))),
461
a_bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
463
result = a_bzrdir.cloning_metadir()
464
# We should have got a reference control dir with default branch and
465
# repository formats.
466
# This pokes a little, just to be sure.
467
self.assertEqual(bzrdir.BzrDirMetaFormat1, type(result))
468
self.assertEqual(None, result._repository_format)
469
self.assertEqual(None, result._branch_format)
470
client.finished_test()
473
class TestBzrDirOpenBranch(TestRemote):
475
def test_backwards_compat(self):
476
self.setup_smart_server_with_call_log()
477
self.make_branch('.')
478
a_dir = BzrDir.open(self.get_url('.'))
479
self.reset_smart_call_log()
480
verb = 'BzrDir.open_branchV2'
481
self.disable_verb(verb)
482
format = a_dir.open_branch()
483
call_count = len([call for call in self.hpss_calls if
484
call.call.method == verb])
485
self.assertEqual(1, call_count)
364
487
def test_branch_present(self):
488
reference_format = self.get_repo_format()
489
network_name = reference_format.network_name()
490
branch_network_name = self.get_branch_format().network_name()
365
491
transport = MemoryTransport()
366
492
transport.mkdir('quack')
367
493
transport = transport.clone('quack')
368
494
client = FakeClient(transport.base)
369
495
client.add_expected_call(
370
'BzrDir.open_branch', ('quack/',),
371
'success', ('ok', ''))
496
'BzrDir.open_branchV2', ('quack/',),
497
'success', ('branch', branch_network_name))
372
498
client.add_expected_call(
373
'BzrDir.find_repositoryV2', ('quack/',),
374
'success', ('ok', '', 'no', 'no', 'no'))
499
'BzrDir.find_repositoryV3', ('quack/',),
500
'success', ('ok', '', 'no', 'no', 'no', network_name))
375
501
client.add_expected_call(
376
502
'Branch.get_stacked_on_url', ('quack/',),
377
503
'error', ('NotStacked',))
378
bzrdir = RemoteBzrDir(transport, _client=client)
504
bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
379
506
result = bzrdir.open_branch()
380
507
self.assertIsInstance(result, RemoteBranch)
381
508
self.assertEqual(bzrdir, result.bzrdir)
468
605
RemoteBzrDirFormat.probe_transport, OldServerTransport())
471
class TestBzrDirOpenRepository(tests.TestCase):
473
def test_backwards_compat_1_2(self):
474
transport = MemoryTransport()
475
transport.mkdir('quack')
476
transport = transport.clone('quack')
477
client = FakeClient(transport.base)
478
client.add_unknown_method_response('RemoteRepository.find_repositoryV2')
608
class TestBzrDirCreateBranch(TestRemote):
610
def test_backwards_compat(self):
611
self.setup_smart_server_with_call_log()
612
repo = self.make_repository('.')
613
self.reset_smart_call_log()
614
self.disable_verb('BzrDir.create_branch')
615
branch = repo.bzrdir.create_branch()
616
create_branch_call_count = len([call for call in self.hpss_calls if
617
call.call.method == 'BzrDir.create_branch'])
618
self.assertEqual(1, create_branch_call_count)
620
def test_current_server(self):
621
transport = self.get_transport('.')
622
transport = transport.clone('quack')
623
self.make_repository('quack')
624
client = FakeClient(transport.base)
625
reference_bzrdir_format = bzrdir.format_registry.get('default')()
626
reference_format = reference_bzrdir_format.get_branch_format()
627
network_name = reference_format.network_name()
628
reference_repo_fmt = reference_bzrdir_format.repository_format
629
reference_repo_name = reference_repo_fmt.network_name()
630
client.add_expected_call(
631
'BzrDir.create_branch', ('quack/', network_name),
632
'success', ('ok', network_name, '', 'no', 'no', 'yes',
633
reference_repo_name))
634
a_bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
636
branch = a_bzrdir.create_branch()
637
# We should have got a remote branch
638
self.assertIsInstance(branch, remote.RemoteBranch)
639
# its format should have the settings from the response
640
format = branch._format
641
self.assertEqual(network_name, format.network_name())
644
class TestBzrDirCreateRepository(TestRemote):
646
def test_backwards_compat(self):
647
self.setup_smart_server_with_call_log()
648
bzrdir = self.make_bzrdir('.')
649
self.reset_smart_call_log()
650
self.disable_verb('BzrDir.create_repository')
651
repo = bzrdir.create_repository()
652
create_repo_call_count = len([call for call in self.hpss_calls if
653
call.call.method == 'BzrDir.create_repository'])
654
self.assertEqual(1, create_repo_call_count)
656
def test_current_server(self):
657
transport = self.get_transport('.')
658
transport = transport.clone('quack')
659
self.make_bzrdir('quack')
660
client = FakeClient(transport.base)
661
reference_bzrdir_format = bzrdir.format_registry.get('default')()
662
reference_format = reference_bzrdir_format.repository_format
663
network_name = reference_format.network_name()
664
client.add_expected_call(
665
'BzrDir.create_repository', ('quack/',
666
'Bazaar pack repository format 1 (needs bzr 0.92)\n', 'False'),
667
'success', ('ok', 'no', 'no', 'no', network_name))
668
a_bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
670
repo = a_bzrdir.create_repository()
671
# We should have got a remote repository
672
self.assertIsInstance(repo, remote.RemoteRepository)
673
# its format should have the settings from the response
674
format = repo._format
675
self.assertFalse(format.rich_root_data)
676
self.assertFalse(format.supports_tree_reference)
677
self.assertFalse(format.supports_external_lookups)
678
self.assertEqual(network_name, format.network_name())
681
class TestBzrDirOpenRepository(TestRemote):
683
def test_backwards_compat_1_2_3(self):
684
# fallback all the way to the first version.
685
reference_format = self.get_repo_format()
686
network_name = reference_format.network_name()
687
client = FakeClient('bzr://example.com/')
688
client.add_unknown_method_response('BzrDir.find_repositoryV3')
689
client.add_unknown_method_response('BzrDir.find_repositoryV2')
479
690
client.add_success_response('ok', '', 'no', 'no')
480
bzrdir = RemoteBzrDir(transport, _client=client)
481
repo = bzrdir.open_repository()
483
[('call', 'BzrDir.find_repositoryV2', ('quack/',)),
484
('call', 'BzrDir.find_repository', ('quack/',))],
691
# A real repository instance will be created to determine the network
693
client.add_success_response_with_body(
694
"Bazaar-NG meta directory, format 1\n", 'ok')
695
client.add_success_response_with_body(
696
reference_format.get_format_string(), 'ok')
697
# PackRepository wants to do a stat
698
client.add_success_response('stat', '0', '65535')
699
remote_transport = RemoteTransport('bzr://example.com/quack/', medium=False,
701
bzrdir = RemoteBzrDir(remote_transport, remote.RemoteBzrDirFormat(),
703
repo = bzrdir.open_repository()
705
[('call', 'BzrDir.find_repositoryV3', ('quack/',)),
706
('call', 'BzrDir.find_repositoryV2', ('quack/',)),
707
('call', 'BzrDir.find_repository', ('quack/',)),
708
('call_expecting_body', 'get', ('/quack/.bzr/branch-format',)),
709
('call_expecting_body', 'get', ('/quack/.bzr/repository/format',)),
710
('call', 'stat', ('/quack/.bzr/repository',)),
713
self.assertEqual(network_name, repo._format.network_name())
715
def test_backwards_compat_2(self):
716
# fallback to find_repositoryV2
717
reference_format = self.get_repo_format()
718
network_name = reference_format.network_name()
719
client = FakeClient('bzr://example.com/')
720
client.add_unknown_method_response('BzrDir.find_repositoryV3')
721
client.add_success_response('ok', '', 'no', 'no', 'no')
722
# A real repository instance will be created to determine the network
724
client.add_success_response_with_body(
725
"Bazaar-NG meta directory, format 1\n", 'ok')
726
client.add_success_response_with_body(
727
reference_format.get_format_string(), 'ok')
728
# PackRepository wants to do a stat
729
client.add_success_response('stat', '0', '65535')
730
remote_transport = RemoteTransport('bzr://example.com/quack/', medium=False,
732
bzrdir = RemoteBzrDir(remote_transport, remote.RemoteBzrDirFormat(),
734
repo = bzrdir.open_repository()
736
[('call', 'BzrDir.find_repositoryV3', ('quack/',)),
737
('call', 'BzrDir.find_repositoryV2', ('quack/',)),
738
('call_expecting_body', 'get', ('/quack/.bzr/branch-format',)),
739
('call_expecting_body', 'get', ('/quack/.bzr/repository/format',)),
740
('call', 'stat', ('/quack/.bzr/repository',)),
743
self.assertEqual(network_name, repo._format.network_name())
745
def test_current_server(self):
746
reference_format = self.get_repo_format()
747
network_name = reference_format.network_name()
748
transport = MemoryTransport()
749
transport.mkdir('quack')
750
transport = transport.clone('quack')
751
client = FakeClient(transport.base)
752
client.add_success_response('ok', '', 'no', 'no', 'no', network_name)
753
bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
755
repo = bzrdir.open_repository()
757
[('call', 'BzrDir.find_repositoryV3', ('quack/',))],
759
self.assertEqual(network_name, repo._format.network_name())
762
class TestBzrDirFormatInitializeEx(TestRemote):
764
def test_success(self):
765
"""Simple test for typical successful call."""
766
fmt = bzrdir.RemoteBzrDirFormat()
767
default_format_name = BzrDirFormat.get_default_format().network_name()
768
transport = self.get_transport()
769
client = FakeClient(transport.base)
770
client.add_expected_call(
771
'BzrDirFormat.initialize_ex_1.16',
772
(default_format_name, 'path', 'False', 'False', 'False', '',
773
'', '', '', 'False'),
775
('.', 'no', 'no', 'yes', 'repo fmt', 'repo bzrdir fmt',
776
'bzrdir fmt', 'False', '', '', 'repo lock token'))
777
# XXX: It would be better to call fmt.initialize_on_transport_ex, but
778
# it's currently hard to test that without supplying a real remote
779
# transport connected to a real server.
780
result = fmt._initialize_on_transport_ex_rpc(client, 'path',
781
transport, False, False, False, None, None, None, None, False)
782
client.finished_test()
784
def test_error(self):
785
"""Error responses are translated, e.g. 'PermissionDenied' raises the
786
corresponding error from the client.
788
fmt = bzrdir.RemoteBzrDirFormat()
789
default_format_name = BzrDirFormat.get_default_format().network_name()
790
transport = self.get_transport()
791
client = FakeClient(transport.base)
792
client.add_expected_call(
793
'BzrDirFormat.initialize_ex_1.16',
794
(default_format_name, 'path', 'False', 'False', 'False', '',
795
'', '', '', 'False'),
797
('PermissionDenied', 'path', 'extra info'))
798
# XXX: It would be better to call fmt.initialize_on_transport_ex, but
799
# it's currently hard to test that without supplying a real remote
800
# transport connected to a real server.
801
err = self.assertRaises(errors.PermissionDenied,
802
fmt._initialize_on_transport_ex_rpc, client, 'path', transport,
803
False, False, False, None, None, None, None, False)
804
self.assertEqual('path', err.path)
805
self.assertEqual(': extra info', err.extra)
806
client.finished_test()
808
def test_error_from_real_server(self):
809
"""Integration test for error translation."""
810
transport = self.make_smart_server('foo')
811
transport = transport.clone('no-such-path')
812
fmt = bzrdir.RemoteBzrDirFormat()
813
err = self.assertRaises(errors.NoSuchFile,
814
fmt.initialize_on_transport_ex, transport, create_prefix=False)
488
817
class OldSmartClient(object):
513
842
return OldSmartClient()
516
class RemoteBranchTestCase(tests.TestCase):
845
class RemoteBzrDirTestCase(TestRemote):
847
def make_remote_bzrdir(self, transport, client):
848
"""Make a RemotebzrDir using 'client' as the _client."""
849
return RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
853
class RemoteBranchTestCase(RemoteBzrDirTestCase):
518
855
def make_remote_branch(self, transport, client):
519
856
"""Make a RemoteBranch using 'client' as its _SmartClient.
521
858
A RemoteBzrDir and RemoteRepository will also be created to fill out
522
859
the RemoteBranch, albeit with stub values for some of their attributes.
524
861
# we do not want bzrdir to make any remote calls, so use False as its
525
862
# _client. If it tries to make a remote call, this will fail
527
bzrdir = RemoteBzrDir(transport, _client=False)
864
bzrdir = self.make_remote_bzrdir(transport, False)
528
865
repo = RemoteRepository(bzrdir, None, _client=client)
529
return RemoteBranch(bzrdir, repo, _client=client)
866
branch_format = self.get_branch_format()
867
format = RemoteBranchFormat(network_name=branch_format.network_name())
868
return RemoteBranch(bzrdir, repo, _client=client, format=format)
871
class TestBranchGetParent(RemoteBranchTestCase):
873
def test_no_parent(self):
874
# in an empty branch we decode the response properly
875
transport = MemoryTransport()
876
client = FakeClient(transport.base)
877
client.add_expected_call(
878
'Branch.get_stacked_on_url', ('quack/',),
879
'error', ('NotStacked',))
880
client.add_expected_call(
881
'Branch.get_parent', ('quack/',),
883
transport.mkdir('quack')
884
transport = transport.clone('quack')
885
branch = self.make_remote_branch(transport, client)
886
result = branch.get_parent()
887
client.finished_test()
888
self.assertEqual(None, result)
890
def test_parent_relative(self):
891
transport = MemoryTransport()
892
client = FakeClient(transport.base)
893
client.add_expected_call(
894
'Branch.get_stacked_on_url', ('kwaak/',),
895
'error', ('NotStacked',))
896
client.add_expected_call(
897
'Branch.get_parent', ('kwaak/',),
898
'success', ('../foo/',))
899
transport.mkdir('kwaak')
900
transport = transport.clone('kwaak')
901
branch = self.make_remote_branch(transport, client)
902
result = branch.get_parent()
903
self.assertEqual(transport.clone('../foo').base, result)
905
def test_parent_absolute(self):
906
transport = MemoryTransport()
907
client = FakeClient(transport.base)
908
client.add_expected_call(
909
'Branch.get_stacked_on_url', ('kwaak/',),
910
'error', ('NotStacked',))
911
client.add_expected_call(
912
'Branch.get_parent', ('kwaak/',),
913
'success', ('http://foo/',))
914
transport.mkdir('kwaak')
915
transport = transport.clone('kwaak')
916
branch = self.make_remote_branch(transport, client)
917
result = branch.get_parent()
918
self.assertEqual('http://foo/', result)
919
client.finished_test()
922
class TestBranchSetParentLocation(RemoteBranchTestCase):
924
def test_no_parent(self):
925
# We call the verb when setting parent to None
926
transport = MemoryTransport()
927
client = FakeClient(transport.base)
928
client.add_expected_call(
929
'Branch.get_stacked_on_url', ('quack/',),
930
'error', ('NotStacked',))
931
client.add_expected_call(
932
'Branch.set_parent_location', ('quack/', 'b', 'r', ''),
934
transport.mkdir('quack')
935
transport = transport.clone('quack')
936
branch = self.make_remote_branch(transport, client)
937
branch._lock_token = 'b'
938
branch._repo_lock_token = 'r'
939
branch._set_parent_location(None)
940
client.finished_test()
942
def test_parent(self):
943
transport = MemoryTransport()
944
client = FakeClient(transport.base)
945
client.add_expected_call(
946
'Branch.get_stacked_on_url', ('kwaak/',),
947
'error', ('NotStacked',))
948
client.add_expected_call(
949
'Branch.set_parent_location', ('kwaak/', 'b', 'r', 'foo'),
951
transport.mkdir('kwaak')
952
transport = transport.clone('kwaak')
953
branch = self.make_remote_branch(transport, client)
954
branch._lock_token = 'b'
955
branch._repo_lock_token = 'r'
956
branch._set_parent_location('foo')
957
client.finished_test()
959
def test_backwards_compat(self):
960
self.setup_smart_server_with_call_log()
961
branch = self.make_branch('.')
962
self.reset_smart_call_log()
963
verb = 'Branch.set_parent_location'
964
self.disable_verb(verb)
965
branch.set_parent('http://foo/')
966
self.assertLength(12, self.hpss_calls)
969
class TestBranchGetTagsBytes(RemoteBranchTestCase):
971
def test_backwards_compat(self):
972
self.setup_smart_server_with_call_log()
973
branch = self.make_branch('.')
974
self.reset_smart_call_log()
975
verb = 'Branch.get_tags_bytes'
976
self.disable_verb(verb)
977
branch.tags.get_tag_dict()
978
call_count = len([call for call in self.hpss_calls if
979
call.call.method == verb])
980
self.assertEqual(1, call_count)
982
def test_trivial(self):
983
transport = MemoryTransport()
984
client = FakeClient(transport.base)
985
client.add_expected_call(
986
'Branch.get_stacked_on_url', ('quack/',),
987
'error', ('NotStacked',))
988
client.add_expected_call(
989
'Branch.get_tags_bytes', ('quack/',),
991
transport.mkdir('quack')
992
transport = transport.clone('quack')
993
branch = self.make_remote_branch(transport, client)
994
result = branch.tags.get_tag_dict()
995
client.finished_test()
996
self.assertEqual({}, result)
532
999
class TestBranchLastRevisionInfo(RemoteBranchTestCase):
566
1033
self.assertEqual((2, revid), result)
569
class TestBranch_get_stacked_on_url(tests.TestCaseWithMemoryTransport):
1036
class TestBranch_get_stacked_on_url(TestRemote):
570
1037
"""Test Branch._get_stacked_on_url rpc"""
572
1039
def test_get_stacked_on_invalid_url(self):
573
raise tests.KnownFailure('opening a branch requires the server to open the fallback repository')
574
transport = FakeRemoteTransport('fakeremotetransport:///')
1040
# test that asking for a stacked on url the server can't access works.
1041
# This isn't perfect, but then as we're in the same process there
1042
# really isn't anything we can do to be 100% sure that the server
1043
# doesn't just open in - this test probably needs to be rewritten using
1044
# a spawn()ed server.
1045
stacked_branch = self.make_branch('stacked', format='1.9')
1046
memory_branch = self.make_branch('base', format='1.9')
1047
vfs_url = self.get_vfs_only_url('base')
1048
stacked_branch.set_stacked_on_url(vfs_url)
1049
transport = stacked_branch.bzrdir.root_transport
575
1050
client = FakeClient(transport.base)
576
1051
client.add_expected_call(
577
'Branch.get_stacked_on_url', ('.',),
578
'success', ('ok', 'file:///stacked/on'))
579
bzrdir = RemoteBzrDir(transport, _client=client)
580
branch = RemoteBranch(bzrdir, None, _client=client)
1052
'Branch.get_stacked_on_url', ('stacked/',),
1053
'success', ('ok', vfs_url))
1054
# XXX: Multiple calls are bad, this second call documents what is
1056
client.add_expected_call(
1057
'Branch.get_stacked_on_url', ('stacked/',),
1058
'success', ('ok', vfs_url))
1059
bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
1061
repo_fmt = remote.RemoteRepositoryFormat()
1062
repo_fmt._custom_format = stacked_branch.repository._format
1063
branch = RemoteBranch(bzrdir, RemoteRepository(bzrdir, repo_fmt),
581
1065
result = branch.get_stacked_on_url()
583
'file:///stacked/on', result)
1066
self.assertEqual(vfs_url, result)
585
1068
def test_backwards_compatible(self):
586
1069
# like with bzr1.6 with no Branch.get_stacked_on_url rpc
938
1460
self.assertEqual('rejection message', err.msg)
941
class TestBranchControlGetBranchConf(tests.TestCaseWithMemoryTransport):
942
"""Getting the branch configuration should use an abstract method not vfs.
1463
class TestBranchGetSetConfig(RemoteBranchTestCase):
945
1465
def test_get_branch_conf(self):
946
raise tests.KnownFailure('branch.conf is not retrieved by get_config_file')
947
## # We should see that branch.get_config() does a single rpc to get the
948
## # remote configuration file, abstracting away where that is stored on
949
## # the server. However at the moment it always falls back to using the
950
## # vfs, and this would need some changes in config.py.
952
## # in an empty branch we decode the response properly
953
## client = FakeClient([(('ok', ), '# config file body')], self.get_url())
954
## # we need to make a real branch because the remote_branch.control_files
955
## # will trigger _ensure_real.
956
## branch = self.make_branch('quack')
957
## transport = branch.bzrdir.root_transport
958
## # we do not want bzrdir to make any remote calls
959
## bzrdir = RemoteBzrDir(transport, _client=False)
960
## branch = RemoteBranch(bzrdir, None, _client=client)
961
## config = branch.get_config()
963
## [('call_expecting_body', 'Branch.get_config_file', ('quack/',))],
1466
# in an empty branch we decode the response properly
1467
client = FakeClient()
1468
client.add_expected_call(
1469
'Branch.get_stacked_on_url', ('memory:///',),
1470
'error', ('NotStacked',),)
1471
client.add_success_response_with_body('# config file body', 'ok')
1472
transport = MemoryTransport()
1473
branch = self.make_remote_branch(transport, client)
1474
config = branch.get_config()
1475
config.has_explicit_nickname()
1477
[('call', 'Branch.get_stacked_on_url', ('memory:///',)),
1478
('call_expecting_body', 'Branch.get_config_file', ('memory:///',))],
1481
def test_get_multi_line_branch_conf(self):
1482
# Make sure that multiple-line branch.conf files are supported
1484
# https://bugs.edge.launchpad.net/bzr/+bug/354075
1485
client = FakeClient()
1486
client.add_expected_call(
1487
'Branch.get_stacked_on_url', ('memory:///',),
1488
'error', ('NotStacked',),)
1489
client.add_success_response_with_body('a = 1\nb = 2\nc = 3\n', 'ok')
1490
transport = MemoryTransport()
1491
branch = self.make_remote_branch(transport, client)
1492
config = branch.get_config()
1493
self.assertEqual(u'2', config.get_user_option('b'))
1495
def test_set_option(self):
1496
client = FakeClient()
1497
client.add_expected_call(
1498
'Branch.get_stacked_on_url', ('memory:///',),
1499
'error', ('NotStacked',),)
1500
client.add_expected_call(
1501
'Branch.lock_write', ('memory:///', '', ''),
1502
'success', ('ok', 'branch token', 'repo token'))
1503
client.add_expected_call(
1504
'Branch.set_config_option', ('memory:///', 'branch token',
1505
'repo token', 'foo', 'bar', ''),
1507
client.add_expected_call(
1508
'Branch.unlock', ('memory:///', 'branch token', 'repo token'),
1510
transport = MemoryTransport()
1511
branch = self.make_remote_branch(transport, client)
1513
config = branch._get_config()
1514
config.set_option('foo', 'bar')
1516
client.finished_test()
1518
def test_backwards_compat_set_option(self):
1519
self.setup_smart_server_with_call_log()
1520
branch = self.make_branch('.')
1521
verb = 'Branch.set_config_option'
1522
self.disable_verb(verb)
1524
self.addCleanup(branch.unlock)
1525
self.reset_smart_call_log()
1526
branch._get_config().set_option('value', 'name')
1527
self.assertLength(10, self.hpss_calls)
1528
self.assertEqual('value', branch._get_config().get_option('name'))
967
1531
class TestBranchLockWrite(RemoteBranchTestCase):
1243
1850
errors.UnexpectedSmartServerResponse,
1244
1851
repo.get_parent_map, ['a-revision-id'])
1853
def test_get_parent_map_negative_caches_missing_keys(self):
1854
self.setup_smart_server_with_call_log()
1855
repo = self.make_repository('foo')
1856
self.assertIsInstance(repo, RemoteRepository)
1858
self.addCleanup(repo.unlock)
1859
self.reset_smart_call_log()
1860
graph = repo.get_graph()
1861
self.assertEqual({},
1862
graph.get_parent_map(['some-missing', 'other-missing']))
1863
self.assertLength(1, self.hpss_calls)
1864
# No call if we repeat this
1865
self.reset_smart_call_log()
1866
graph = repo.get_graph()
1867
self.assertEqual({},
1868
graph.get_parent_map(['some-missing', 'other-missing']))
1869
self.assertLength(0, self.hpss_calls)
1870
# Asking for more unknown keys makes a request.
1871
self.reset_smart_call_log()
1872
graph = repo.get_graph()
1873
self.assertEqual({},
1874
graph.get_parent_map(['some-missing', 'other-missing',
1876
self.assertLength(1, self.hpss_calls)
1878
def disableExtraResults(self):
1879
old_flag = SmartServerRepositoryGetParentMap.no_extra_results
1880
SmartServerRepositoryGetParentMap.no_extra_results = True
1882
SmartServerRepositoryGetParentMap.no_extra_results = old_flag
1883
self.addCleanup(reset_values)
1885
def test_null_cached_missing_and_stop_key(self):
1886
self.setup_smart_server_with_call_log()
1887
# Make a branch with a single revision.
1888
builder = self.make_branch_builder('foo')
1889
builder.start_series()
1890
builder.build_snapshot('first', None, [
1891
('add', ('', 'root-id', 'directory', ''))])
1892
builder.finish_series()
1893
branch = builder.get_branch()
1894
repo = branch.repository
1895
self.assertIsInstance(repo, RemoteRepository)
1896
# Stop the server from sending extra results.
1897
self.disableExtraResults()
1899
self.addCleanup(repo.unlock)
1900
self.reset_smart_call_log()
1901
graph = repo.get_graph()
1902
# Query for 'first' and 'null:'. Because 'null:' is a parent of
1903
# 'first' it will be a candidate for the stop_keys of subsequent
1904
# requests, and because 'null:' was queried but not returned it will be
1905
# cached as missing.
1906
self.assertEqual({'first': ('null:',)},
1907
graph.get_parent_map(['first', 'null:']))
1908
# Now query for another key. This request will pass along a recipe of
1909
# start and stop keys describing the already cached results, and this
1910
# recipe's revision count must be correct (or else it will trigger an
1911
# error from the server).
1912
self.assertEqual({}, graph.get_parent_map(['another-key']))
1913
# This assertion guards against disableExtraResults silently failing to
1914
# work, thus invalidating the test.
1915
self.assertLength(2, self.hpss_calls)
1917
def test_get_parent_map_gets_ghosts_from_result(self):
1918
# asking for a revision should negatively cache close ghosts in its
1920
self.setup_smart_server_with_call_log()
1921
tree = self.make_branch_and_memory_tree('foo')
1924
builder = treebuilder.TreeBuilder()
1925
builder.start_tree(tree)
1927
builder.finish_tree()
1928
tree.set_parent_ids(['non-existant'], allow_leftmost_as_ghost=True)
1929
rev_id = tree.commit('')
1933
self.addCleanup(tree.unlock)
1934
repo = tree.branch.repository
1935
self.assertIsInstance(repo, RemoteRepository)
1937
repo.get_parent_map([rev_id])
1938
self.reset_smart_call_log()
1939
# Now asking for rev_id's ghost parent should not make calls
1940
self.assertEqual({}, repo.get_parent_map(['non-existant']))
1941
self.assertLength(0, self.hpss_calls)
1247
1944
class TestGetParentMapAllowsNew(tests.TestCaseWithTransport):
1332
2032
repo, client = self.setup_fake_client_and_repository(transport_path)
1333
2033
client.add_error_response('AnUnexpectedError')
1334
2034
e = self.assertRaises(errors.UnknownErrorFromSmartServer,
1335
self.applyDeprecated, one_four, repo.get_revision_graph, revid)
2035
repo._get_revision_graph, revid)
1336
2036
self.assertEqual(('AnUnexpectedError',), e.error_tuple)
2039
class TestRepositoryGetRevIdForRevno(TestRemoteRepository):
2042
repo, client = self.setup_fake_client_and_repository('quack')
2043
client.add_expected_call(
2044
'Repository.get_rev_id_for_revno', ('quack/', 5, (42, 'rev-foo')),
2045
'success', ('ok', 'rev-five'))
2046
result = repo.get_rev_id_for_revno(5, (42, 'rev-foo'))
2047
self.assertEqual((True, 'rev-five'), result)
2048
client.finished_test()
2050
def test_history_incomplete(self):
2051
repo, client = self.setup_fake_client_and_repository('quack')
2052
client.add_expected_call(
2053
'Repository.get_rev_id_for_revno', ('quack/', 5, (42, 'rev-foo')),
2054
'success', ('history-incomplete', 10, 'rev-ten'))
2055
result = repo.get_rev_id_for_revno(5, (42, 'rev-foo'))
2056
self.assertEqual((False, (10, 'rev-ten')), result)
2057
client.finished_test()
2059
def test_history_incomplete_with_fallback(self):
2060
"""A 'history-incomplete' response causes the fallback repository to be
2061
queried too, if one is set.
2063
# Make a repo with a fallback repo, both using a FakeClient.
2064
format = remote.response_tuple_to_repo_format(
2065
('yes', 'no', 'yes', 'fake-network-name'))
2066
repo, client = self.setup_fake_client_and_repository('quack')
2067
repo._format = format
2068
fallback_repo, ignored = self.setup_fake_client_and_repository(
2070
fallback_repo._client = client
2071
repo.add_fallback_repository(fallback_repo)
2072
# First the client should ask the primary repo
2073
client.add_expected_call(
2074
'Repository.get_rev_id_for_revno', ('quack/', 1, (42, 'rev-foo')),
2075
'success', ('history-incomplete', 2, 'rev-two'))
2076
# Then it should ask the fallback, using revno/revid from the
2077
# history-incomplete response as the known revno/revid.
2078
client.add_expected_call(
2079
'Repository.get_rev_id_for_revno',('fallback/', 1, (2, 'rev-two')),
2080
'success', ('ok', 'rev-one'))
2081
result = repo.get_rev_id_for_revno(1, (42, 'rev-foo'))
2082
self.assertEqual((True, 'rev-one'), result)
2083
client.finished_test()
2085
def test_nosuchrevision(self):
2086
# 'nosuchrevision' is returned when the known-revid is not found in the
2087
# remote repo. The client translates that response to NoSuchRevision.
2088
repo, client = self.setup_fake_client_and_repository('quack')
2089
client.add_expected_call(
2090
'Repository.get_rev_id_for_revno', ('quack/', 5, (42, 'rev-foo')),
2091
'error', ('nosuchrevision', 'rev-foo'))
2093
errors.NoSuchRevision,
2094
repo.get_rev_id_for_revno, 5, (42, 'rev-foo'))
2095
client.finished_test()
1339
2098
class TestRepositoryIsShared(TestRemoteRepository):
1341
2100
def test_is_shared(self):
1430
2215
self.assertEqual([], client._calls)
2218
class TestRepositoryInsertStream(TestRemoteRepository):
2220
def test_unlocked_repo(self):
2221
transport_path = 'quack'
2222
repo, client = self.setup_fake_client_and_repository(transport_path)
2223
client.add_expected_call(
2224
'Repository.insert_stream', ('quack/', ''),
2226
client.add_expected_call(
2227
'Repository.insert_stream', ('quack/', ''),
2229
sink = repo._get_sink()
2230
fmt = repository.RepositoryFormat.get_default_format()
2231
resume_tokens, missing_keys = sink.insert_stream([], fmt, [])
2232
self.assertEqual([], resume_tokens)
2233
self.assertEqual(set(), missing_keys)
2234
client.finished_test()
2236
def test_locked_repo_with_no_lock_token(self):
2237
transport_path = 'quack'
2238
repo, client = self.setup_fake_client_and_repository(transport_path)
2239
client.add_expected_call(
2240
'Repository.lock_write', ('quack/', ''),
2241
'success', ('ok', ''))
2242
client.add_expected_call(
2243
'Repository.insert_stream', ('quack/', ''),
2245
client.add_expected_call(
2246
'Repository.insert_stream', ('quack/', ''),
2249
sink = repo._get_sink()
2250
fmt = repository.RepositoryFormat.get_default_format()
2251
resume_tokens, missing_keys = sink.insert_stream([], fmt, [])
2252
self.assertEqual([], resume_tokens)
2253
self.assertEqual(set(), missing_keys)
2254
client.finished_test()
2256
def test_locked_repo_with_lock_token(self):
2257
transport_path = 'quack'
2258
repo, client = self.setup_fake_client_and_repository(transport_path)
2259
client.add_expected_call(
2260
'Repository.lock_write', ('quack/', ''),
2261
'success', ('ok', 'a token'))
2262
client.add_expected_call(
2263
'Repository.insert_stream_locked', ('quack/', '', 'a token'),
2265
client.add_expected_call(
2266
'Repository.insert_stream_locked', ('quack/', '', 'a token'),
2269
sink = repo._get_sink()
2270
fmt = repository.RepositoryFormat.get_default_format()
2271
resume_tokens, missing_keys = sink.insert_stream([], fmt, [])
2272
self.assertEqual([], resume_tokens)
2273
self.assertEqual(set(), missing_keys)
2274
client.finished_test()
1433
2277
class TestRepositoryTarball(TestRemoteRepository):
1435
2279
# This is a canned tarball reponse we can validate against
1793
2641
remote_repo.unlock()
1795
2643
def prepare_stacked_remote_branch(self):
1796
smart_server = server.SmartTCPServer_for_testing()
1797
smart_server.setUp()
1798
self.addCleanup(smart_server.tearDown)
1799
tree1 = self.make_branch_and_tree('tree1')
2644
"""Get stacked_upon and stacked branches with content in each."""
2645
self.setup_smart_server_with_call_log()
2646
tree1 = self.make_branch_and_tree('tree1', format='1.9')
1800
2647
tree1.commit('rev1', rev_id='rev1')
1801
tree2 = self.make_branch_and_tree('tree2', format='1.6')
1802
tree2.branch.set_stacked_on_url(tree1.branch.base)
1803
branch2 = Branch.open(smart_server.get_url() + '/tree2')
2648
tree2 = tree1.branch.bzrdir.sprout('tree2', stacked=True
2649
).open_workingtree()
2650
tree2.commit('local changes make me feel good.')
2651
branch2 = Branch.open(self.get_url('tree2'))
1804
2652
branch2.lock_read()
1805
2653
self.addCleanup(branch2.unlock)
2654
return tree1.branch, branch2
1808
2656
def test_stacked_get_parent_map(self):
1809
2657
# the public implementation of get_parent_map obeys stacking
1810
branch = self.prepare_stacked_remote_branch()
2658
_, branch = self.prepare_stacked_remote_branch()
1811
2659
repo = branch.repository
1812
2660
self.assertEqual(['rev1'], repo.get_parent_map(['rev1']).keys())
1814
2662
def test_unstacked_get_parent_map(self):
1815
2663
# _unstacked_provider.get_parent_map ignores stacking
1816
branch = self.prepare_stacked_remote_branch()
2664
_, branch = self.prepare_stacked_remote_branch()
1817
2665
provider = branch.repository._unstacked_provider
1818
2666
self.assertEqual([], provider.get_parent_map(['rev1']).keys())
2668
def fetch_stream_to_rev_order(self, stream):
2670
for kind, substream in stream:
2671
if not kind == 'revisions':
2674
for content in substream:
2675
result.append(content.key[-1])
2678
def get_ordered_revs(self, format, order):
2679
"""Get a list of the revisions in a stream to format format.
2681
:param format: The format of the target.
2682
:param order: the order that target should have requested.
2683
:result: The revision ids in the stream, in the order seen,
2684
the topological order of revisions in the source.
2686
unordered_format = bzrdir.format_registry.get(format)()
2687
target_repository_format = unordered_format.repository_format
2689
self.assertEqual(order, target_repository_format._fetch_order)
2690
trunk, stacked = self.prepare_stacked_remote_branch()
2691
source = stacked.repository._get_source(target_repository_format)
2692
tip = stacked.last_revision()
2693
revs = stacked.repository.get_ancestry(tip)
2694
search = graph.PendingAncestryResult([tip], stacked.repository)
2695
self.reset_smart_call_log()
2696
stream = source.get_stream(search)
2699
# We trust that if a revision is in the stream the rest of the new
2700
# content for it is too, as per our main fetch tests; here we are
2701
# checking that the revisions are actually included at all, and their
2703
return self.fetch_stream_to_rev_order(stream), revs
2705
def test_stacked_get_stream_unordered(self):
2706
# Repository._get_source.get_stream() from a stacked repository with
2707
# unordered yields the full data from both stacked and stacked upon
2709
rev_ord, expected_revs = self.get_ordered_revs('1.9', 'unordered')
2710
self.assertEqual(set(expected_revs), set(rev_ord))
2711
# Getting unordered results should have made a streaming data request
2712
# from the server, then one from the backing branch.
2713
self.assertLength(2, self.hpss_calls)
2715
def test_stacked_get_stream_topological(self):
2716
# Repository._get_source.get_stream() from a stacked repository with
2717
# topological sorting yields the full data from both stacked and
2718
# stacked upon sources in topological order.
2719
rev_ord, expected_revs = self.get_ordered_revs('knit', 'topological')
2720
self.assertEqual(expected_revs, rev_ord)
2721
# Getting topological sort requires VFS calls still
2722
self.assertLength(12, self.hpss_calls)
2724
def test_stacked_get_stream_groupcompress(self):
2725
# Repository._get_source.get_stream() from a stacked repository with
2726
# groupcompress sorting yields the full data from both stacked and
2727
# stacked upon sources in groupcompress order.
2728
raise tests.TestSkipped('No groupcompress ordered format available')
2729
rev_ord, expected_revs = self.get_ordered_revs('dev5', 'groupcompress')
2730
self.assertEqual(expected_revs, reversed(rev_ord))
2731
# Getting unordered results should have made a streaming data request
2732
# from the backing branch, and one from the stacked on branch.
2733
self.assertLength(2, self.hpss_calls)
2735
def test_stacked_pull_more_than_stacking_has_bug_360791(self):
2736
# When pulling some fixed amount of content that is more than the
2737
# source has (because some is coming from a fallback branch, no error
2738
# should be received. This was reported as bug 360791.
2739
# Need three branches: a trunk, a stacked branch, and a preexisting
2740
# branch pulling content from stacked and trunk.
2741
self.setup_smart_server_with_call_log()
2742
trunk = self.make_branch_and_tree('trunk', format="1.9-rich-root")
2743
r1 = trunk.commit('start')
2744
stacked_branch = trunk.branch.create_clone_on_transport(
2745
self.get_transport('stacked'), stacked_on=trunk.branch.base)
2746
local = self.make_branch('local', format='1.9-rich-root')
2747
local.repository.fetch(stacked_branch.repository,
2748
stacked_branch.last_revision())
1821
2751
class TestRemoteBranchEffort(tests.TestCaseWithTransport):