135
113
b = BzrDir.open_from_transport(self.transport).open_branch()
136
114
self.assertStartsWith(str(b), 'RemoteBranch(')
138
def test_remote_bzrdir_repr(self):
139
b = BzrDir.open_from_transport(self.transport)
140
self.assertStartsWith(str(b), 'RemoteBzrDir(')
142
def test_remote_branch_format_supports_stacking(self):
144
self.make_branch('unstackable', format='pack-0.92')
145
b = BzrDir.open_from_transport(t.clone('unstackable')).open_branch()
146
self.assertFalse(b._format.supports_stacking())
147
self.make_branch('stackable', format='1.9')
148
b = BzrDir.open_from_transport(t.clone('stackable')).open_branch()
149
self.assertTrue(b._format.supports_stacking())
151
def test_remote_repo_format_supports_external_references(self):
153
bd = self.make_bzrdir('unstackable', format='pack-0.92')
154
r = bd.create_repository()
155
self.assertFalse(r._format.supports_external_lookups)
156
r = BzrDir.open_from_transport(t.clone('unstackable')).open_repository()
157
self.assertFalse(r._format.supports_external_lookups)
158
bd = self.make_bzrdir('stackable', format='1.9')
159
r = bd.create_repository()
160
self.assertTrue(r._format.supports_external_lookups)
161
r = BzrDir.open_from_transport(t.clone('stackable')).open_repository()
162
self.assertTrue(r._format.supports_external_lookups)
164
def test_remote_branch_set_append_revisions_only(self):
165
# Make a format 1.9 branch, which supports append_revisions_only
166
branch = self.make_branch('branch', format='1.9')
167
config = branch.get_config()
168
branch.set_append_revisions_only(True)
170
'True', config.get_user_option('append_revisions_only'))
171
branch.set_append_revisions_only(False)
173
'False', config.get_user_option('append_revisions_only'))
175
def test_remote_branch_set_append_revisions_only_upgrade_reqd(self):
176
branch = self.make_branch('branch', format='knit')
177
config = branch.get_config()
179
errors.UpgradeRequired, branch.set_append_revisions_only, True)
182
117
class FakeProtocol(object):
183
118
"""Lookalike SmartClientRequestProtocolOne allowing body reading tests."""
418
340
# Calling _remember_remote_is_before again with a lower value works.
419
341
client_medium._remember_remote_is_before((1, 5))
420
342
self.assertTrue(client_medium._is_remote_before((1, 5)))
421
# If you call _remember_remote_is_before with a higher value it logs a
422
# warning, and continues to remember the lower value.
423
self.assertNotContainsRe(self.get_log(), '_remember_remote_is_before')
424
client_medium._remember_remote_is_before((1, 9))
425
self.assertContainsRe(self.get_log(), '_remember_remote_is_before')
426
self.assertTrue(client_medium._is_remote_before((1, 5)))
429
class TestBzrDirCloningMetaDir(TestRemote):
431
def test_backwards_compat(self):
432
self.setup_smart_server_with_call_log()
433
a_dir = self.make_bzrdir('.')
434
self.reset_smart_call_log()
435
verb = 'BzrDir.cloning_metadir'
436
self.disable_verb(verb)
437
format = a_dir.cloning_metadir()
438
call_count = len([call for call in self.hpss_calls if
439
call.call.method == verb])
440
self.assertEqual(1, call_count)
442
def test_branch_reference(self):
443
transport = self.get_transport('quack')
444
referenced = self.make_branch('referenced')
445
expected = referenced.bzrdir.cloning_metadir()
446
client = FakeClient(transport.base)
447
client.add_expected_call(
448
'BzrDir.cloning_metadir', ('quack/', 'False'),
449
'error', ('BranchReference',)),
450
client.add_expected_call(
451
'BzrDir.open_branchV3', ('quack/',),
452
'success', ('ref', self.get_url('referenced'))),
453
a_bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
455
result = a_bzrdir.cloning_metadir()
456
# We should have got a control dir matching the referenced branch.
457
self.assertEqual(bzrdir.BzrDirMetaFormat1, type(result))
458
self.assertEqual(expected._repository_format, result._repository_format)
459
self.assertEqual(expected._branch_format, result._branch_format)
460
self.assertFinished(client)
462
def test_current_server(self):
463
transport = self.get_transport('.')
464
transport = transport.clone('quack')
465
self.make_bzrdir('quack')
466
client = FakeClient(transport.base)
467
reference_bzrdir_format = bzrdir.format_registry.get('default')()
468
control_name = reference_bzrdir_format.network_name()
469
client.add_expected_call(
470
'BzrDir.cloning_metadir', ('quack/', 'False'),
471
'success', (control_name, '', ('branch', ''))),
472
a_bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
474
result = a_bzrdir.cloning_metadir()
475
# We should have got a reference control dir with default branch and
476
# repository formats.
477
# This pokes a little, just to be sure.
478
self.assertEqual(bzrdir.BzrDirMetaFormat1, type(result))
479
self.assertEqual(None, result._repository_format)
480
self.assertEqual(None, result._branch_format)
481
self.assertFinished(client)
484
class TestBzrDirOpen(TestRemote):
486
def make_fake_client_and_transport(self, path='quack'):
487
transport = MemoryTransport()
488
transport.mkdir(path)
489
transport = transport.clone(path)
490
client = FakeClient(transport.base)
491
return client, transport
493
def test_absent(self):
494
client, transport = self.make_fake_client_and_transport()
495
client.add_expected_call(
496
'BzrDir.open_2.1', ('quack/',), 'success', ('no',))
497
self.assertRaises(errors.NotBranchError, RemoteBzrDir, transport,
498
remote.RemoteBzrDirFormat(), _client=client, _force_probe=True)
499
self.assertFinished(client)
501
def test_present_without_workingtree(self):
502
client, transport = self.make_fake_client_and_transport()
503
client.add_expected_call(
504
'BzrDir.open_2.1', ('quack/',), 'success', ('yes', 'no'))
505
bd = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
506
_client=client, _force_probe=True)
507
self.assertIsInstance(bd, RemoteBzrDir)
508
self.assertFalse(bd.has_workingtree())
509
self.assertRaises(errors.NoWorkingTree, bd.open_workingtree)
510
self.assertFinished(client)
512
def test_present_with_workingtree(self):
513
client, transport = self.make_fake_client_and_transport()
514
client.add_expected_call(
515
'BzrDir.open_2.1', ('quack/',), 'success', ('yes', 'yes'))
516
bd = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
517
_client=client, _force_probe=True)
518
self.assertIsInstance(bd, RemoteBzrDir)
519
self.assertTrue(bd.has_workingtree())
520
self.assertRaises(errors.NotLocalUrl, bd.open_workingtree)
521
self.assertFinished(client)
523
def test_backwards_compat(self):
524
client, transport = self.make_fake_client_and_transport()
525
client.add_expected_call(
526
'BzrDir.open_2.1', ('quack/',), 'unknown', ('BzrDir.open_2.1',))
527
client.add_expected_call(
528
'BzrDir.open', ('quack/',), 'success', ('yes',))
529
bd = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
530
_client=client, _force_probe=True)
531
self.assertIsInstance(bd, RemoteBzrDir)
532
self.assertFinished(client)
534
def test_backwards_compat_hpss_v2(self):
535
client, transport = self.make_fake_client_and_transport()
536
# Monkey-patch fake client to simulate real-world behaviour with v2
537
# server: upon first RPC call detect the protocol version, and because
538
# the version is 2 also do _remember_remote_is_before((1, 6)) before
539
# continuing with the RPC.
540
orig_check_call = client._check_call
541
def check_call(method, args):
542
client._medium._protocol_version = 2
543
client._medium._remember_remote_is_before((1, 6))
544
client._check_call = orig_check_call
545
client._check_call(method, args)
546
client._check_call = check_call
547
client.add_expected_call(
548
'BzrDir.open_2.1', ('quack/',), 'unknown', ('BzrDir.open_2.1',))
549
client.add_expected_call(
550
'BzrDir.open', ('quack/',), 'success', ('yes',))
551
bd = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
552
_client=client, _force_probe=True)
553
self.assertIsInstance(bd, RemoteBzrDir)
554
self.assertFinished(client)
557
class TestBzrDirOpenBranch(TestRemote):
559
def test_backwards_compat(self):
560
self.setup_smart_server_with_call_log()
561
self.make_branch('.')
562
a_dir = BzrDir.open(self.get_url('.'))
563
self.reset_smart_call_log()
564
verb = 'BzrDir.open_branchV3'
565
self.disable_verb(verb)
566
format = a_dir.open_branch()
567
call_count = len([call for call in self.hpss_calls if
568
call.call.method == verb])
569
self.assertEqual(1, call_count)
343
# You cannot call _remember_remote_is_before with a larger value.
345
AssertionError, client_medium._remember_remote_is_before, (1, 9))
348
class TestBzrDirOpenBranch(tests.TestCase):
571
350
def test_branch_present(self):
572
reference_format = self.get_repo_format()
573
network_name = reference_format.network_name()
574
branch_network_name = self.get_branch_format().network_name()
575
351
transport = MemoryTransport()
576
352
transport.mkdir('quack')
577
353
transport = transport.clone('quack')
578
354
client = FakeClient(transport.base)
579
355
client.add_expected_call(
580
'BzrDir.open_branchV3', ('quack/',),
581
'success', ('branch', branch_network_name))
356
'BzrDir.open_branch', ('quack/',),
357
'success', ('ok', ''))
582
358
client.add_expected_call(
583
'BzrDir.find_repositoryV3', ('quack/',),
584
'success', ('ok', '', 'no', 'no', 'no', network_name))
359
'BzrDir.find_repositoryV2', ('quack/',),
360
'success', ('ok', '', 'no', 'no', 'no'))
585
361
client.add_expected_call(
586
362
'Branch.get_stacked_on_url', ('quack/',),
587
363
'error', ('NotStacked',))
628
404
# transmitted as "~", not "%7E".
629
405
transport = RemoteTCPTransport('bzr://localhost/~hello/')
630
406
client = FakeClient(transport.base)
631
reference_format = self.get_repo_format()
632
network_name = reference_format.network_name()
633
branch_network_name = self.get_branch_format().network_name()
634
client.add_expected_call(
635
'BzrDir.open_branchV3', ('~hello/',),
636
'success', ('branch', branch_network_name))
637
client.add_expected_call(
638
'BzrDir.find_repositoryV3', ('~hello/',),
639
'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'))
640
413
client.add_expected_call(
641
414
'Branch.get_stacked_on_url', ('~hello/',),
642
415
'error', ('NotStacked',))
643
416
bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
645
418
result = bzrdir.open_branch()
646
self.assertFinished(client)
419
client.finished_test()
648
421
def check_open_repository(self, rich_root, subtrees, external_lookup='no'):
649
reference_format = self.get_repo_format()
650
network_name = reference_format.network_name()
651
422
transport = MemoryTransport()
652
423
transport.mkdir('quack')
653
424
transport = transport.clone('quack')
757
490
self.assertIsInstance(repo, remote.RemoteRepository)
758
491
# its format should have the settings from the response
759
492
format = repo._format
760
self.assertTrue(format.rich_root_data)
761
self.assertTrue(format.supports_tree_reference)
762
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)
763
496
self.assertEqual(network_name, format.network_name())
766
class TestBzrDirOpenRepository(TestRemote):
499
class TestBzrDirOpenRepository(tests.TestCase):
768
def test_backwards_compat_1_2_3(self):
769
# fallback all the way to the first version.
770
reference_format = self.get_repo_format()
771
network_name = reference_format.network_name()
772
server_url = 'bzr://example.com/'
773
self.permit_url(server_url)
774
client = FakeClient(server_url)
775
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)
776
506
client.add_unknown_method_response('BzrDir.find_repositoryV2')
777
507
client.add_success_response('ok', '', 'no', 'no')
778
# A real repository instance will be created to determine the network
780
client.add_success_response_with_body(
781
"Bazaar-NG meta directory, format 1\n", 'ok')
782
client.add_success_response_with_body(
783
reference_format.get_format_string(), 'ok')
784
# PackRepository wants to do a stat
785
client.add_success_response('stat', '0', '65535')
786
remote_transport = RemoteTransport(server_url + 'quack/', medium=False,
788
bzrdir = RemoteBzrDir(remote_transport, remote.RemoteBzrDirFormat(),
790
repo = bzrdir.open_repository()
792
[('call', 'BzrDir.find_repositoryV3', ('quack/',)),
793
('call', 'BzrDir.find_repositoryV2', ('quack/',)),
794
('call', 'BzrDir.find_repository', ('quack/',)),
795
('call_expecting_body', 'get', ('/quack/.bzr/branch-format',)),
796
('call_expecting_body', 'get', ('/quack/.bzr/repository/format',)),
797
('call', 'stat', ('/quack/.bzr/repository',)),
800
self.assertEqual(network_name, repo._format.network_name())
802
def test_backwards_compat_2(self):
803
# fallback to find_repositoryV2
804
reference_format = self.get_repo_format()
805
network_name = reference_format.network_name()
806
server_url = 'bzr://example.com/'
807
self.permit_url(server_url)
808
client = FakeClient(server_url)
809
client.add_unknown_method_response('BzrDir.find_repositoryV3')
810
client.add_success_response('ok', '', 'no', 'no', 'no')
811
# A real repository instance will be created to determine the network
813
client.add_success_response_with_body(
814
"Bazaar-NG meta directory, format 1\n", 'ok')
815
client.add_success_response_with_body(
816
reference_format.get_format_string(), 'ok')
817
# PackRepository wants to do a stat
818
client.add_success_response('stat', '0', '65535')
819
remote_transport = RemoteTransport(server_url + 'quack/', medium=False,
821
bzrdir = RemoteBzrDir(remote_transport, remote.RemoteBzrDirFormat(),
823
repo = bzrdir.open_repository()
825
[('call', 'BzrDir.find_repositoryV3', ('quack/',)),
826
('call', 'BzrDir.find_repositoryV2', ('quack/',)),
827
('call_expecting_body', 'get', ('/quack/.bzr/branch-format',)),
828
('call_expecting_body', 'get', ('/quack/.bzr/repository/format',)),
829
('call', 'stat', ('/quack/.bzr/repository',)),
832
self.assertEqual(network_name, repo._format.network_name())
834
def test_current_server(self):
835
reference_format = self.get_repo_format()
836
network_name = reference_format.network_name()
837
transport = MemoryTransport()
838
transport.mkdir('quack')
839
transport = transport.clone('quack')
840
client = FakeClient(transport.base)
841
client.add_success_response('ok', '', 'no', 'no', 'no', network_name)
842
508
bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
844
510
repo = bzrdir.open_repository()
845
511
self.assertEqual(
846
[('call', 'BzrDir.find_repositoryV3', ('quack/',))],
512
[('call', 'BzrDir.find_repositoryV2', ('quack/',)),
513
('call', 'BzrDir.find_repository', ('quack/',))],
848
self.assertEqual(network_name, repo._format.network_name())
851
class TestBzrDirFormatInitializeEx(TestRemote):
853
def test_success(self):
854
"""Simple test for typical successful call."""
855
fmt = bzrdir.RemoteBzrDirFormat()
856
default_format_name = BzrDirFormat.get_default_format().network_name()
857
transport = self.get_transport()
858
client = FakeClient(transport.base)
859
client.add_expected_call(
860
'BzrDirFormat.initialize_ex_1.16',
861
(default_format_name, 'path', 'False', 'False', 'False', '',
862
'', '', '', 'False'),
864
('.', 'no', 'no', 'yes', 'repo fmt', 'repo bzrdir fmt',
865
'bzrdir fmt', 'False', '', '', 'repo lock token'))
866
# XXX: It would be better to call fmt.initialize_on_transport_ex, but
867
# it's currently hard to test that without supplying a real remote
868
# transport connected to a real server.
869
result = fmt._initialize_on_transport_ex_rpc(client, 'path',
870
transport, False, False, False, None, None, None, None, False)
871
self.assertFinished(client)
873
def test_error(self):
874
"""Error responses are translated, e.g. 'PermissionDenied' raises the
875
corresponding error from the client.
877
fmt = bzrdir.RemoteBzrDirFormat()
878
default_format_name = BzrDirFormat.get_default_format().network_name()
879
transport = self.get_transport()
880
client = FakeClient(transport.base)
881
client.add_expected_call(
882
'BzrDirFormat.initialize_ex_1.16',
883
(default_format_name, 'path', 'False', 'False', 'False', '',
884
'', '', '', 'False'),
886
('PermissionDenied', 'path', 'extra info'))
887
# XXX: It would be better to call fmt.initialize_on_transport_ex, but
888
# it's currently hard to test that without supplying a real remote
889
# transport connected to a real server.
890
err = self.assertRaises(errors.PermissionDenied,
891
fmt._initialize_on_transport_ex_rpc, client, 'path', transport,
892
False, False, False, None, None, None, None, False)
893
self.assertEqual('path', err.path)
894
self.assertEqual(': extra info', err.extra)
895
self.assertFinished(client)
897
def test_error_from_real_server(self):
898
"""Integration test for error translation."""
899
transport = self.make_smart_server('foo')
900
transport = transport.clone('no-such-path')
901
fmt = bzrdir.RemoteBzrDirFormat()
902
err = self.assertRaises(errors.NoSuchFile,
903
fmt.initialize_on_transport_ex, transport, create_prefix=False)
906
517
class OldSmartClient(object):
960
553
# we do not want bzrdir to make any remote calls, so use False as its
961
554
# _client. If it tries to make a remote call, this will fail
963
bzrdir = self.make_remote_bzrdir(transport, False)
556
bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
964
558
repo = RemoteRepository(bzrdir, None, _client=client)
965
branch_format = self.get_branch_format()
966
format = RemoteBranchFormat(network_name=branch_format.network_name())
967
return RemoteBranch(bzrdir, repo, _client=client, format=format)
970
class TestBranchGetParent(RemoteBranchTestCase):
972
def test_no_parent(self):
973
# in an empty branch we decode the response properly
974
transport = MemoryTransport()
975
client = FakeClient(transport.base)
976
client.add_expected_call(
977
'Branch.get_stacked_on_url', ('quack/',),
978
'error', ('NotStacked',))
979
client.add_expected_call(
980
'Branch.get_parent', ('quack/',),
982
transport.mkdir('quack')
983
transport = transport.clone('quack')
984
branch = self.make_remote_branch(transport, client)
985
result = branch.get_parent()
986
self.assertFinished(client)
987
self.assertEqual(None, result)
989
def test_parent_relative(self):
990
transport = MemoryTransport()
991
client = FakeClient(transport.base)
992
client.add_expected_call(
993
'Branch.get_stacked_on_url', ('kwaak/',),
994
'error', ('NotStacked',))
995
client.add_expected_call(
996
'Branch.get_parent', ('kwaak/',),
997
'success', ('../foo/',))
998
transport.mkdir('kwaak')
999
transport = transport.clone('kwaak')
1000
branch = self.make_remote_branch(transport, client)
1001
result = branch.get_parent()
1002
self.assertEqual(transport.clone('../foo').base, result)
1004
def test_parent_absolute(self):
1005
transport = MemoryTransport()
1006
client = FakeClient(transport.base)
1007
client.add_expected_call(
1008
'Branch.get_stacked_on_url', ('kwaak/',),
1009
'error', ('NotStacked',))
1010
client.add_expected_call(
1011
'Branch.get_parent', ('kwaak/',),
1012
'success', ('http://foo/',))
1013
transport.mkdir('kwaak')
1014
transport = transport.clone('kwaak')
1015
branch = self.make_remote_branch(transport, client)
1016
result = branch.get_parent()
1017
self.assertEqual('http://foo/', result)
1018
self.assertFinished(client)
1021
class TestBranchSetParentLocation(RemoteBranchTestCase):
1023
def test_no_parent(self):
1024
# We call the verb when setting parent to None
1025
transport = MemoryTransport()
1026
client = FakeClient(transport.base)
1027
client.add_expected_call(
1028
'Branch.get_stacked_on_url', ('quack/',),
1029
'error', ('NotStacked',))
1030
client.add_expected_call(
1031
'Branch.set_parent_location', ('quack/', 'b', 'r', ''),
1033
transport.mkdir('quack')
1034
transport = transport.clone('quack')
1035
branch = self.make_remote_branch(transport, client)
1036
branch._lock_token = 'b'
1037
branch._repo_lock_token = 'r'
1038
branch._set_parent_location(None)
1039
self.assertFinished(client)
1041
def test_parent(self):
1042
transport = MemoryTransport()
1043
client = FakeClient(transport.base)
1044
client.add_expected_call(
1045
'Branch.get_stacked_on_url', ('kwaak/',),
1046
'error', ('NotStacked',))
1047
client.add_expected_call(
1048
'Branch.set_parent_location', ('kwaak/', 'b', 'r', 'foo'),
1050
transport.mkdir('kwaak')
1051
transport = transport.clone('kwaak')
1052
branch = self.make_remote_branch(transport, client)
1053
branch._lock_token = 'b'
1054
branch._repo_lock_token = 'r'
1055
branch._set_parent_location('foo')
1056
self.assertFinished(client)
1058
def test_backwards_compat(self):
1059
self.setup_smart_server_with_call_log()
1060
branch = self.make_branch('.')
1061
self.reset_smart_call_log()
1062
verb = 'Branch.set_parent_location'
1063
self.disable_verb(verb)
1064
branch.set_parent('http://foo/')
1065
self.assertLength(12, self.hpss_calls)
1068
class TestBranchGetTagsBytes(RemoteBranchTestCase):
1070
def test_backwards_compat(self):
1071
self.setup_smart_server_with_call_log()
1072
branch = self.make_branch('.')
1073
self.reset_smart_call_log()
1074
verb = 'Branch.get_tags_bytes'
1075
self.disable_verb(verb)
1076
branch.tags.get_tag_dict()
1077
call_count = len([call for call in self.hpss_calls if
1078
call.call.method == verb])
1079
self.assertEqual(1, call_count)
1081
def test_trivial(self):
1082
transport = MemoryTransport()
1083
client = FakeClient(transport.base)
1084
client.add_expected_call(
1085
'Branch.get_stacked_on_url', ('quack/',),
1086
'error', ('NotStacked',))
1087
client.add_expected_call(
1088
'Branch.get_tags_bytes', ('quack/',),
1090
transport.mkdir('quack')
1091
transport = transport.clone('quack')
1092
branch = self.make_remote_branch(transport, client)
1093
result = branch.tags.get_tag_dict()
1094
self.assertFinished(client)
1095
self.assertEqual({}, result)
1098
class TestBranchSetTagsBytes(RemoteBranchTestCase):
1100
def test_trivial(self):
1101
transport = MemoryTransport()
1102
client = FakeClient(transport.base)
1103
client.add_expected_call(
1104
'Branch.get_stacked_on_url', ('quack/',),
1105
'error', ('NotStacked',))
1106
client.add_expected_call(
1107
'Branch.set_tags_bytes', ('quack/', 'branch token', 'repo token'),
1109
transport.mkdir('quack')
1110
transport = transport.clone('quack')
1111
branch = self.make_remote_branch(transport, client)
1112
self.lock_remote_branch(branch)
1113
branch._set_tags_bytes('tags bytes')
1114
self.assertFinished(client)
1115
self.assertEqual('tags bytes', client._calls[-1][-1])
1117
def test_backwards_compatible(self):
1118
transport = MemoryTransport()
1119
client = FakeClient(transport.base)
1120
client.add_expected_call(
1121
'Branch.get_stacked_on_url', ('quack/',),
1122
'error', ('NotStacked',))
1123
client.add_expected_call(
1124
'Branch.set_tags_bytes', ('quack/', 'branch token', 'repo token'),
1125
'unknown', ('Branch.set_tags_bytes',))
1126
transport.mkdir('quack')
1127
transport = transport.clone('quack')
1128
branch = self.make_remote_branch(transport, client)
1129
self.lock_remote_branch(branch)
1130
class StubRealBranch(object):
1133
def _set_tags_bytes(self, bytes):
1134
self.calls.append(('set_tags_bytes', bytes))
1135
real_branch = StubRealBranch()
1136
branch._real_branch = real_branch
1137
branch._set_tags_bytes('tags bytes')
1138
# Call a second time, to exercise the 'remote version already inferred'
1140
branch._set_tags_bytes('tags bytes')
1141
self.assertFinished(client)
1143
[('set_tags_bytes', 'tags bytes')] * 2, real_branch.calls)
559
return RemoteBranch(bzrdir, repo, _client=client)
1146
562
class TestBranchLastRevisionInfo(RemoteBranchTestCase):
1597
1018
self.assertEqual('rejection message', err.msg)
1600
class TestBranchGetSetConfig(RemoteBranchTestCase):
1021
class TestBranchControlGetBranchConf(tests.TestCaseWithMemoryTransport):
1022
"""Getting the branch configuration should use an abstract method not vfs.
1602
1025
def test_get_branch_conf(self):
1603
# in an empty branch we decode the response properly
1604
client = FakeClient()
1605
client.add_expected_call(
1606
'Branch.get_stacked_on_url', ('memory:///',),
1607
'error', ('NotStacked',),)
1608
client.add_success_response_with_body('# config file body', 'ok')
1609
transport = MemoryTransport()
1610
branch = self.make_remote_branch(transport, client)
1611
config = branch.get_config()
1612
config.has_explicit_nickname()
1614
[('call', 'Branch.get_stacked_on_url', ('memory:///',)),
1615
('call_expecting_body', 'Branch.get_config_file', ('memory:///',))],
1618
def test_get_multi_line_branch_conf(self):
1619
# Make sure that multiple-line branch.conf files are supported
1621
# https://bugs.edge.launchpad.net/bzr/+bug/354075
1622
client = FakeClient()
1623
client.add_expected_call(
1624
'Branch.get_stacked_on_url', ('memory:///',),
1625
'error', ('NotStacked',),)
1626
client.add_success_response_with_body('a = 1\nb = 2\nc = 3\n', 'ok')
1627
transport = MemoryTransport()
1628
branch = self.make_remote_branch(transport, client)
1629
config = branch.get_config()
1630
self.assertEqual(u'2', config.get_user_option('b'))
1632
def test_set_option(self):
1633
client = FakeClient()
1634
client.add_expected_call(
1635
'Branch.get_stacked_on_url', ('memory:///',),
1636
'error', ('NotStacked',),)
1637
client.add_expected_call(
1638
'Branch.lock_write', ('memory:///', '', ''),
1639
'success', ('ok', 'branch token', 'repo token'))
1640
client.add_expected_call(
1641
'Branch.set_config_option', ('memory:///', 'branch token',
1642
'repo token', 'foo', 'bar', ''),
1644
client.add_expected_call(
1645
'Branch.unlock', ('memory:///', 'branch token', 'repo token'),
1647
transport = MemoryTransport()
1648
branch = self.make_remote_branch(transport, client)
1650
config = branch._get_config()
1651
config.set_option('foo', 'bar')
1653
self.assertFinished(client)
1655
def test_backwards_compat_set_option(self):
1656
self.setup_smart_server_with_call_log()
1657
branch = self.make_branch('.')
1658
verb = 'Branch.set_config_option'
1659
self.disable_verb(verb)
1661
self.addCleanup(branch.unlock)
1662
self.reset_smart_call_log()
1663
branch._get_config().set_option('value', 'name')
1664
self.assertLength(10, self.hpss_calls)
1665
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/',))],
1668
1047
class TestBranchLockWrite(RemoteBranchTestCase):
1949
1261
self.assertEqual({r1: (NULL_REVISION,)}, parents)
1950
1262
self.assertEqual(
1951
1263
[('call_with_body_bytes_expecting_body',
1952
'Repository.get_parent_map', ('quack/', 'include-missing:', r2),
1264
'Repository.get_parent_map', ('quack/', r2), '\n\n0'),
1954
1265
('call_with_body_bytes_expecting_body',
1955
'Repository.get_parent_map', ('quack/', 'include-missing:', r1),
1266
'Repository.get_parent_map', ('quack/', r1), '\n\n0'),
1961
1271
def test_get_parent_map_reconnects_if_unknown_method(self):
1962
1272
transport_path = 'quack'
1963
rev_id = 'revision-id'
1964
1273
repo, client = self.setup_fake_client_and_repository(transport_path)
1965
client.add_unknown_method_response('Repository.get_parent_map')
1966
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')
1967
1276
self.assertFalse(client._medium._is_remote_before((1, 2)))
1968
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])
1969
1283
self.assertEqual(
1970
1284
[('call_with_body_bytes_expecting_body',
1971
'Repository.get_parent_map', ('quack/', 'include-missing:',
1285
'Repository.get_parent_map', ('quack/', rev_id), '\n\n0'),
1973
1286
('disconnect medium',),
1974
1287
('call_expecting_body', 'Repository.get_revision_graph',
1975
1288
('quack/', ''))],
1977
1290
# The medium is now marked as being connected to an older server
1978
1291
self.assertTrue(client._medium._is_remote_before((1, 2)))
1979
self.assertEqual({rev_id: ('null:',)}, parents)
1981
1293
def test_get_parent_map_fallback_parentless_node(self):
1982
1294
"""get_parent_map falls back to get_revision_graph on old servers. The
2008
1324
errors.UnexpectedSmartServerResponse,
2009
1325
repo.get_parent_map, ['a-revision-id'])
2011
def test_get_parent_map_negative_caches_missing_keys(self):
2012
self.setup_smart_server_with_call_log()
2013
repo = self.make_repository('foo')
2014
self.assertIsInstance(repo, RemoteRepository)
2016
self.addCleanup(repo.unlock)
2017
self.reset_smart_call_log()
2018
graph = repo.get_graph()
2019
self.assertEqual({},
2020
graph.get_parent_map(['some-missing', 'other-missing']))
2021
self.assertLength(1, self.hpss_calls)
2022
# No call if we repeat this
2023
self.reset_smart_call_log()
2024
graph = repo.get_graph()
2025
self.assertEqual({},
2026
graph.get_parent_map(['some-missing', 'other-missing']))
2027
self.assertLength(0, self.hpss_calls)
2028
# Asking for more unknown keys makes a request.
2029
self.reset_smart_call_log()
2030
graph = repo.get_graph()
2031
self.assertEqual({},
2032
graph.get_parent_map(['some-missing', 'other-missing',
2034
self.assertLength(1, self.hpss_calls)
2036
def disableExtraResults(self):
2037
self.overrideAttr(SmartServerRepositoryGetParentMap,
2038
'no_extra_results', True)
2040
def test_null_cached_missing_and_stop_key(self):
2041
self.setup_smart_server_with_call_log()
2042
# Make a branch with a single revision.
2043
builder = self.make_branch_builder('foo')
2044
builder.start_series()
2045
builder.build_snapshot('first', None, [
2046
('add', ('', 'root-id', 'directory', ''))])
2047
builder.finish_series()
2048
branch = builder.get_branch()
2049
repo = branch.repository
2050
self.assertIsInstance(repo, RemoteRepository)
2051
# Stop the server from sending extra results.
2052
self.disableExtraResults()
2054
self.addCleanup(repo.unlock)
2055
self.reset_smart_call_log()
2056
graph = repo.get_graph()
2057
# Query for 'first' and 'null:'. Because 'null:' is a parent of
2058
# 'first' it will be a candidate for the stop_keys of subsequent
2059
# requests, and because 'null:' was queried but not returned it will be
2060
# cached as missing.
2061
self.assertEqual({'first': ('null:',)},
2062
graph.get_parent_map(['first', 'null:']))
2063
# Now query for another key. This request will pass along a recipe of
2064
# start and stop keys describing the already cached results, and this
2065
# recipe's revision count must be correct (or else it will trigger an
2066
# error from the server).
2067
self.assertEqual({}, graph.get_parent_map(['another-key']))
2068
# This assertion guards against disableExtraResults silently failing to
2069
# work, thus invalidating the test.
2070
self.assertLength(2, self.hpss_calls)
2072
def test_get_parent_map_gets_ghosts_from_result(self):
2073
# asking for a revision should negatively cache close ghosts in its
2075
self.setup_smart_server_with_call_log()
2076
tree = self.make_branch_and_memory_tree('foo')
2079
builder = treebuilder.TreeBuilder()
2080
builder.start_tree(tree)
2082
builder.finish_tree()
2083
tree.set_parent_ids(['non-existant'], allow_leftmost_as_ghost=True)
2084
rev_id = tree.commit('')
2088
self.addCleanup(tree.unlock)
2089
repo = tree.branch.repository
2090
self.assertIsInstance(repo, RemoteRepository)
2092
repo.get_parent_map([rev_id])
2093
self.reset_smart_call_log()
2094
# Now asking for rev_id's ghost parent should not make calls
2095
self.assertEqual({}, repo.get_parent_map(['non-existant']))
2096
self.assertLength(0, self.hpss_calls)
2099
1328
class TestGetParentMapAllowsNew(tests.TestCaseWithTransport):
2101
1330
def test_allows_new_revisions(self):
2102
1331
"""get_parent_map's results can be updated by commit."""
2103
smart_server = test_server.SmartTCPServer_for_testing()
2104
self.start_server(smart_server)
1332
smart_server = server.SmartTCPServer_for_testing()
1333
smart_server.setUp()
1334
self.addCleanup(smart_server.tearDown)
2105
1335
self.make_branch('branch')
2106
1336
branch = Branch.open(smart_server.get_url() + '/branch')
2107
1337
tree = branch.create_checkout('tree', lightweight=True)
2186
1413
repo, client = self.setup_fake_client_and_repository(transport_path)
2187
1414
client.add_error_response('AnUnexpectedError')
2188
1415
e = self.assertRaises(errors.UnknownErrorFromSmartServer,
2189
repo._get_revision_graph, revid)
1416
self.applyDeprecated, one_four, repo.get_revision_graph, revid)
2190
1417
self.assertEqual(('AnUnexpectedError',), e.error_tuple)
2193
class TestRepositoryGetRevIdForRevno(TestRemoteRepository):
2196
repo, client = self.setup_fake_client_and_repository('quack')
2197
client.add_expected_call(
2198
'Repository.get_rev_id_for_revno', ('quack/', 5, (42, 'rev-foo')),
2199
'success', ('ok', 'rev-five'))
2200
result = repo.get_rev_id_for_revno(5, (42, 'rev-foo'))
2201
self.assertEqual((True, 'rev-five'), result)
2202
self.assertFinished(client)
2204
def test_history_incomplete(self):
2205
repo, client = self.setup_fake_client_and_repository('quack')
2206
client.add_expected_call(
2207
'Repository.get_rev_id_for_revno', ('quack/', 5, (42, 'rev-foo')),
2208
'success', ('history-incomplete', 10, 'rev-ten'))
2209
result = repo.get_rev_id_for_revno(5, (42, 'rev-foo'))
2210
self.assertEqual((False, (10, 'rev-ten')), result)
2211
self.assertFinished(client)
2213
def test_history_incomplete_with_fallback(self):
2214
"""A 'history-incomplete' response causes the fallback repository to be
2215
queried too, if one is set.
2217
# Make a repo with a fallback repo, both using a FakeClient.
2218
format = remote.response_tuple_to_repo_format(
2219
('yes', 'no', 'yes', self.get_repo_format().network_name()))
2220
repo, client = self.setup_fake_client_and_repository('quack')
2221
repo._format = format
2222
fallback_repo, ignored = self.setup_fake_client_and_repository(
2224
fallback_repo._client = client
2225
fallback_repo._format = format
2226
repo.add_fallback_repository(fallback_repo)
2227
# First the client should ask the primary repo
2228
client.add_expected_call(
2229
'Repository.get_rev_id_for_revno', ('quack/', 1, (42, 'rev-foo')),
2230
'success', ('history-incomplete', 2, 'rev-two'))
2231
# Then it should ask the fallback, using revno/revid from the
2232
# history-incomplete response as the known revno/revid.
2233
client.add_expected_call(
2234
'Repository.get_rev_id_for_revno',('fallback/', 1, (2, 'rev-two')),
2235
'success', ('ok', 'rev-one'))
2236
result = repo.get_rev_id_for_revno(1, (42, 'rev-foo'))
2237
self.assertEqual((True, 'rev-one'), result)
2238
self.assertFinished(client)
2240
def test_nosuchrevision(self):
2241
# 'nosuchrevision' is returned when the known-revid is not found in the
2242
# remote repo. The client translates that response to NoSuchRevision.
2243
repo, client = self.setup_fake_client_and_repository('quack')
2244
client.add_expected_call(
2245
'Repository.get_rev_id_for_revno', ('quack/', 5, (42, 'rev-foo')),
2246
'error', ('nosuchrevision', 'rev-foo'))
2248
errors.NoSuchRevision,
2249
repo.get_rev_id_for_revno, 5, (42, 'rev-foo'))
2250
self.assertFinished(client)
2252
def test_branch_fallback_locking(self):
2253
"""RemoteBranch.get_rev_id takes a read lock, and tries to call the
2254
get_rev_id_for_revno verb. If the verb is unknown the VFS fallback
2255
will be invoked, which will fail if the repo is unlocked.
2257
self.setup_smart_server_with_call_log()
2258
tree = self.make_branch_and_memory_tree('.')
2260
rev1 = tree.commit('First')
2261
rev2 = tree.commit('Second')
2263
branch = tree.branch
2264
self.assertFalse(branch.is_locked())
2265
self.reset_smart_call_log()
2266
verb = 'Repository.get_rev_id_for_revno'
2267
self.disable_verb(verb)
2268
self.assertEqual(rev1, branch.get_rev_id(1))
2269
self.assertLength(1, [call for call in self.hpss_calls if
2270
call.call.method == verb])
2273
1420
class TestRepositoryIsShared(TestRemoteRepository):
2275
1422
def test_is_shared(self):
2390
1537
self.assertEqual([], client._calls)
2393
class TestRepositoryInsertStreamBase(TestRemoteRepository):
2394
"""Base class for Repository.insert_stream and .insert_stream_1.19
2398
def checkInsertEmptyStream(self, repo, client):
2399
"""Insert an empty stream, checking the result.
2401
This checks that there are no resume_tokens or missing_keys, and that
2402
the client is finished.
2404
sink = repo._get_sink()
2405
fmt = repository.RepositoryFormat.get_default_format()
2406
resume_tokens, missing_keys = sink.insert_stream([], fmt, [])
2407
self.assertEqual([], resume_tokens)
2408
self.assertEqual(set(), missing_keys)
2409
self.assertFinished(client)
2412
class TestRepositoryInsertStream(TestRepositoryInsertStreamBase):
2413
"""Tests for using Repository.insert_stream verb when the _1.19 variant is
2416
This test case is very similar to TestRepositoryInsertStream_1_19.
2420
TestRemoteRepository.setUp(self)
2421
self.disable_verb('Repository.insert_stream_1.19')
2423
def test_unlocked_repo(self):
2424
transport_path = 'quack'
2425
repo, client = self.setup_fake_client_and_repository(transport_path)
2426
client.add_expected_call(
2427
'Repository.insert_stream_1.19', ('quack/', ''),
2428
'unknown', ('Repository.insert_stream_1.19',))
2429
client.add_expected_call(
2430
'Repository.insert_stream', ('quack/', ''),
2432
client.add_expected_call(
2433
'Repository.insert_stream', ('quack/', ''),
2435
self.checkInsertEmptyStream(repo, client)
2437
def test_locked_repo_with_no_lock_token(self):
2438
transport_path = 'quack'
2439
repo, client = self.setup_fake_client_and_repository(transport_path)
2440
client.add_expected_call(
2441
'Repository.lock_write', ('quack/', ''),
2442
'success', ('ok', ''))
2443
client.add_expected_call(
2444
'Repository.insert_stream_1.19', ('quack/', ''),
2445
'unknown', ('Repository.insert_stream_1.19',))
2446
client.add_expected_call(
2447
'Repository.insert_stream', ('quack/', ''),
2449
client.add_expected_call(
2450
'Repository.insert_stream', ('quack/', ''),
2453
self.checkInsertEmptyStream(repo, client)
2455
def test_locked_repo_with_lock_token(self):
2456
transport_path = 'quack'
2457
repo, client = self.setup_fake_client_and_repository(transport_path)
2458
client.add_expected_call(
2459
'Repository.lock_write', ('quack/', ''),
2460
'success', ('ok', 'a token'))
2461
client.add_expected_call(
2462
'Repository.insert_stream_1.19', ('quack/', '', 'a token'),
2463
'unknown', ('Repository.insert_stream_1.19',))
2464
client.add_expected_call(
2465
'Repository.insert_stream_locked', ('quack/', '', 'a token'),
2467
client.add_expected_call(
2468
'Repository.insert_stream_locked', ('quack/', '', 'a token'),
2471
self.checkInsertEmptyStream(repo, client)
2473
def test_stream_with_inventory_deltas(self):
2474
"""'inventory-deltas' substreams cannot be sent to the
2475
Repository.insert_stream verb, because not all servers that implement
2476
that verb will accept them. So when one is encountered the RemoteSink
2477
immediately stops using that verb and falls back to VFS insert_stream.
2479
transport_path = 'quack'
2480
repo, client = self.setup_fake_client_and_repository(transport_path)
2481
client.add_expected_call(
2482
'Repository.insert_stream_1.19', ('quack/', ''),
2483
'unknown', ('Repository.insert_stream_1.19',))
2484
client.add_expected_call(
2485
'Repository.insert_stream', ('quack/', ''),
2487
client.add_expected_call(
2488
'Repository.insert_stream', ('quack/', ''),
2490
# Create a fake real repository for insert_stream to fall back on, so
2491
# that we can directly see the records the RemoteSink passes to the
2496
def insert_stream(self, stream, src_format, resume_tokens):
2497
for substream_kind, substream in stream:
2498
self.records.append(
2499
(substream_kind, [record.key for record in substream]))
2500
return ['fake tokens'], ['fake missing keys']
2501
fake_real_sink = FakeRealSink()
2502
class FakeRealRepository:
2503
def _get_sink(self):
2504
return fake_real_sink
2505
def is_in_write_group(self):
2507
def refresh_data(self):
2509
repo._real_repository = FakeRealRepository()
2510
sink = repo._get_sink()
2511
fmt = repository.RepositoryFormat.get_default_format()
2512
stream = self.make_stream_with_inv_deltas(fmt)
2513
resume_tokens, missing_keys = sink.insert_stream(stream, fmt, [])
2514
# Every record from the first inventory delta should have been sent to
2516
expected_records = [
2517
('inventory-deltas', [('rev2',), ('rev3',)]),
2518
('texts', [('some-rev', 'some-file')])]
2519
self.assertEqual(expected_records, fake_real_sink.records)
2520
# The return values from the real sink's insert_stream are propagated
2521
# back to the original caller.
2522
self.assertEqual(['fake tokens'], resume_tokens)
2523
self.assertEqual(['fake missing keys'], missing_keys)
2524
self.assertFinished(client)
2526
def make_stream_with_inv_deltas(self, fmt):
2527
"""Make a simple stream with an inventory delta followed by more
2528
records and more substreams to test that all records and substreams
2529
from that point on are used.
2531
This sends, in order:
2532
* inventories substream: rev1, rev2, rev3. rev2 and rev3 are
2534
* texts substream: (some-rev, some-file)
2536
# Define a stream using generators so that it isn't rewindable.
2537
inv = inventory.Inventory(revision_id='rev1')
2538
inv.root.revision = 'rev1'
2539
def stream_with_inv_delta():
2540
yield ('inventories', inventories_substream())
2541
yield ('inventory-deltas', inventory_delta_substream())
2543
versionedfile.FulltextContentFactory(
2544
('some-rev', 'some-file'), (), None, 'content')])
2545
def inventories_substream():
2546
# An empty inventory fulltext. This will be streamed normally.
2547
text = fmt._serializer.write_inventory_to_string(inv)
2548
yield versionedfile.FulltextContentFactory(
2549
('rev1',), (), None, text)
2550
def inventory_delta_substream():
2551
# An inventory delta. This can't be streamed via this verb, so it
2552
# will trigger a fallback to VFS insert_stream.
2553
entry = inv.make_entry(
2554
'directory', 'newdir', inv.root.file_id, 'newdir-id')
2555
entry.revision = 'ghost'
2556
delta = [(None, 'newdir', 'newdir-id', entry)]
2557
serializer = inventory_delta.InventoryDeltaSerializer(
2558
versioned_root=True, tree_references=False)
2559
lines = serializer.delta_to_lines('rev1', 'rev2', delta)
2560
yield versionedfile.ChunkedContentFactory(
2561
('rev2',), (('rev1',)), None, lines)
2563
lines = serializer.delta_to_lines('rev1', 'rev3', delta)
2564
yield versionedfile.ChunkedContentFactory(
2565
('rev3',), (('rev1',)), None, lines)
2566
return stream_with_inv_delta()
2569
class TestRepositoryInsertStream_1_19(TestRepositoryInsertStreamBase):
2571
def test_unlocked_repo(self):
2572
transport_path = 'quack'
2573
repo, client = self.setup_fake_client_and_repository(transport_path)
2574
client.add_expected_call(
2575
'Repository.insert_stream_1.19', ('quack/', ''),
2577
client.add_expected_call(
2578
'Repository.insert_stream_1.19', ('quack/', ''),
2580
self.checkInsertEmptyStream(repo, client)
2582
def test_locked_repo_with_no_lock_token(self):
2583
transport_path = 'quack'
2584
repo, client = self.setup_fake_client_and_repository(transport_path)
2585
client.add_expected_call(
2586
'Repository.lock_write', ('quack/', ''),
2587
'success', ('ok', ''))
2588
client.add_expected_call(
2589
'Repository.insert_stream_1.19', ('quack/', ''),
2591
client.add_expected_call(
2592
'Repository.insert_stream_1.19', ('quack/', ''),
2595
self.checkInsertEmptyStream(repo, client)
2597
def test_locked_repo_with_lock_token(self):
2598
transport_path = 'quack'
2599
repo, client = self.setup_fake_client_and_repository(transport_path)
2600
client.add_expected_call(
2601
'Repository.lock_write', ('quack/', ''),
2602
'success', ('ok', 'a token'))
2603
client.add_expected_call(
2604
'Repository.insert_stream_1.19', ('quack/', '', 'a token'),
2606
client.add_expected_call(
2607
'Repository.insert_stream_1.19', ('quack/', '', 'a token'),
2610
self.checkInsertEmptyStream(repo, client)
2613
1540
class TestRepositoryTarball(TestRemoteRepository):
2615
1542
# This is a canned tarball reponse we can validate against
2991
1900
remote_repo.unlock()
2993
1902
def prepare_stacked_remote_branch(self):
2994
"""Get stacked_upon and stacked branches with content in each."""
2995
self.setup_smart_server_with_call_log()
2996
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')
2997
1907
tree1.commit('rev1', rev_id='rev1')
2998
tree2 = tree1.branch.bzrdir.sprout('tree2', stacked=True
2999
).open_workingtree()
3000
local_tree = tree2.branch.create_checkout('local')
3001
local_tree.commit('local changes make me feel good.')
3002
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')
3003
1911
branch2.lock_read()
3004
1912
self.addCleanup(branch2.unlock)
3005
return tree1.branch, branch2
3007
1915
def test_stacked_get_parent_map(self):
3008
1916
# the public implementation of get_parent_map obeys stacking
3009
_, branch = self.prepare_stacked_remote_branch()
1917
branch = self.prepare_stacked_remote_branch()
3010
1918
repo = branch.repository
3011
1919
self.assertEqual(['rev1'], repo.get_parent_map(['rev1']).keys())
3013
1921
def test_unstacked_get_parent_map(self):
3014
1922
# _unstacked_provider.get_parent_map ignores stacking
3015
_, branch = self.prepare_stacked_remote_branch()
1923
branch = self.prepare_stacked_remote_branch()
3016
1924
provider = branch.repository._unstacked_provider
3017
1925
self.assertEqual([], provider.get_parent_map(['rev1']).keys())
3019
def fetch_stream_to_rev_order(self, stream):
3021
for kind, substream in stream:
3022
if not kind == 'revisions':
3025
for content in substream:
3026
result.append(content.key[-1])
3029
def get_ordered_revs(self, format, order, branch_factory=None):
3030
"""Get a list of the revisions in a stream to format format.
3032
:param format: The format of the target.
3033
:param order: the order that target should have requested.
3034
:param branch_factory: A callable to create a trunk and stacked branch
3035
to fetch from. If none, self.prepare_stacked_remote_branch is used.
3036
:result: The revision ids in the stream, in the order seen,
3037
the topological order of revisions in the source.
3039
unordered_format = bzrdir.format_registry.get(format)()
3040
target_repository_format = unordered_format.repository_format
3042
self.assertEqual(order, target_repository_format._fetch_order)
3043
if branch_factory is None:
3044
branch_factory = self.prepare_stacked_remote_branch
3045
_, stacked = branch_factory()
3046
source = stacked.repository._get_source(target_repository_format)
3047
tip = stacked.last_revision()
3048
revs = stacked.repository.get_ancestry(tip)
3049
search = graph.PendingAncestryResult([tip], stacked.repository)
3050
self.reset_smart_call_log()
3051
stream = source.get_stream(search)
3054
# We trust that if a revision is in the stream the rest of the new
3055
# content for it is too, as per our main fetch tests; here we are
3056
# checking that the revisions are actually included at all, and their
3058
return self.fetch_stream_to_rev_order(stream), revs
3060
def test_stacked_get_stream_unordered(self):
3061
# Repository._get_source.get_stream() from a stacked repository with
3062
# unordered yields the full data from both stacked and stacked upon
3064
rev_ord, expected_revs = self.get_ordered_revs('1.9', 'unordered')
3065
self.assertEqual(set(expected_revs), set(rev_ord))
3066
# Getting unordered results should have made a streaming data request
3067
# from the server, then one from the backing branch.
3068
self.assertLength(2, self.hpss_calls)
3070
def test_stacked_on_stacked_get_stream_unordered(self):
3071
# Repository._get_source.get_stream() from a stacked repository which
3072
# is itself stacked yields the full data from all three sources.
3073
def make_stacked_stacked():
3074
_, stacked = self.prepare_stacked_remote_branch()
3075
tree = stacked.bzrdir.sprout('tree3', stacked=True
3076
).open_workingtree()
3077
local_tree = tree.branch.create_checkout('local-tree3')
3078
local_tree.commit('more local changes are better')
3079
branch = Branch.open(self.get_url('tree3'))
3081
self.addCleanup(branch.unlock)
3083
rev_ord, expected_revs = self.get_ordered_revs('1.9', 'unordered',
3084
branch_factory=make_stacked_stacked)
3085
self.assertEqual(set(expected_revs), set(rev_ord))
3086
# Getting unordered results should have made a streaming data request
3087
# from the server, and one from each backing repo
3088
self.assertLength(3, self.hpss_calls)
3090
def test_stacked_get_stream_topological(self):
3091
# Repository._get_source.get_stream() from a stacked repository with
3092
# topological sorting yields the full data from both stacked and
3093
# stacked upon sources in topological order.
3094
rev_ord, expected_revs = self.get_ordered_revs('knit', 'topological')
3095
self.assertEqual(expected_revs, rev_ord)
3096
# Getting topological sort requires VFS calls still - one of which is
3097
# pushing up from the bound branch.
3098
self.assertLength(13, self.hpss_calls)
3100
def test_stacked_get_stream_groupcompress(self):
3101
# Repository._get_source.get_stream() from a stacked repository with
3102
# groupcompress sorting yields the full data from both stacked and
3103
# stacked upon sources in groupcompress order.
3104
raise tests.TestSkipped('No groupcompress ordered format available')
3105
rev_ord, expected_revs = self.get_ordered_revs('dev5', 'groupcompress')
3106
self.assertEqual(expected_revs, reversed(rev_ord))
3107
# Getting unordered results should have made a streaming data request
3108
# from the backing branch, and one from the stacked on branch.
3109
self.assertLength(2, self.hpss_calls)
3111
def test_stacked_pull_more_than_stacking_has_bug_360791(self):
3112
# When pulling some fixed amount of content that is more than the
3113
# source has (because some is coming from a fallback branch, no error
3114
# should be received. This was reported as bug 360791.
3115
# Need three branches: a trunk, a stacked branch, and a preexisting
3116
# branch pulling content from stacked and trunk.
3117
self.setup_smart_server_with_call_log()
3118
trunk = self.make_branch_and_tree('trunk', format="1.9-rich-root")
3119
r1 = trunk.commit('start')
3120
stacked_branch = trunk.branch.create_clone_on_transport(
3121
self.get_transport('stacked'), stacked_on=trunk.branch.base)
3122
local = self.make_branch('local', format='1.9-rich-root')
3123
local.repository.fetch(stacked_branch.repository,
3124
stacked_branch.last_revision())
3127
1928
class TestRemoteBranchEffort(tests.TestCaseWithTransport):