134
113
b = BzrDir.open_from_transport(self.transport).open_branch()
135
114
self.assertStartsWith(str(b), 'RemoteBranch(')
137
def test_remote_bzrdir_repr(self):
138
b = BzrDir.open_from_transport(self.transport)
139
self.assertStartsWith(str(b), 'RemoteBzrDir(')
141
def test_remote_branch_format_supports_stacking(self):
143
self.make_branch('unstackable', format='pack-0.92')
144
b = BzrDir.open_from_transport(t.clone('unstackable')).open_branch()
145
self.assertFalse(b._format.supports_stacking())
146
self.make_branch('stackable', format='1.9')
147
b = BzrDir.open_from_transport(t.clone('stackable')).open_branch()
148
self.assertTrue(b._format.supports_stacking())
150
def test_remote_repo_format_supports_external_references(self):
152
bd = self.make_bzrdir('unstackable', format='pack-0.92')
153
r = bd.create_repository()
154
self.assertFalse(r._format.supports_external_lookups)
155
r = BzrDir.open_from_transport(t.clone('unstackable')).open_repository()
156
self.assertFalse(r._format.supports_external_lookups)
157
bd = self.make_bzrdir('stackable', format='1.9')
158
r = bd.create_repository()
159
self.assertTrue(r._format.supports_external_lookups)
160
r = BzrDir.open_from_transport(t.clone('stackable')).open_repository()
161
self.assertTrue(r._format.supports_external_lookups)
163
def test_remote_branch_set_append_revisions_only(self):
164
# Make a format 1.9 branch, which supports append_revisions_only
165
branch = self.make_branch('branch', format='1.9')
166
config = branch.get_config()
167
branch.set_append_revisions_only(True)
169
'True', config.get_user_option('append_revisions_only'))
170
branch.set_append_revisions_only(False)
172
'False', config.get_user_option('append_revisions_only'))
174
def test_remote_branch_set_append_revisions_only_upgrade_reqd(self):
175
branch = self.make_branch('branch', format='knit')
176
config = branch.get_config()
178
errors.UpgradeRequired, branch.set_append_revisions_only, True)
181
117
class FakeProtocol(object):
182
118
"""Lookalike SmartClientRequestProtocolOne allowing body reading tests."""
422
345
AssertionError, client_medium._remember_remote_is_before, (1, 9))
425
class TestBzrDirCloningMetaDir(TestRemote):
427
def test_backwards_compat(self):
428
self.setup_smart_server_with_call_log()
429
a_dir = self.make_bzrdir('.')
430
self.reset_smart_call_log()
431
verb = 'BzrDir.cloning_metadir'
432
self.disable_verb(verb)
433
format = a_dir.cloning_metadir()
434
call_count = len([call for call in self.hpss_calls if
435
call.call.method == verb])
436
self.assertEqual(1, call_count)
438
def test_branch_reference(self):
439
transport = self.get_transport('quack')
440
referenced = self.make_branch('referenced')
441
expected = referenced.bzrdir.cloning_metadir()
442
client = FakeClient(transport.base)
443
client.add_expected_call(
444
'BzrDir.cloning_metadir', ('quack/', 'False'),
445
'error', ('BranchReference',)),
446
client.add_expected_call(
447
'BzrDir.open_branchV3', ('quack/',),
448
'success', ('ref', self.get_url('referenced'))),
449
a_bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
451
result = a_bzrdir.cloning_metadir()
452
# We should have got a control dir matching the referenced branch.
453
self.assertEqual(bzrdir.BzrDirMetaFormat1, type(result))
454
self.assertEqual(expected._repository_format, result._repository_format)
455
self.assertEqual(expected._branch_format, result._branch_format)
456
self.assertFinished(client)
458
def test_current_server(self):
459
transport = self.get_transport('.')
460
transport = transport.clone('quack')
461
self.make_bzrdir('quack')
462
client = FakeClient(transport.base)
463
reference_bzrdir_format = bzrdir.format_registry.get('default')()
464
control_name = reference_bzrdir_format.network_name()
465
client.add_expected_call(
466
'BzrDir.cloning_metadir', ('quack/', 'False'),
467
'success', (control_name, '', ('branch', ''))),
468
a_bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
470
result = a_bzrdir.cloning_metadir()
471
# We should have got a reference control dir with default branch and
472
# repository formats.
473
# This pokes a little, just to be sure.
474
self.assertEqual(bzrdir.BzrDirMetaFormat1, type(result))
475
self.assertEqual(None, result._repository_format)
476
self.assertEqual(None, result._branch_format)
477
self.assertFinished(client)
480
class TestBzrDirOpen(TestRemote):
482
def make_fake_client_and_transport(self, path='quack'):
483
transport = MemoryTransport()
484
transport.mkdir(path)
485
transport = transport.clone(path)
486
client = FakeClient(transport.base)
487
return client, transport
489
def test_absent(self):
490
client, transport = self.make_fake_client_and_transport()
491
client.add_expected_call(
492
'BzrDir.open_2.1', ('quack/',), 'success', ('no',))
493
self.assertRaises(errors.NotBranchError, RemoteBzrDir, transport,
494
remote.RemoteBzrDirFormat(), _client=client, _force_probe=True)
495
self.assertFinished(client)
497
def test_present_without_workingtree(self):
498
client, transport = self.make_fake_client_and_transport()
499
client.add_expected_call(
500
'BzrDir.open_2.1', ('quack/',), 'success', ('yes', 'no'))
501
bd = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
502
_client=client, _force_probe=True)
503
self.assertIsInstance(bd, RemoteBzrDir)
504
self.assertFalse(bd.has_workingtree())
505
self.assertRaises(errors.NoWorkingTree, bd.open_workingtree)
506
self.assertFinished(client)
508
def test_present_with_workingtree(self):
509
client, transport = self.make_fake_client_and_transport()
510
client.add_expected_call(
511
'BzrDir.open_2.1', ('quack/',), 'success', ('yes', 'yes'))
512
bd = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
513
_client=client, _force_probe=True)
514
self.assertIsInstance(bd, RemoteBzrDir)
515
self.assertTrue(bd.has_workingtree())
516
self.assertRaises(errors.NotLocalUrl, bd.open_workingtree)
517
self.assertFinished(client)
519
def test_backwards_compat(self):
520
client, transport = self.make_fake_client_and_transport()
521
client.add_expected_call(
522
'BzrDir.open_2.1', ('quack/',), 'unknown', ('BzrDir.open_2.1',))
523
client.add_expected_call(
524
'BzrDir.open', ('quack/',), 'success', ('yes',))
525
bd = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
526
_client=client, _force_probe=True)
527
self.assertIsInstance(bd, RemoteBzrDir)
528
self.assertFinished(client)
531
class TestBzrDirOpenBranch(TestRemote):
533
def test_backwards_compat(self):
534
self.setup_smart_server_with_call_log()
535
self.make_branch('.')
536
a_dir = BzrDir.open(self.get_url('.'))
537
self.reset_smart_call_log()
538
verb = 'BzrDir.open_branchV3'
539
self.disable_verb(verb)
540
format = a_dir.open_branch()
541
call_count = len([call for call in self.hpss_calls if
542
call.call.method == verb])
543
self.assertEqual(1, call_count)
348
class TestBzrDirOpenBranch(tests.TestCase):
545
350
def test_branch_present(self):
546
reference_format = self.get_repo_format()
547
network_name = reference_format.network_name()
548
branch_network_name = self.get_branch_format().network_name()
549
351
transport = MemoryTransport()
550
352
transport.mkdir('quack')
551
353
transport = transport.clone('quack')
552
354
client = FakeClient(transport.base)
553
355
client.add_expected_call(
554
'BzrDir.open_branchV3', ('quack/',),
555
'success', ('branch', branch_network_name))
356
'BzrDir.open_branch', ('quack/',),
357
'success', ('ok', ''))
556
358
client.add_expected_call(
557
'BzrDir.find_repositoryV3', ('quack/',),
558
'success', ('ok', '', 'no', 'no', 'no', network_name))
359
'BzrDir.find_repositoryV2', ('quack/',),
360
'success', ('ok', '', 'no', 'no', 'no'))
559
361
client.add_expected_call(
560
362
'Branch.get_stacked_on_url', ('quack/',),
561
363
'error', ('NotStacked',))
602
404
# transmitted as "~", not "%7E".
603
405
transport = RemoteTCPTransport('bzr://localhost/~hello/')
604
406
client = FakeClient(transport.base)
605
reference_format = self.get_repo_format()
606
network_name = reference_format.network_name()
607
branch_network_name = self.get_branch_format().network_name()
608
client.add_expected_call(
609
'BzrDir.open_branchV3', ('~hello/',),
610
'success', ('branch', branch_network_name))
611
client.add_expected_call(
612
'BzrDir.find_repositoryV3', ('~hello/',),
613
'success', ('ok', '', 'no', 'no', 'no', network_name))
407
client.add_expected_call(
408
'BzrDir.open_branch', ('~hello/',),
409
'success', ('ok', ''))
410
client.add_expected_call(
411
'BzrDir.find_repositoryV2', ('~hello/',),
412
'success', ('ok', '', 'no', 'no', 'no'))
614
413
client.add_expected_call(
615
414
'Branch.get_stacked_on_url', ('~hello/',),
616
415
'error', ('NotStacked',))
617
416
bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
619
418
result = bzrdir.open_branch()
620
self.assertFinished(client)
419
client.finished_test()
622
421
def check_open_repository(self, rich_root, subtrees, external_lookup='no'):
623
reference_format = self.get_repo_format()
624
network_name = reference_format.network_name()
625
422
transport = MemoryTransport()
626
423
transport.mkdir('quack')
627
424
transport = transport.clone('quack')
731
490
self.assertIsInstance(repo, remote.RemoteRepository)
732
491
# its format should have the settings from the response
733
492
format = repo._format
734
self.assertTrue(format.rich_root_data)
735
self.assertTrue(format.supports_tree_reference)
736
self.assertTrue(format.supports_external_lookups)
493
self.assertFalse(format.rich_root_data)
494
self.assertFalse(format.supports_tree_reference)
495
self.assertFalse(format.supports_external_lookups)
737
496
self.assertEqual(network_name, format.network_name())
740
class TestBzrDirOpenRepository(TestRemote):
499
class TestBzrDirOpenRepository(tests.TestCase):
742
def test_backwards_compat_1_2_3(self):
743
# fallback all the way to the first version.
744
reference_format = self.get_repo_format()
745
network_name = reference_format.network_name()
746
server_url = 'bzr://example.com/'
747
self.permit_url(server_url)
748
client = FakeClient(server_url)
749
client.add_unknown_method_response('BzrDir.find_repositoryV3')
501
def test_backwards_compat_1_2(self):
502
transport = MemoryTransport()
503
transport.mkdir('quack')
504
transport = transport.clone('quack')
505
client = FakeClient(transport.base)
750
506
client.add_unknown_method_response('BzrDir.find_repositoryV2')
751
507
client.add_success_response('ok', '', 'no', 'no')
752
# A real repository instance will be created to determine the network
754
client.add_success_response_with_body(
755
"Bazaar-NG meta directory, format 1\n", 'ok')
756
client.add_success_response_with_body(
757
reference_format.get_format_string(), 'ok')
758
# PackRepository wants to do a stat
759
client.add_success_response('stat', '0', '65535')
760
remote_transport = RemoteTransport(server_url + 'quack/', medium=False,
762
bzrdir = RemoteBzrDir(remote_transport, remote.RemoteBzrDirFormat(),
764
repo = bzrdir.open_repository()
766
[('call', 'BzrDir.find_repositoryV3', ('quack/',)),
767
('call', 'BzrDir.find_repositoryV2', ('quack/',)),
768
('call', 'BzrDir.find_repository', ('quack/',)),
769
('call_expecting_body', 'get', ('/quack/.bzr/branch-format',)),
770
('call_expecting_body', 'get', ('/quack/.bzr/repository/format',)),
771
('call', 'stat', ('/quack/.bzr/repository',)),
774
self.assertEqual(network_name, repo._format.network_name())
776
def test_backwards_compat_2(self):
777
# fallback to find_repositoryV2
778
reference_format = self.get_repo_format()
779
network_name = reference_format.network_name()
780
server_url = 'bzr://example.com/'
781
self.permit_url(server_url)
782
client = FakeClient(server_url)
783
client.add_unknown_method_response('BzrDir.find_repositoryV3')
784
client.add_success_response('ok', '', 'no', 'no', 'no')
785
# A real repository instance will be created to determine the network
787
client.add_success_response_with_body(
788
"Bazaar-NG meta directory, format 1\n", 'ok')
789
client.add_success_response_with_body(
790
reference_format.get_format_string(), 'ok')
791
# PackRepository wants to do a stat
792
client.add_success_response('stat', '0', '65535')
793
remote_transport = RemoteTransport(server_url + 'quack/', medium=False,
795
bzrdir = RemoteBzrDir(remote_transport, remote.RemoteBzrDirFormat(),
797
repo = bzrdir.open_repository()
799
[('call', 'BzrDir.find_repositoryV3', ('quack/',)),
800
('call', 'BzrDir.find_repositoryV2', ('quack/',)),
801
('call_expecting_body', 'get', ('/quack/.bzr/branch-format',)),
802
('call_expecting_body', 'get', ('/quack/.bzr/repository/format',)),
803
('call', 'stat', ('/quack/.bzr/repository',)),
806
self.assertEqual(network_name, repo._format.network_name())
808
def test_current_server(self):
809
reference_format = self.get_repo_format()
810
network_name = reference_format.network_name()
811
transport = MemoryTransport()
812
transport.mkdir('quack')
813
transport = transport.clone('quack')
814
client = FakeClient(transport.base)
815
client.add_success_response('ok', '', 'no', 'no', 'no', network_name)
816
508
bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
818
510
repo = bzrdir.open_repository()
819
511
self.assertEqual(
820
[('call', 'BzrDir.find_repositoryV3', ('quack/',))],
512
[('call', 'BzrDir.find_repositoryV2', ('quack/',)),
513
('call', 'BzrDir.find_repository', ('quack/',))],
822
self.assertEqual(network_name, repo._format.network_name())
825
class TestBzrDirFormatInitializeEx(TestRemote):
827
def test_success(self):
828
"""Simple test for typical successful call."""
829
fmt = bzrdir.RemoteBzrDirFormat()
830
default_format_name = BzrDirFormat.get_default_format().network_name()
831
transport = self.get_transport()
832
client = FakeClient(transport.base)
833
client.add_expected_call(
834
'BzrDirFormat.initialize_ex_1.16',
835
(default_format_name, 'path', 'False', 'False', 'False', '',
836
'', '', '', 'False'),
838
('.', 'no', 'no', 'yes', 'repo fmt', 'repo bzrdir fmt',
839
'bzrdir fmt', 'False', '', '', 'repo lock token'))
840
# XXX: It would be better to call fmt.initialize_on_transport_ex, but
841
# it's currently hard to test that without supplying a real remote
842
# transport connected to a real server.
843
result = fmt._initialize_on_transport_ex_rpc(client, 'path',
844
transport, False, False, False, None, None, None, None, False)
845
self.assertFinished(client)
847
def test_error(self):
848
"""Error responses are translated, e.g. 'PermissionDenied' raises the
849
corresponding error from the client.
851
fmt = bzrdir.RemoteBzrDirFormat()
852
default_format_name = BzrDirFormat.get_default_format().network_name()
853
transport = self.get_transport()
854
client = FakeClient(transport.base)
855
client.add_expected_call(
856
'BzrDirFormat.initialize_ex_1.16',
857
(default_format_name, 'path', 'False', 'False', 'False', '',
858
'', '', '', 'False'),
860
('PermissionDenied', 'path', 'extra info'))
861
# XXX: It would be better to call fmt.initialize_on_transport_ex, but
862
# it's currently hard to test that without supplying a real remote
863
# transport connected to a real server.
864
err = self.assertRaises(errors.PermissionDenied,
865
fmt._initialize_on_transport_ex_rpc, client, 'path', transport,
866
False, False, False, None, None, None, None, False)
867
self.assertEqual('path', err.path)
868
self.assertEqual(': extra info', err.extra)
869
self.assertFinished(client)
871
def test_error_from_real_server(self):
872
"""Integration test for error translation."""
873
transport = self.make_smart_server('foo')
874
transport = transport.clone('no-such-path')
875
fmt = bzrdir.RemoteBzrDirFormat()
876
err = self.assertRaises(errors.NoSuchFile,
877
fmt.initialize_on_transport_ex, transport, create_prefix=False)
880
517
class OldSmartClient(object):
934
553
# we do not want bzrdir to make any remote calls, so use False as its
935
554
# _client. If it tries to make a remote call, this will fail
937
bzrdir = self.make_remote_bzrdir(transport, False)
556
bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
938
558
repo = RemoteRepository(bzrdir, None, _client=client)
939
branch_format = self.get_branch_format()
940
format = RemoteBranchFormat(network_name=branch_format.network_name())
941
return RemoteBranch(bzrdir, repo, _client=client, format=format)
944
class TestBranchGetParent(RemoteBranchTestCase):
946
def test_no_parent(self):
947
# in an empty branch we decode the response properly
948
transport = MemoryTransport()
949
client = FakeClient(transport.base)
950
client.add_expected_call(
951
'Branch.get_stacked_on_url', ('quack/',),
952
'error', ('NotStacked',))
953
client.add_expected_call(
954
'Branch.get_parent', ('quack/',),
956
transport.mkdir('quack')
957
transport = transport.clone('quack')
958
branch = self.make_remote_branch(transport, client)
959
result = branch.get_parent()
960
self.assertFinished(client)
961
self.assertEqual(None, result)
963
def test_parent_relative(self):
964
transport = MemoryTransport()
965
client = FakeClient(transport.base)
966
client.add_expected_call(
967
'Branch.get_stacked_on_url', ('kwaak/',),
968
'error', ('NotStacked',))
969
client.add_expected_call(
970
'Branch.get_parent', ('kwaak/',),
971
'success', ('../foo/',))
972
transport.mkdir('kwaak')
973
transport = transport.clone('kwaak')
974
branch = self.make_remote_branch(transport, client)
975
result = branch.get_parent()
976
self.assertEqual(transport.clone('../foo').base, result)
978
def test_parent_absolute(self):
979
transport = MemoryTransport()
980
client = FakeClient(transport.base)
981
client.add_expected_call(
982
'Branch.get_stacked_on_url', ('kwaak/',),
983
'error', ('NotStacked',))
984
client.add_expected_call(
985
'Branch.get_parent', ('kwaak/',),
986
'success', ('http://foo/',))
987
transport.mkdir('kwaak')
988
transport = transport.clone('kwaak')
989
branch = self.make_remote_branch(transport, client)
990
result = branch.get_parent()
991
self.assertEqual('http://foo/', result)
992
self.assertFinished(client)
995
class TestBranchSetParentLocation(RemoteBranchTestCase):
997
def test_no_parent(self):
998
# We call the verb when setting parent to None
999
transport = MemoryTransport()
1000
client = FakeClient(transport.base)
1001
client.add_expected_call(
1002
'Branch.get_stacked_on_url', ('quack/',),
1003
'error', ('NotStacked',))
1004
client.add_expected_call(
1005
'Branch.set_parent_location', ('quack/', 'b', 'r', ''),
1007
transport.mkdir('quack')
1008
transport = transport.clone('quack')
1009
branch = self.make_remote_branch(transport, client)
1010
branch._lock_token = 'b'
1011
branch._repo_lock_token = 'r'
1012
branch._set_parent_location(None)
1013
self.assertFinished(client)
1015
def test_parent(self):
1016
transport = MemoryTransport()
1017
client = FakeClient(transport.base)
1018
client.add_expected_call(
1019
'Branch.get_stacked_on_url', ('kwaak/',),
1020
'error', ('NotStacked',))
1021
client.add_expected_call(
1022
'Branch.set_parent_location', ('kwaak/', 'b', 'r', 'foo'),
1024
transport.mkdir('kwaak')
1025
transport = transport.clone('kwaak')
1026
branch = self.make_remote_branch(transport, client)
1027
branch._lock_token = 'b'
1028
branch._repo_lock_token = 'r'
1029
branch._set_parent_location('foo')
1030
self.assertFinished(client)
1032
def test_backwards_compat(self):
1033
self.setup_smart_server_with_call_log()
1034
branch = self.make_branch('.')
1035
self.reset_smart_call_log()
1036
verb = 'Branch.set_parent_location'
1037
self.disable_verb(verb)
1038
branch.set_parent('http://foo/')
1039
self.assertLength(12, self.hpss_calls)
1042
class TestBranchGetTagsBytes(RemoteBranchTestCase):
1044
def test_backwards_compat(self):
1045
self.setup_smart_server_with_call_log()
1046
branch = self.make_branch('.')
1047
self.reset_smart_call_log()
1048
verb = 'Branch.get_tags_bytes'
1049
self.disable_verb(verb)
1050
branch.tags.get_tag_dict()
1051
call_count = len([call for call in self.hpss_calls if
1052
call.call.method == verb])
1053
self.assertEqual(1, call_count)
1055
def test_trivial(self):
1056
transport = MemoryTransport()
1057
client = FakeClient(transport.base)
1058
client.add_expected_call(
1059
'Branch.get_stacked_on_url', ('quack/',),
1060
'error', ('NotStacked',))
1061
client.add_expected_call(
1062
'Branch.get_tags_bytes', ('quack/',),
1064
transport.mkdir('quack')
1065
transport = transport.clone('quack')
1066
branch = self.make_remote_branch(transport, client)
1067
result = branch.tags.get_tag_dict()
1068
self.assertFinished(client)
1069
self.assertEqual({}, result)
1072
class TestBranchSetTagsBytes(RemoteBranchTestCase):
1074
def test_trivial(self):
1075
transport = MemoryTransport()
1076
client = FakeClient(transport.base)
1077
client.add_expected_call(
1078
'Branch.get_stacked_on_url', ('quack/',),
1079
'error', ('NotStacked',))
1080
client.add_expected_call(
1081
'Branch.set_tags_bytes', ('quack/', 'branch token', 'repo token'),
1083
transport.mkdir('quack')
1084
transport = transport.clone('quack')
1085
branch = self.make_remote_branch(transport, client)
1086
self.lock_remote_branch(branch)
1087
branch._set_tags_bytes('tags bytes')
1088
self.assertFinished(client)
1089
self.assertEqual('tags bytes', client._calls[-1][-1])
1091
def test_backwards_compatible(self):
1092
transport = MemoryTransport()
1093
client = FakeClient(transport.base)
1094
client.add_expected_call(
1095
'Branch.get_stacked_on_url', ('quack/',),
1096
'error', ('NotStacked',))
1097
client.add_expected_call(
1098
'Branch.set_tags_bytes', ('quack/', 'branch token', 'repo token'),
1099
'unknown', ('Branch.set_tags_bytes',))
1100
transport.mkdir('quack')
1101
transport = transport.clone('quack')
1102
branch = self.make_remote_branch(transport, client)
1103
self.lock_remote_branch(branch)
1104
class StubRealBranch(object):
1107
def _set_tags_bytes(self, bytes):
1108
self.calls.append(('set_tags_bytes', bytes))
1109
real_branch = StubRealBranch()
1110
branch._real_branch = real_branch
1111
branch._set_tags_bytes('tags bytes')
1112
# Call a second time, to exercise the 'remote version already inferred'
1114
branch._set_tags_bytes('tags bytes')
1115
self.assertFinished(client)
1117
[('set_tags_bytes', 'tags bytes')] * 2, real_branch.calls)
559
return RemoteBranch(bzrdir, repo, _client=client)
1120
562
class TestBranchLastRevisionInfo(RemoteBranchTestCase):
1571
1018
self.assertEqual('rejection message', err.msg)
1574
class TestBranchGetSetConfig(RemoteBranchTestCase):
1021
class TestBranchControlGetBranchConf(tests.TestCaseWithMemoryTransport):
1022
"""Getting the branch configuration should use an abstract method not vfs.
1576
1025
def test_get_branch_conf(self):
1577
# in an empty branch we decode the response properly
1578
client = FakeClient()
1579
client.add_expected_call(
1580
'Branch.get_stacked_on_url', ('memory:///',),
1581
'error', ('NotStacked',),)
1582
client.add_success_response_with_body('# config file body', 'ok')
1583
transport = MemoryTransport()
1584
branch = self.make_remote_branch(transport, client)
1585
config = branch.get_config()
1586
config.has_explicit_nickname()
1588
[('call', 'Branch.get_stacked_on_url', ('memory:///',)),
1589
('call_expecting_body', 'Branch.get_config_file', ('memory:///',))],
1592
def test_get_multi_line_branch_conf(self):
1593
# Make sure that multiple-line branch.conf files are supported
1595
# https://bugs.edge.launchpad.net/bzr/+bug/354075
1596
client = FakeClient()
1597
client.add_expected_call(
1598
'Branch.get_stacked_on_url', ('memory:///',),
1599
'error', ('NotStacked',),)
1600
client.add_success_response_with_body('a = 1\nb = 2\nc = 3\n', 'ok')
1601
transport = MemoryTransport()
1602
branch = self.make_remote_branch(transport, client)
1603
config = branch.get_config()
1604
self.assertEqual(u'2', config.get_user_option('b'))
1606
def test_set_option(self):
1607
client = FakeClient()
1608
client.add_expected_call(
1609
'Branch.get_stacked_on_url', ('memory:///',),
1610
'error', ('NotStacked',),)
1611
client.add_expected_call(
1612
'Branch.lock_write', ('memory:///', '', ''),
1613
'success', ('ok', 'branch token', 'repo token'))
1614
client.add_expected_call(
1615
'Branch.set_config_option', ('memory:///', 'branch token',
1616
'repo token', 'foo', 'bar', ''),
1618
client.add_expected_call(
1619
'Branch.unlock', ('memory:///', 'branch token', 'repo token'),
1621
transport = MemoryTransport()
1622
branch = self.make_remote_branch(transport, client)
1624
config = branch._get_config()
1625
config.set_option('foo', 'bar')
1627
self.assertFinished(client)
1629
def test_backwards_compat_set_option(self):
1630
self.setup_smart_server_with_call_log()
1631
branch = self.make_branch('.')
1632
verb = 'Branch.set_config_option'
1633
self.disable_verb(verb)
1635
self.addCleanup(branch.unlock)
1636
self.reset_smart_call_log()
1637
branch._get_config().set_option('value', 'name')
1638
self.assertLength(10, self.hpss_calls)
1639
self.assertEqual('value', branch._get_config().get_option('name'))
1026
raise tests.KnownFailure('branch.conf is not retrieved by get_config_file')
1027
## # We should see that branch.get_config() does a single rpc to get the
1028
## # remote configuration file, abstracting away where that is stored on
1029
## # the server. However at the moment it always falls back to using the
1030
## # vfs, and this would need some changes in config.py.
1032
## # in an empty branch we decode the response properly
1033
## client = FakeClient([(('ok', ), '# config file body')], self.get_url())
1034
## # we need to make a real branch because the remote_branch.control_files
1035
## # will trigger _ensure_real.
1036
## branch = self.make_branch('quack')
1037
## transport = branch.bzrdir.root_transport
1038
## # we do not want bzrdir to make any remote calls
1039
## bzrdir = RemoteBzrDir(transport, _client=False)
1040
## branch = RemoteBranch(bzrdir, None, _client=client)
1041
## config = branch.get_config()
1042
## self.assertEqual(
1043
## [('call_expecting_body', 'Branch.get_config_file', ('quack/',))],
1642
1047
class TestBranchLockWrite(RemoteBranchTestCase):
1923
1261
self.assertEqual({r1: (NULL_REVISION,)}, parents)
1924
1262
self.assertEqual(
1925
1263
[('call_with_body_bytes_expecting_body',
1926
'Repository.get_parent_map', ('quack/', 'include-missing:', r2),
1264
'Repository.get_parent_map', ('quack/', r2), '\n\n0'),
1928
1265
('call_with_body_bytes_expecting_body',
1929
'Repository.get_parent_map', ('quack/', 'include-missing:', r1),
1266
'Repository.get_parent_map', ('quack/', r1), '\n\n0'),
1935
1271
def test_get_parent_map_reconnects_if_unknown_method(self):
1936
1272
transport_path = 'quack'
1937
rev_id = 'revision-id'
1938
1273
repo, client = self.setup_fake_client_and_repository(transport_path)
1939
client.add_unknown_method_response('Repository.get_parent_map')
1940
client.add_success_response_with_body(rev_id, 'ok')
1274
client.add_unknown_method_response('Repository,get_parent_map')
1275
client.add_success_response_with_body('', 'ok')
1941
1276
self.assertFalse(client._medium._is_remote_before((1, 2)))
1942
parents = repo.get_parent_map([rev_id])
1277
rev_id = 'revision-id'
1278
expected_deprecations = [
1279
'bzrlib.remote.RemoteRepository.get_revision_graph was deprecated '
1281
parents = self.callDeprecated(
1282
expected_deprecations, repo.get_parent_map, [rev_id])
1943
1283
self.assertEqual(
1944
1284
[('call_with_body_bytes_expecting_body',
1945
'Repository.get_parent_map', ('quack/', 'include-missing:',
1285
'Repository.get_parent_map', ('quack/', rev_id), '\n\n0'),
1947
1286
('disconnect medium',),
1948
1287
('call_expecting_body', 'Repository.get_revision_graph',
1949
1288
('quack/', ''))],
1951
1290
# The medium is now marked as being connected to an older server
1952
1291
self.assertTrue(client._medium._is_remote_before((1, 2)))
1953
self.assertEqual({rev_id: ('null:',)}, parents)
1955
1293
def test_get_parent_map_fallback_parentless_node(self):
1956
1294
"""get_parent_map falls back to get_revision_graph on old servers. The
1982
1324
errors.UnexpectedSmartServerResponse,
1983
1325
repo.get_parent_map, ['a-revision-id'])
1985
def test_get_parent_map_negative_caches_missing_keys(self):
1986
self.setup_smart_server_with_call_log()
1987
repo = self.make_repository('foo')
1988
self.assertIsInstance(repo, RemoteRepository)
1990
self.addCleanup(repo.unlock)
1991
self.reset_smart_call_log()
1992
graph = repo.get_graph()
1993
self.assertEqual({},
1994
graph.get_parent_map(['some-missing', 'other-missing']))
1995
self.assertLength(1, self.hpss_calls)
1996
# No call if we repeat this
1997
self.reset_smart_call_log()
1998
graph = repo.get_graph()
1999
self.assertEqual({},
2000
graph.get_parent_map(['some-missing', 'other-missing']))
2001
self.assertLength(0, self.hpss_calls)
2002
# Asking for more unknown keys makes a request.
2003
self.reset_smart_call_log()
2004
graph = repo.get_graph()
2005
self.assertEqual({},
2006
graph.get_parent_map(['some-missing', 'other-missing',
2008
self.assertLength(1, self.hpss_calls)
2010
def disableExtraResults(self):
2011
self.overrideAttr(SmartServerRepositoryGetParentMap,
2012
'no_extra_results', True)
2014
def test_null_cached_missing_and_stop_key(self):
2015
self.setup_smart_server_with_call_log()
2016
# Make a branch with a single revision.
2017
builder = self.make_branch_builder('foo')
2018
builder.start_series()
2019
builder.build_snapshot('first', None, [
2020
('add', ('', 'root-id', 'directory', ''))])
2021
builder.finish_series()
2022
branch = builder.get_branch()
2023
repo = branch.repository
2024
self.assertIsInstance(repo, RemoteRepository)
2025
# Stop the server from sending extra results.
2026
self.disableExtraResults()
2028
self.addCleanup(repo.unlock)
2029
self.reset_smart_call_log()
2030
graph = repo.get_graph()
2031
# Query for 'first' and 'null:'. Because 'null:' is a parent of
2032
# 'first' it will be a candidate for the stop_keys of subsequent
2033
# requests, and because 'null:' was queried but not returned it will be
2034
# cached as missing.
2035
self.assertEqual({'first': ('null:',)},
2036
graph.get_parent_map(['first', 'null:']))
2037
# Now query for another key. This request will pass along a recipe of
2038
# start and stop keys describing the already cached results, and this
2039
# recipe's revision count must be correct (or else it will trigger an
2040
# error from the server).
2041
self.assertEqual({}, graph.get_parent_map(['another-key']))
2042
# This assertion guards against disableExtraResults silently failing to
2043
# work, thus invalidating the test.
2044
self.assertLength(2, self.hpss_calls)
2046
def test_get_parent_map_gets_ghosts_from_result(self):
2047
# asking for a revision should negatively cache close ghosts in its
2049
self.setup_smart_server_with_call_log()
2050
tree = self.make_branch_and_memory_tree('foo')
2053
builder = treebuilder.TreeBuilder()
2054
builder.start_tree(tree)
2056
builder.finish_tree()
2057
tree.set_parent_ids(['non-existant'], allow_leftmost_as_ghost=True)
2058
rev_id = tree.commit('')
2062
self.addCleanup(tree.unlock)
2063
repo = tree.branch.repository
2064
self.assertIsInstance(repo, RemoteRepository)
2066
repo.get_parent_map([rev_id])
2067
self.reset_smart_call_log()
2068
# Now asking for rev_id's ghost parent should not make calls
2069
self.assertEqual({}, repo.get_parent_map(['non-existant']))
2070
self.assertLength(0, self.hpss_calls)
2073
1328
class TestGetParentMapAllowsNew(tests.TestCaseWithTransport):
2075
1330
def test_allows_new_revisions(self):
2076
1331
"""get_parent_map's results can be updated by commit."""
2077
1332
smart_server = server.SmartTCPServer_for_testing()
2078
self.start_server(smart_server)
1333
smart_server.setUp()
1334
self.addCleanup(smart_server.tearDown)
2079
1335
self.make_branch('branch')
2080
1336
branch = Branch.open(smart_server.get_url() + '/branch')
2081
1337
tree = branch.create_checkout('tree', lightweight=True)
2160
1413
repo, client = self.setup_fake_client_and_repository(transport_path)
2161
1414
client.add_error_response('AnUnexpectedError')
2162
1415
e = self.assertRaises(errors.UnknownErrorFromSmartServer,
2163
repo._get_revision_graph, revid)
1416
self.applyDeprecated, one_four, repo.get_revision_graph, revid)
2164
1417
self.assertEqual(('AnUnexpectedError',), e.error_tuple)
2167
class TestRepositoryGetRevIdForRevno(TestRemoteRepository):
2170
repo, client = self.setup_fake_client_and_repository('quack')
2171
client.add_expected_call(
2172
'Repository.get_rev_id_for_revno', ('quack/', 5, (42, 'rev-foo')),
2173
'success', ('ok', 'rev-five'))
2174
result = repo.get_rev_id_for_revno(5, (42, 'rev-foo'))
2175
self.assertEqual((True, 'rev-five'), result)
2176
self.assertFinished(client)
2178
def test_history_incomplete(self):
2179
repo, client = self.setup_fake_client_and_repository('quack')
2180
client.add_expected_call(
2181
'Repository.get_rev_id_for_revno', ('quack/', 5, (42, 'rev-foo')),
2182
'success', ('history-incomplete', 10, 'rev-ten'))
2183
result = repo.get_rev_id_for_revno(5, (42, 'rev-foo'))
2184
self.assertEqual((False, (10, 'rev-ten')), result)
2185
self.assertFinished(client)
2187
def test_history_incomplete_with_fallback(self):
2188
"""A 'history-incomplete' response causes the fallback repository to be
2189
queried too, if one is set.
2191
# Make a repo with a fallback repo, both using a FakeClient.
2192
format = remote.response_tuple_to_repo_format(
2193
('yes', 'no', 'yes', 'fake-network-name'))
2194
repo, client = self.setup_fake_client_and_repository('quack')
2195
repo._format = format
2196
fallback_repo, ignored = self.setup_fake_client_and_repository(
2198
fallback_repo._client = client
2199
repo.add_fallback_repository(fallback_repo)
2200
# First the client should ask the primary repo
2201
client.add_expected_call(
2202
'Repository.get_rev_id_for_revno', ('quack/', 1, (42, 'rev-foo')),
2203
'success', ('history-incomplete', 2, 'rev-two'))
2204
# Then it should ask the fallback, using revno/revid from the
2205
# history-incomplete response as the known revno/revid.
2206
client.add_expected_call(
2207
'Repository.get_rev_id_for_revno',('fallback/', 1, (2, 'rev-two')),
2208
'success', ('ok', 'rev-one'))
2209
result = repo.get_rev_id_for_revno(1, (42, 'rev-foo'))
2210
self.assertEqual((True, 'rev-one'), result)
2211
self.assertFinished(client)
2213
def test_nosuchrevision(self):
2214
# 'nosuchrevision' is returned when the known-revid is not found in the
2215
# remote repo. The client translates that response to NoSuchRevision.
2216
repo, client = self.setup_fake_client_and_repository('quack')
2217
client.add_expected_call(
2218
'Repository.get_rev_id_for_revno', ('quack/', 5, (42, 'rev-foo')),
2219
'error', ('nosuchrevision', 'rev-foo'))
2221
errors.NoSuchRevision,
2222
repo.get_rev_id_for_revno, 5, (42, 'rev-foo'))
2223
self.assertFinished(client)
2225
def test_branch_fallback_locking(self):
2226
"""RemoteBranch.get_rev_id takes a read lock, and tries to call the
2227
get_rev_id_for_revno verb. If the verb is unknown the VFS fallback
2228
will be invoked, which will fail if the repo is unlocked.
2230
self.setup_smart_server_with_call_log()
2231
tree = self.make_branch_and_memory_tree('.')
2233
rev1 = tree.commit('First')
2234
rev2 = tree.commit('Second')
2236
branch = tree.branch
2237
self.assertFalse(branch.is_locked())
2238
self.reset_smart_call_log()
2239
verb = 'Repository.get_rev_id_for_revno'
2240
self.disable_verb(verb)
2241
self.assertEqual(rev1, branch.get_rev_id(1))
2242
self.assertLength(1, [call for call in self.hpss_calls if
2243
call.call.method == verb])
2246
1420
class TestRepositoryIsShared(TestRemoteRepository):
2248
1422
def test_is_shared(self):
2363
1537
self.assertEqual([], client._calls)
2366
class TestRepositoryInsertStreamBase(TestRemoteRepository):
2367
"""Base class for Repository.insert_stream and .insert_stream_1.19
2371
def checkInsertEmptyStream(self, repo, client):
2372
"""Insert an empty stream, checking the result.
2374
This checks that there are no resume_tokens or missing_keys, and that
2375
the client is finished.
2377
sink = repo._get_sink()
2378
fmt = repository.RepositoryFormat.get_default_format()
2379
resume_tokens, missing_keys = sink.insert_stream([], fmt, [])
2380
self.assertEqual([], resume_tokens)
2381
self.assertEqual(set(), missing_keys)
2382
self.assertFinished(client)
2385
class TestRepositoryInsertStream(TestRepositoryInsertStreamBase):
2386
"""Tests for using Repository.insert_stream verb when the _1.19 variant is
2389
This test case is very similar to TestRepositoryInsertStream_1_19.
2393
TestRemoteRepository.setUp(self)
2394
self.disable_verb('Repository.insert_stream_1.19')
2396
def test_unlocked_repo(self):
2397
transport_path = 'quack'
2398
repo, client = self.setup_fake_client_and_repository(transport_path)
2399
client.add_expected_call(
2400
'Repository.insert_stream_1.19', ('quack/', ''),
2401
'unknown', ('Repository.insert_stream_1.19',))
2402
client.add_expected_call(
2403
'Repository.insert_stream', ('quack/', ''),
2405
client.add_expected_call(
2406
'Repository.insert_stream', ('quack/', ''),
2408
self.checkInsertEmptyStream(repo, client)
2410
def test_locked_repo_with_no_lock_token(self):
2411
transport_path = 'quack'
2412
repo, client = self.setup_fake_client_and_repository(transport_path)
2413
client.add_expected_call(
2414
'Repository.lock_write', ('quack/', ''),
2415
'success', ('ok', ''))
2416
client.add_expected_call(
2417
'Repository.insert_stream_1.19', ('quack/', ''),
2418
'unknown', ('Repository.insert_stream_1.19',))
2419
client.add_expected_call(
2420
'Repository.insert_stream', ('quack/', ''),
2422
client.add_expected_call(
2423
'Repository.insert_stream', ('quack/', ''),
2426
self.checkInsertEmptyStream(repo, client)
2428
def test_locked_repo_with_lock_token(self):
2429
transport_path = 'quack'
2430
repo, client = self.setup_fake_client_and_repository(transport_path)
2431
client.add_expected_call(
2432
'Repository.lock_write', ('quack/', ''),
2433
'success', ('ok', 'a token'))
2434
client.add_expected_call(
2435
'Repository.insert_stream_1.19', ('quack/', '', 'a token'),
2436
'unknown', ('Repository.insert_stream_1.19',))
2437
client.add_expected_call(
2438
'Repository.insert_stream_locked', ('quack/', '', 'a token'),
2440
client.add_expected_call(
2441
'Repository.insert_stream_locked', ('quack/', '', 'a token'),
2444
self.checkInsertEmptyStream(repo, client)
2446
def test_stream_with_inventory_deltas(self):
2447
"""'inventory-deltas' substreams cannot be sent to the
2448
Repository.insert_stream verb, because not all servers that implement
2449
that verb will accept them. So when one is encountered the RemoteSink
2450
immediately stops using that verb and falls back to VFS insert_stream.
2452
transport_path = 'quack'
2453
repo, client = self.setup_fake_client_and_repository(transport_path)
2454
client.add_expected_call(
2455
'Repository.insert_stream_1.19', ('quack/', ''),
2456
'unknown', ('Repository.insert_stream_1.19',))
2457
client.add_expected_call(
2458
'Repository.insert_stream', ('quack/', ''),
2460
client.add_expected_call(
2461
'Repository.insert_stream', ('quack/', ''),
2463
# Create a fake real repository for insert_stream to fall back on, so
2464
# that we can directly see the records the RemoteSink passes to the
2469
def insert_stream(self, stream, src_format, resume_tokens):
2470
for substream_kind, substream in stream:
2471
self.records.append(
2472
(substream_kind, [record.key for record in substream]))
2473
return ['fake tokens'], ['fake missing keys']
2474
fake_real_sink = FakeRealSink()
2475
class FakeRealRepository:
2476
def _get_sink(self):
2477
return fake_real_sink
2478
def is_in_write_group(self):
2480
def refresh_data(self):
2482
repo._real_repository = FakeRealRepository()
2483
sink = repo._get_sink()
2484
fmt = repository.RepositoryFormat.get_default_format()
2485
stream = self.make_stream_with_inv_deltas(fmt)
2486
resume_tokens, missing_keys = sink.insert_stream(stream, fmt, [])
2487
# Every record from the first inventory delta should have been sent to
2489
expected_records = [
2490
('inventory-deltas', [('rev2',), ('rev3',)]),
2491
('texts', [('some-rev', 'some-file')])]
2492
self.assertEqual(expected_records, fake_real_sink.records)
2493
# The return values from the real sink's insert_stream are propagated
2494
# back to the original caller.
2495
self.assertEqual(['fake tokens'], resume_tokens)
2496
self.assertEqual(['fake missing keys'], missing_keys)
2497
self.assertFinished(client)
2499
def make_stream_with_inv_deltas(self, fmt):
2500
"""Make a simple stream with an inventory delta followed by more
2501
records and more substreams to test that all records and substreams
2502
from that point on are used.
2504
This sends, in order:
2505
* inventories substream: rev1, rev2, rev3. rev2 and rev3 are
2507
* texts substream: (some-rev, some-file)
2509
# Define a stream using generators so that it isn't rewindable.
2510
inv = inventory.Inventory(revision_id='rev1')
2511
inv.root.revision = 'rev1'
2512
def stream_with_inv_delta():
2513
yield ('inventories', inventories_substream())
2514
yield ('inventory-deltas', inventory_delta_substream())
2516
versionedfile.FulltextContentFactory(
2517
('some-rev', 'some-file'), (), None, 'content')])
2518
def inventories_substream():
2519
# An empty inventory fulltext. This will be streamed normally.
2520
text = fmt._serializer.write_inventory_to_string(inv)
2521
yield versionedfile.FulltextContentFactory(
2522
('rev1',), (), None, text)
2523
def inventory_delta_substream():
2524
# An inventory delta. This can't be streamed via this verb, so it
2525
# will trigger a fallback to VFS insert_stream.
2526
entry = inv.make_entry(
2527
'directory', 'newdir', inv.root.file_id, 'newdir-id')
2528
entry.revision = 'ghost'
2529
delta = [(None, 'newdir', 'newdir-id', entry)]
2530
serializer = inventory_delta.InventoryDeltaSerializer(
2531
versioned_root=True, tree_references=False)
2532
lines = serializer.delta_to_lines('rev1', 'rev2', delta)
2533
yield versionedfile.ChunkedContentFactory(
2534
('rev2',), (('rev1',)), None, lines)
2536
lines = serializer.delta_to_lines('rev1', 'rev3', delta)
2537
yield versionedfile.ChunkedContentFactory(
2538
('rev3',), (('rev1',)), None, lines)
2539
return stream_with_inv_delta()
2542
class TestRepositoryInsertStream_1_19(TestRepositoryInsertStreamBase):
2544
def test_unlocked_repo(self):
2545
transport_path = 'quack'
2546
repo, client = self.setup_fake_client_and_repository(transport_path)
2547
client.add_expected_call(
2548
'Repository.insert_stream_1.19', ('quack/', ''),
2550
client.add_expected_call(
2551
'Repository.insert_stream_1.19', ('quack/', ''),
2553
self.checkInsertEmptyStream(repo, client)
2555
def test_locked_repo_with_no_lock_token(self):
2556
transport_path = 'quack'
2557
repo, client = self.setup_fake_client_and_repository(transport_path)
2558
client.add_expected_call(
2559
'Repository.lock_write', ('quack/', ''),
2560
'success', ('ok', ''))
2561
client.add_expected_call(
2562
'Repository.insert_stream_1.19', ('quack/', ''),
2564
client.add_expected_call(
2565
'Repository.insert_stream_1.19', ('quack/', ''),
2568
self.checkInsertEmptyStream(repo, client)
2570
def test_locked_repo_with_lock_token(self):
2571
transport_path = 'quack'
2572
repo, client = self.setup_fake_client_and_repository(transport_path)
2573
client.add_expected_call(
2574
'Repository.lock_write', ('quack/', ''),
2575
'success', ('ok', 'a token'))
2576
client.add_expected_call(
2577
'Repository.insert_stream_1.19', ('quack/', '', 'a token'),
2579
client.add_expected_call(
2580
'Repository.insert_stream_1.19', ('quack/', '', 'a token'),
2583
self.checkInsertEmptyStream(repo, client)
2586
1540
class TestRepositoryTarball(TestRemoteRepository):
2588
1542
# This is a canned tarball reponse we can validate against
2964
1900
remote_repo.unlock()
2966
1902
def prepare_stacked_remote_branch(self):
2967
"""Get stacked_upon and stacked branches with content in each."""
2968
self.setup_smart_server_with_call_log()
2969
tree1 = self.make_branch_and_tree('tree1', format='1.9')
1903
smart_server = server.SmartTCPServer_for_testing()
1904
smart_server.setUp()
1905
self.addCleanup(smart_server.tearDown)
1906
tree1 = self.make_branch_and_tree('tree1')
2970
1907
tree1.commit('rev1', rev_id='rev1')
2971
tree2 = tree1.branch.bzrdir.sprout('tree2', stacked=True
2972
).open_workingtree()
2973
local_tree = tree2.branch.create_checkout('local')
2974
local_tree.commit('local changes make me feel good.')
2975
branch2 = Branch.open(self.get_url('tree2'))
1908
tree2 = self.make_branch_and_tree('tree2', format='1.6')
1909
tree2.branch.set_stacked_on_url(tree1.branch.base)
1910
branch2 = Branch.open(smart_server.get_url() + '/tree2')
2976
1911
branch2.lock_read()
2977
1912
self.addCleanup(branch2.unlock)
2978
return tree1.branch, branch2
2980
1915
def test_stacked_get_parent_map(self):
2981
1916
# the public implementation of get_parent_map obeys stacking
2982
_, branch = self.prepare_stacked_remote_branch()
1917
branch = self.prepare_stacked_remote_branch()
2983
1918
repo = branch.repository
2984
1919
self.assertEqual(['rev1'], repo.get_parent_map(['rev1']).keys())
2986
1921
def test_unstacked_get_parent_map(self):
2987
1922
# _unstacked_provider.get_parent_map ignores stacking
2988
_, branch = self.prepare_stacked_remote_branch()
1923
branch = self.prepare_stacked_remote_branch()
2989
1924
provider = branch.repository._unstacked_provider
2990
1925
self.assertEqual([], provider.get_parent_map(['rev1']).keys())
2992
def fetch_stream_to_rev_order(self, stream):
2994
for kind, substream in stream:
2995
if not kind == 'revisions':
2998
for content in substream:
2999
result.append(content.key[-1])
3002
def get_ordered_revs(self, format, order, branch_factory=None):
3003
"""Get a list of the revisions in a stream to format format.
3005
:param format: The format of the target.
3006
:param order: the order that target should have requested.
3007
:param branch_factory: A callable to create a trunk and stacked branch
3008
to fetch from. If none, self.prepare_stacked_remote_branch is used.
3009
:result: The revision ids in the stream, in the order seen,
3010
the topological order of revisions in the source.
3012
unordered_format = bzrdir.format_registry.get(format)()
3013
target_repository_format = unordered_format.repository_format
3015
self.assertEqual(order, target_repository_format._fetch_order)
3016
if branch_factory is None:
3017
branch_factory = self.prepare_stacked_remote_branch
3018
_, stacked = branch_factory()
3019
source = stacked.repository._get_source(target_repository_format)
3020
tip = stacked.last_revision()
3021
revs = stacked.repository.get_ancestry(tip)
3022
search = graph.PendingAncestryResult([tip], stacked.repository)
3023
self.reset_smart_call_log()
3024
stream = source.get_stream(search)
3027
# We trust that if a revision is in the stream the rest of the new
3028
# content for it is too, as per our main fetch tests; here we are
3029
# checking that the revisions are actually included at all, and their
3031
return self.fetch_stream_to_rev_order(stream), revs
3033
def test_stacked_get_stream_unordered(self):
3034
# Repository._get_source.get_stream() from a stacked repository with
3035
# unordered yields the full data from both stacked and stacked upon
3037
rev_ord, expected_revs = self.get_ordered_revs('1.9', 'unordered')
3038
self.assertEqual(set(expected_revs), set(rev_ord))
3039
# Getting unordered results should have made a streaming data request
3040
# from the server, then one from the backing branch.
3041
self.assertLength(2, self.hpss_calls)
3043
def test_stacked_on_stacked_get_stream_unordered(self):
3044
# Repository._get_source.get_stream() from a stacked repository which
3045
# is itself stacked yields the full data from all three sources.
3046
def make_stacked_stacked():
3047
_, stacked = self.prepare_stacked_remote_branch()
3048
tree = stacked.bzrdir.sprout('tree3', stacked=True
3049
).open_workingtree()
3050
local_tree = tree.branch.create_checkout('local-tree3')
3051
local_tree.commit('more local changes are better')
3052
branch = Branch.open(self.get_url('tree3'))
3054
self.addCleanup(branch.unlock)
3056
rev_ord, expected_revs = self.get_ordered_revs('1.9', 'unordered',
3057
branch_factory=make_stacked_stacked)
3058
self.assertEqual(set(expected_revs), set(rev_ord))
3059
# Getting unordered results should have made a streaming data request
3060
# from the server, and one from each backing repo
3061
self.assertLength(3, self.hpss_calls)
3063
def test_stacked_get_stream_topological(self):
3064
# Repository._get_source.get_stream() from a stacked repository with
3065
# topological sorting yields the full data from both stacked and
3066
# stacked upon sources in topological order.
3067
rev_ord, expected_revs = self.get_ordered_revs('knit', 'topological')
3068
self.assertEqual(expected_revs, rev_ord)
3069
# Getting topological sort requires VFS calls still - one of which is
3070
# pushing up from the bound branch.
3071
self.assertLength(13, self.hpss_calls)
3073
def test_stacked_get_stream_groupcompress(self):
3074
# Repository._get_source.get_stream() from a stacked repository with
3075
# groupcompress sorting yields the full data from both stacked and
3076
# stacked upon sources in groupcompress order.
3077
raise tests.TestSkipped('No groupcompress ordered format available')
3078
rev_ord, expected_revs = self.get_ordered_revs('dev5', 'groupcompress')
3079
self.assertEqual(expected_revs, reversed(rev_ord))
3080
# Getting unordered results should have made a streaming data request
3081
# from the backing branch, and one from the stacked on branch.
3082
self.assertLength(2, self.hpss_calls)
3084
def test_stacked_pull_more_than_stacking_has_bug_360791(self):
3085
# When pulling some fixed amount of content that is more than the
3086
# source has (because some is coming from a fallback branch, no error
3087
# should be received. This was reported as bug 360791.
3088
# Need three branches: a trunk, a stacked branch, and a preexisting
3089
# branch pulling content from stacked and trunk.
3090
self.setup_smart_server_with_call_log()
3091
trunk = self.make_branch_and_tree('trunk', format="1.9-rich-root")
3092
r1 = trunk.commit('start')
3093
stacked_branch = trunk.branch.create_clone_on_transport(
3094
self.get_transport('stacked'), stacked_on=trunk.branch.base)
3095
local = self.make_branch('local', format='1.9-rich-root')
3096
local.repository.fetch(stacked_branch.repository,
3097
stacked_branch.last_revision())
3100
1928
class TestRemoteBranchEffort(tests.TestCaseWithTransport):