106
135
b = BzrDir.open_from_transport(self.transport).open_branch()
107
136
self.assertStartsWith(str(b), 'RemoteBranch(')
110
class FakeRemoteTransport(object):
111
"""This class provides the minimum support for use in place of a RemoteTransport.
113
It doesn't actually transmit requests, but rather expects them to be
114
handled by a FakeClient which holds canned responses. It does not allow
115
any vfs access, therefore is not suitable for testing any operation that
116
will fallback to vfs access. Backing the test by an instance of this
117
class guarantees that it's - done using non-vfs operations.
120
_default_url = 'fakeremotetransport://host/path/'
122
def __init__(self, url=None):
124
url = self._default_url
128
return "%r(%r)" % (self.__class__.__name__,
131
def clone(self, relpath):
132
return FakeRemoteTransport(urlutils.join(self.base, relpath))
134
def get(self, relpath):
135
# only get is specifically stubbed out, because it's usually the first
136
# thing we do. anything else will fail with an AttributeError.
137
raise AssertionError("%r doesn't support file access to %r"
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)
142
182
class FakeProtocol(object):
352
423
AssertionError, client_medium._remember_remote_is_before, (1, 9))
355
class TestBzrDirOpenBranch(tests.TestCase):
426
class TestBzrDirCloningMetaDir(TestRemote):
428
def test_backwards_compat(self):
429
self.setup_smart_server_with_call_log()
430
a_dir = self.make_bzrdir('.')
431
self.reset_smart_call_log()
432
verb = 'BzrDir.cloning_metadir'
433
self.disable_verb(verb)
434
format = a_dir.cloning_metadir()
435
call_count = len([call for call in self.hpss_calls if
436
call.call.method == verb])
437
self.assertEqual(1, call_count)
439
def test_branch_reference(self):
440
transport = self.get_transport('quack')
441
referenced = self.make_branch('referenced')
442
expected = referenced.bzrdir.cloning_metadir()
443
client = FakeClient(transport.base)
444
client.add_expected_call(
445
'BzrDir.cloning_metadir', ('quack/', 'False'),
446
'error', ('BranchReference',)),
447
client.add_expected_call(
448
'BzrDir.open_branchV3', ('quack/',),
449
'success', ('ref', self.get_url('referenced'))),
450
a_bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
452
result = a_bzrdir.cloning_metadir()
453
# We should have got a control dir matching the referenced branch.
454
self.assertEqual(bzrdir.BzrDirMetaFormat1, type(result))
455
self.assertEqual(expected._repository_format, result._repository_format)
456
self.assertEqual(expected._branch_format, result._branch_format)
457
self.assertFinished(client)
459
def test_current_server(self):
460
transport = self.get_transport('.')
461
transport = transport.clone('quack')
462
self.make_bzrdir('quack')
463
client = FakeClient(transport.base)
464
reference_bzrdir_format = bzrdir.format_registry.get('default')()
465
control_name = reference_bzrdir_format.network_name()
466
client.add_expected_call(
467
'BzrDir.cloning_metadir', ('quack/', 'False'),
468
'success', (control_name, '', ('branch', ''))),
469
a_bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
471
result = a_bzrdir.cloning_metadir()
472
# We should have got a reference control dir with default branch and
473
# repository formats.
474
# This pokes a little, just to be sure.
475
self.assertEqual(bzrdir.BzrDirMetaFormat1, type(result))
476
self.assertEqual(None, result._repository_format)
477
self.assertEqual(None, result._branch_format)
478
self.assertFinished(client)
481
class TestBzrDirOpen(TestRemote):
483
def make_fake_client_and_transport(self, path='quack'):
484
transport = MemoryTransport()
485
transport.mkdir(path)
486
transport = transport.clone(path)
487
client = FakeClient(transport.base)
488
return client, transport
490
def test_absent(self):
491
client, transport = self.make_fake_client_and_transport()
492
client.add_expected_call(
493
'BzrDir.open_2.1', ('quack/',), 'success', ('no',))
494
self.assertRaises(errors.NotBranchError, RemoteBzrDir, transport,
495
remote.RemoteBzrDirFormat(), _client=client, _force_probe=True)
496
self.assertFinished(client)
498
def test_present_without_workingtree(self):
499
client, transport = self.make_fake_client_and_transport()
500
client.add_expected_call(
501
'BzrDir.open_2.1', ('quack/',), 'success', ('yes', 'no'))
502
bd = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
503
_client=client, _force_probe=True)
504
self.assertIsInstance(bd, RemoteBzrDir)
505
self.assertFalse(bd.has_workingtree())
506
self.assertRaises(errors.NoWorkingTree, bd.open_workingtree)
507
self.assertFinished(client)
509
def test_present_with_workingtree(self):
510
client, transport = self.make_fake_client_and_transport()
511
client.add_expected_call(
512
'BzrDir.open_2.1', ('quack/',), 'success', ('yes', 'yes'))
513
bd = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
514
_client=client, _force_probe=True)
515
self.assertIsInstance(bd, RemoteBzrDir)
516
self.assertTrue(bd.has_workingtree())
517
self.assertRaises(errors.NotLocalUrl, bd.open_workingtree)
518
self.assertFinished(client)
520
def test_backwards_compat(self):
521
client, transport = self.make_fake_client_and_transport()
522
client.add_expected_call(
523
'BzrDir.open_2.1', ('quack/',), 'unknown', ('BzrDir.open_2.1',))
524
client.add_expected_call(
525
'BzrDir.open', ('quack/',), 'success', ('yes',))
526
bd = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
527
_client=client, _force_probe=True)
528
self.assertIsInstance(bd, RemoteBzrDir)
529
self.assertFinished(client)
532
class TestBzrDirOpenBranch(TestRemote):
534
def test_backwards_compat(self):
535
self.setup_smart_server_with_call_log()
536
self.make_branch('.')
537
a_dir = BzrDir.open(self.get_url('.'))
538
self.reset_smart_call_log()
539
verb = 'BzrDir.open_branchV3'
540
self.disable_verb(verb)
541
format = a_dir.open_branch()
542
call_count = len([call for call in self.hpss_calls if
543
call.call.method == verb])
544
self.assertEqual(1, call_count)
357
546
def test_branch_present(self):
547
reference_format = self.get_repo_format()
548
network_name = reference_format.network_name()
549
branch_network_name = self.get_branch_format().network_name()
358
550
transport = MemoryTransport()
359
551
transport.mkdir('quack')
360
552
transport = transport.clone('quack')
361
553
client = FakeClient(transport.base)
362
554
client.add_expected_call(
363
'BzrDir.open_branch', ('quack/',),
364
'success', ('ok', ''))
555
'BzrDir.open_branchV3', ('quack/',),
556
'success', ('branch', branch_network_name))
365
557
client.add_expected_call(
366
'BzrDir.find_repositoryV2', ('quack/',),
367
'success', ('ok', '', 'no', 'no', 'no'))
558
'BzrDir.find_repositoryV3', ('quack/',),
559
'success', ('ok', '', 'no', 'no', 'no', network_name))
368
560
client.add_expected_call(
369
561
'Branch.get_stacked_on_url', ('quack/',),
370
562
'error', ('NotStacked',))
371
bzrdir = RemoteBzrDir(transport, _client=client)
563
bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
372
565
result = bzrdir.open_branch()
373
566
self.assertIsInstance(result, RemoteBranch)
374
567
self.assertEqual(bzrdir, result.bzrdir)
375
client.finished_test()
568
self.assertFinished(client)
377
570
def test_branch_missing(self):
378
571
transport = MemoryTransport()
461
664
RemoteBzrDirFormat.probe_transport, OldServerTransport())
464
class TestBzrDirOpenRepository(tests.TestCase):
466
def test_backwards_compat_1_2(self):
467
transport = MemoryTransport()
468
transport.mkdir('quack')
469
transport = transport.clone('quack')
470
client = FakeClient(transport.base)
471
client.add_unknown_method_response('RemoteRepository.find_repositoryV2')
667
class TestBzrDirCreateBranch(TestRemote):
669
def test_backwards_compat(self):
670
self.setup_smart_server_with_call_log()
671
repo = self.make_repository('.')
672
self.reset_smart_call_log()
673
self.disable_verb('BzrDir.create_branch')
674
branch = repo.bzrdir.create_branch()
675
create_branch_call_count = len([call for call in self.hpss_calls if
676
call.call.method == 'BzrDir.create_branch'])
677
self.assertEqual(1, create_branch_call_count)
679
def test_current_server(self):
680
transport = self.get_transport('.')
681
transport = transport.clone('quack')
682
self.make_repository('quack')
683
client = FakeClient(transport.base)
684
reference_bzrdir_format = bzrdir.format_registry.get('default')()
685
reference_format = reference_bzrdir_format.get_branch_format()
686
network_name = reference_format.network_name()
687
reference_repo_fmt = reference_bzrdir_format.repository_format
688
reference_repo_name = reference_repo_fmt.network_name()
689
client.add_expected_call(
690
'BzrDir.create_branch', ('quack/', network_name),
691
'success', ('ok', network_name, '', 'no', 'no', 'yes',
692
reference_repo_name))
693
a_bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
695
branch = a_bzrdir.create_branch()
696
# We should have got a remote branch
697
self.assertIsInstance(branch, remote.RemoteBranch)
698
# its format should have the settings from the response
699
format = branch._format
700
self.assertEqual(network_name, format.network_name())
703
class TestBzrDirCreateRepository(TestRemote):
705
def test_backwards_compat(self):
706
self.setup_smart_server_with_call_log()
707
bzrdir = self.make_bzrdir('.')
708
self.reset_smart_call_log()
709
self.disable_verb('BzrDir.create_repository')
710
repo = bzrdir.create_repository()
711
create_repo_call_count = len([call for call in self.hpss_calls if
712
call.call.method == 'BzrDir.create_repository'])
713
self.assertEqual(1, create_repo_call_count)
715
def test_current_server(self):
716
transport = self.get_transport('.')
717
transport = transport.clone('quack')
718
self.make_bzrdir('quack')
719
client = FakeClient(transport.base)
720
reference_bzrdir_format = bzrdir.format_registry.get('default')()
721
reference_format = reference_bzrdir_format.repository_format
722
network_name = reference_format.network_name()
723
client.add_expected_call(
724
'BzrDir.create_repository', ('quack/',
725
'Bazaar repository format 2a (needs bzr 1.16 or later)\n',
727
'success', ('ok', 'yes', 'yes', 'yes', network_name))
728
a_bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
730
repo = a_bzrdir.create_repository()
731
# We should have got a remote repository
732
self.assertIsInstance(repo, remote.RemoteRepository)
733
# its format should have the settings from the response
734
format = repo._format
735
self.assertTrue(format.rich_root_data)
736
self.assertTrue(format.supports_tree_reference)
737
self.assertTrue(format.supports_external_lookups)
738
self.assertEqual(network_name, format.network_name())
741
class TestBzrDirOpenRepository(TestRemote):
743
def test_backwards_compat_1_2_3(self):
744
# fallback all the way to the first version.
745
reference_format = self.get_repo_format()
746
network_name = reference_format.network_name()
747
server_url = 'bzr://example.com/'
748
self.permit_url(server_url)
749
client = FakeClient(server_url)
750
client.add_unknown_method_response('BzrDir.find_repositoryV3')
751
client.add_unknown_method_response('BzrDir.find_repositoryV2')
472
752
client.add_success_response('ok', '', 'no', 'no')
473
bzrdir = RemoteBzrDir(transport, _client=client)
474
repo = bzrdir.open_repository()
476
[('call', 'BzrDir.find_repositoryV2', ('quack/',)),
477
('call', 'BzrDir.find_repository', ('quack/',))],
753
# A real repository instance will be created to determine the network
755
client.add_success_response_with_body(
756
"Bazaar-NG meta directory, format 1\n", 'ok')
757
client.add_success_response_with_body(
758
reference_format.get_format_string(), 'ok')
759
# PackRepository wants to do a stat
760
client.add_success_response('stat', '0', '65535')
761
remote_transport = RemoteTransport(server_url + 'quack/', medium=False,
763
bzrdir = RemoteBzrDir(remote_transport, remote.RemoteBzrDirFormat(),
765
repo = bzrdir.open_repository()
767
[('call', 'BzrDir.find_repositoryV3', ('quack/',)),
768
('call', 'BzrDir.find_repositoryV2', ('quack/',)),
769
('call', 'BzrDir.find_repository', ('quack/',)),
770
('call_expecting_body', 'get', ('/quack/.bzr/branch-format',)),
771
('call_expecting_body', 'get', ('/quack/.bzr/repository/format',)),
772
('call', 'stat', ('/quack/.bzr/repository',)),
775
self.assertEqual(network_name, repo._format.network_name())
777
def test_backwards_compat_2(self):
778
# fallback to find_repositoryV2
779
reference_format = self.get_repo_format()
780
network_name = reference_format.network_name()
781
server_url = 'bzr://example.com/'
782
self.permit_url(server_url)
783
client = FakeClient(server_url)
784
client.add_unknown_method_response('BzrDir.find_repositoryV3')
785
client.add_success_response('ok', '', 'no', 'no', 'no')
786
# A real repository instance will be created to determine the network
788
client.add_success_response_with_body(
789
"Bazaar-NG meta directory, format 1\n", 'ok')
790
client.add_success_response_with_body(
791
reference_format.get_format_string(), 'ok')
792
# PackRepository wants to do a stat
793
client.add_success_response('stat', '0', '65535')
794
remote_transport = RemoteTransport(server_url + 'quack/', medium=False,
796
bzrdir = RemoteBzrDir(remote_transport, remote.RemoteBzrDirFormat(),
798
repo = bzrdir.open_repository()
800
[('call', 'BzrDir.find_repositoryV3', ('quack/',)),
801
('call', 'BzrDir.find_repositoryV2', ('quack/',)),
802
('call_expecting_body', 'get', ('/quack/.bzr/branch-format',)),
803
('call_expecting_body', 'get', ('/quack/.bzr/repository/format',)),
804
('call', 'stat', ('/quack/.bzr/repository',)),
807
self.assertEqual(network_name, repo._format.network_name())
809
def test_current_server(self):
810
reference_format = self.get_repo_format()
811
network_name = reference_format.network_name()
812
transport = MemoryTransport()
813
transport.mkdir('quack')
814
transport = transport.clone('quack')
815
client = FakeClient(transport.base)
816
client.add_success_response('ok', '', 'no', 'no', 'no', network_name)
817
bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
819
repo = bzrdir.open_repository()
821
[('call', 'BzrDir.find_repositoryV3', ('quack/',))],
823
self.assertEqual(network_name, repo._format.network_name())
826
class TestBzrDirFormatInitializeEx(TestRemote):
828
def test_success(self):
829
"""Simple test for typical successful call."""
830
fmt = bzrdir.RemoteBzrDirFormat()
831
default_format_name = BzrDirFormat.get_default_format().network_name()
832
transport = self.get_transport()
833
client = FakeClient(transport.base)
834
client.add_expected_call(
835
'BzrDirFormat.initialize_ex_1.16',
836
(default_format_name, 'path', 'False', 'False', 'False', '',
837
'', '', '', 'False'),
839
('.', 'no', 'no', 'yes', 'repo fmt', 'repo bzrdir fmt',
840
'bzrdir fmt', 'False', '', '', 'repo lock token'))
841
# XXX: It would be better to call fmt.initialize_on_transport_ex, but
842
# it's currently hard to test that without supplying a real remote
843
# transport connected to a real server.
844
result = fmt._initialize_on_transport_ex_rpc(client, 'path',
845
transport, False, False, False, None, None, None, None, False)
846
self.assertFinished(client)
848
def test_error(self):
849
"""Error responses are translated, e.g. 'PermissionDenied' raises the
850
corresponding error from the client.
852
fmt = bzrdir.RemoteBzrDirFormat()
853
default_format_name = BzrDirFormat.get_default_format().network_name()
854
transport = self.get_transport()
855
client = FakeClient(transport.base)
856
client.add_expected_call(
857
'BzrDirFormat.initialize_ex_1.16',
858
(default_format_name, 'path', 'False', 'False', 'False', '',
859
'', '', '', 'False'),
861
('PermissionDenied', 'path', 'extra info'))
862
# XXX: It would be better to call fmt.initialize_on_transport_ex, but
863
# it's currently hard to test that without supplying a real remote
864
# transport connected to a real server.
865
err = self.assertRaises(errors.PermissionDenied,
866
fmt._initialize_on_transport_ex_rpc, client, 'path', transport,
867
False, False, False, None, None, None, None, False)
868
self.assertEqual('path', err.path)
869
self.assertEqual(': extra info', err.extra)
870
self.assertFinished(client)
872
def test_error_from_real_server(self):
873
"""Integration test for error translation."""
874
transport = self.make_smart_server('foo')
875
transport = transport.clone('no-such-path')
876
fmt = bzrdir.RemoteBzrDirFormat()
877
err = self.assertRaises(errors.NoSuchFile,
878
fmt.initialize_on_transport_ex, transport, create_prefix=False)
481
881
class OldSmartClient(object):
506
906
return OldSmartClient()
509
class RemoteBranchTestCase(tests.TestCase):
909
class RemoteBzrDirTestCase(TestRemote):
911
def make_remote_bzrdir(self, transport, client):
912
"""Make a RemotebzrDir using 'client' as the _client."""
913
return RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
917
class RemoteBranchTestCase(RemoteBzrDirTestCase):
919
def lock_remote_branch(self, branch):
920
"""Trick a RemoteBranch into thinking it is locked."""
921
branch._lock_mode = 'w'
922
branch._lock_count = 2
923
branch._lock_token = 'branch token'
924
branch._repo_lock_token = 'repo token'
925
branch.repository._lock_mode = 'w'
926
branch.repository._lock_count = 2
927
branch.repository._lock_token = 'repo token'
511
929
def make_remote_branch(self, transport, client):
512
930
"""Make a RemoteBranch using 'client' as its _SmartClient.
514
932
A RemoteBzrDir and RemoteRepository will also be created to fill out
515
933
the RemoteBranch, albeit with stub values for some of their attributes.
517
935
# we do not want bzrdir to make any remote calls, so use False as its
518
936
# _client. If it tries to make a remote call, this will fail
520
bzrdir = RemoteBzrDir(transport, _client=False)
938
bzrdir = self.make_remote_bzrdir(transport, False)
521
939
repo = RemoteRepository(bzrdir, None, _client=client)
522
return RemoteBranch(bzrdir, repo, _client=client)
940
branch_format = self.get_branch_format()
941
format = RemoteBranchFormat(network_name=branch_format.network_name())
942
return RemoteBranch(bzrdir, repo, _client=client, format=format)
945
class TestBranchGetParent(RemoteBranchTestCase):
947
def test_no_parent(self):
948
# in an empty branch we decode the response properly
949
transport = MemoryTransport()
950
client = FakeClient(transport.base)
951
client.add_expected_call(
952
'Branch.get_stacked_on_url', ('quack/',),
953
'error', ('NotStacked',))
954
client.add_expected_call(
955
'Branch.get_parent', ('quack/',),
957
transport.mkdir('quack')
958
transport = transport.clone('quack')
959
branch = self.make_remote_branch(transport, client)
960
result = branch.get_parent()
961
self.assertFinished(client)
962
self.assertEqual(None, result)
964
def test_parent_relative(self):
965
transport = MemoryTransport()
966
client = FakeClient(transport.base)
967
client.add_expected_call(
968
'Branch.get_stacked_on_url', ('kwaak/',),
969
'error', ('NotStacked',))
970
client.add_expected_call(
971
'Branch.get_parent', ('kwaak/',),
972
'success', ('../foo/',))
973
transport.mkdir('kwaak')
974
transport = transport.clone('kwaak')
975
branch = self.make_remote_branch(transport, client)
976
result = branch.get_parent()
977
self.assertEqual(transport.clone('../foo').base, result)
979
def test_parent_absolute(self):
980
transport = MemoryTransport()
981
client = FakeClient(transport.base)
982
client.add_expected_call(
983
'Branch.get_stacked_on_url', ('kwaak/',),
984
'error', ('NotStacked',))
985
client.add_expected_call(
986
'Branch.get_parent', ('kwaak/',),
987
'success', ('http://foo/',))
988
transport.mkdir('kwaak')
989
transport = transport.clone('kwaak')
990
branch = self.make_remote_branch(transport, client)
991
result = branch.get_parent()
992
self.assertEqual('http://foo/', result)
993
self.assertFinished(client)
996
class TestBranchSetParentLocation(RemoteBranchTestCase):
998
def test_no_parent(self):
999
# We call the verb when setting parent to None
1000
transport = MemoryTransport()
1001
client = FakeClient(transport.base)
1002
client.add_expected_call(
1003
'Branch.get_stacked_on_url', ('quack/',),
1004
'error', ('NotStacked',))
1005
client.add_expected_call(
1006
'Branch.set_parent_location', ('quack/', 'b', 'r', ''),
1008
transport.mkdir('quack')
1009
transport = transport.clone('quack')
1010
branch = self.make_remote_branch(transport, client)
1011
branch._lock_token = 'b'
1012
branch._repo_lock_token = 'r'
1013
branch._set_parent_location(None)
1014
self.assertFinished(client)
1016
def test_parent(self):
1017
transport = MemoryTransport()
1018
client = FakeClient(transport.base)
1019
client.add_expected_call(
1020
'Branch.get_stacked_on_url', ('kwaak/',),
1021
'error', ('NotStacked',))
1022
client.add_expected_call(
1023
'Branch.set_parent_location', ('kwaak/', 'b', 'r', 'foo'),
1025
transport.mkdir('kwaak')
1026
transport = transport.clone('kwaak')
1027
branch = self.make_remote_branch(transport, client)
1028
branch._lock_token = 'b'
1029
branch._repo_lock_token = 'r'
1030
branch._set_parent_location('foo')
1031
self.assertFinished(client)
1033
def test_backwards_compat(self):
1034
self.setup_smart_server_with_call_log()
1035
branch = self.make_branch('.')
1036
self.reset_smart_call_log()
1037
verb = 'Branch.set_parent_location'
1038
self.disable_verb(verb)
1039
branch.set_parent('http://foo/')
1040
self.assertLength(12, self.hpss_calls)
1043
class TestBranchGetTagsBytes(RemoteBranchTestCase):
1045
def test_backwards_compat(self):
1046
self.setup_smart_server_with_call_log()
1047
branch = self.make_branch('.')
1048
self.reset_smart_call_log()
1049
verb = 'Branch.get_tags_bytes'
1050
self.disable_verb(verb)
1051
branch.tags.get_tag_dict()
1052
call_count = len([call for call in self.hpss_calls if
1053
call.call.method == verb])
1054
self.assertEqual(1, call_count)
1056
def test_trivial(self):
1057
transport = MemoryTransport()
1058
client = FakeClient(transport.base)
1059
client.add_expected_call(
1060
'Branch.get_stacked_on_url', ('quack/',),
1061
'error', ('NotStacked',))
1062
client.add_expected_call(
1063
'Branch.get_tags_bytes', ('quack/',),
1065
transport.mkdir('quack')
1066
transport = transport.clone('quack')
1067
branch = self.make_remote_branch(transport, client)
1068
result = branch.tags.get_tag_dict()
1069
self.assertFinished(client)
1070
self.assertEqual({}, result)
1073
class TestBranchSetTagsBytes(RemoteBranchTestCase):
1075
def test_trivial(self):
1076
transport = MemoryTransport()
1077
client = FakeClient(transport.base)
1078
client.add_expected_call(
1079
'Branch.get_stacked_on_url', ('quack/',),
1080
'error', ('NotStacked',))
1081
client.add_expected_call(
1082
'Branch.set_tags_bytes', ('quack/', 'branch token', 'repo token'),
1084
transport.mkdir('quack')
1085
transport = transport.clone('quack')
1086
branch = self.make_remote_branch(transport, client)
1087
self.lock_remote_branch(branch)
1088
branch._set_tags_bytes('tags bytes')
1089
self.assertFinished(client)
1090
self.assertEqual('tags bytes', client._calls[-1][-1])
1092
def test_backwards_compatible(self):
1093
transport = MemoryTransport()
1094
client = FakeClient(transport.base)
1095
client.add_expected_call(
1096
'Branch.get_stacked_on_url', ('quack/',),
1097
'error', ('NotStacked',))
1098
client.add_expected_call(
1099
'Branch.set_tags_bytes', ('quack/', 'branch token', 'repo token'),
1100
'unknown', ('Branch.set_tags_bytes',))
1101
transport.mkdir('quack')
1102
transport = transport.clone('quack')
1103
branch = self.make_remote_branch(transport, client)
1104
self.lock_remote_branch(branch)
1105
class StubRealBranch(object):
1108
def _set_tags_bytes(self, bytes):
1109
self.calls.append(('set_tags_bytes', bytes))
1110
real_branch = StubRealBranch()
1111
branch._real_branch = real_branch
1112
branch._set_tags_bytes('tags bytes')
1113
# Call a second time, to exercise the 'remote version already inferred'
1115
branch._set_tags_bytes('tags bytes')
1116
self.assertFinished(client)
1118
[('set_tags_bytes', 'tags bytes')] * 2, real_branch.calls)
525
1121
class TestBranchLastRevisionInfo(RemoteBranchTestCase):
559
1155
self.assertEqual((2, revid), result)
562
class TestBranch_get_stacked_on_url(tests.TestCaseWithMemoryTransport):
1158
class TestBranch_get_stacked_on_url(TestRemote):
563
1159
"""Test Branch._get_stacked_on_url rpc"""
565
1161
def test_get_stacked_on_invalid_url(self):
566
raise tests.KnownFailure('opening a branch requires the server to open the fallback repository')
567
transport = FakeRemoteTransport('fakeremotetransport:///')
1162
# test that asking for a stacked on url the server can't access works.
1163
# This isn't perfect, but then as we're in the same process there
1164
# really isn't anything we can do to be 100% sure that the server
1165
# doesn't just open in - this test probably needs to be rewritten using
1166
# a spawn()ed server.
1167
stacked_branch = self.make_branch('stacked', format='1.9')
1168
memory_branch = self.make_branch('base', format='1.9')
1169
vfs_url = self.get_vfs_only_url('base')
1170
stacked_branch.set_stacked_on_url(vfs_url)
1171
transport = stacked_branch.bzrdir.root_transport
568
1172
client = FakeClient(transport.base)
569
1173
client.add_expected_call(
570
'Branch.get_stacked_on_url', ('.',),
571
'success', ('ok', 'file:///stacked/on'))
572
bzrdir = RemoteBzrDir(transport, _client=client)
573
branch = RemoteBranch(bzrdir, None, _client=client)
1174
'Branch.get_stacked_on_url', ('stacked/',),
1175
'success', ('ok', vfs_url))
1176
# XXX: Multiple calls are bad, this second call documents what is
1178
client.add_expected_call(
1179
'Branch.get_stacked_on_url', ('stacked/',),
1180
'success', ('ok', vfs_url))
1181
bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
1183
repo_fmt = remote.RemoteRepositoryFormat()
1184
repo_fmt._custom_format = stacked_branch.repository._format
1185
branch = RemoteBranch(bzrdir, RemoteRepository(bzrdir, repo_fmt),
574
1187
result = branch.get_stacked_on_url()
576
'file:///stacked/on', result)
1188
self.assertEqual(vfs_url, result)
578
1190
def test_backwards_compatible(self):
579
1191
# like with bzr1.6 with no Branch.get_stacked_on_url rpc
931
1572
self.assertEqual('rejection message', err.msg)
934
class TestBranchControlGetBranchConf(tests.TestCaseWithMemoryTransport):
935
"""Getting the branch configuration should use an abstract method not vfs.
1575
class TestBranchGetSetConfig(RemoteBranchTestCase):
938
1577
def test_get_branch_conf(self):
939
raise tests.KnownFailure('branch.conf is not retrieved by get_config_file')
940
## # We should see that branch.get_config() does a single rpc to get the
941
## # remote configuration file, abstracting away where that is stored on
942
## # the server. However at the moment it always falls back to using the
943
## # vfs, and this would need some changes in config.py.
945
## # in an empty branch we decode the response properly
946
## client = FakeClient([(('ok', ), '# config file body')], self.get_url())
947
## # we need to make a real branch because the remote_branch.control_files
948
## # will trigger _ensure_real.
949
## branch = self.make_branch('quack')
950
## transport = branch.bzrdir.root_transport
951
## # we do not want bzrdir to make any remote calls
952
## bzrdir = RemoteBzrDir(transport, _client=False)
953
## branch = RemoteBranch(bzrdir, None, _client=client)
954
## config = branch.get_config()
956
## [('call_expecting_body', 'Branch.get_config_file', ('quack/',))],
1578
# in an empty branch we decode the response properly
1579
client = FakeClient()
1580
client.add_expected_call(
1581
'Branch.get_stacked_on_url', ('memory:///',),
1582
'error', ('NotStacked',),)
1583
client.add_success_response_with_body('# config file body', 'ok')
1584
transport = MemoryTransport()
1585
branch = self.make_remote_branch(transport, client)
1586
config = branch.get_config()
1587
config.has_explicit_nickname()
1589
[('call', 'Branch.get_stacked_on_url', ('memory:///',)),
1590
('call_expecting_body', 'Branch.get_config_file', ('memory:///',))],
1593
def test_get_multi_line_branch_conf(self):
1594
# Make sure that multiple-line branch.conf files are supported
1596
# https://bugs.edge.launchpad.net/bzr/+bug/354075
1597
client = FakeClient()
1598
client.add_expected_call(
1599
'Branch.get_stacked_on_url', ('memory:///',),
1600
'error', ('NotStacked',),)
1601
client.add_success_response_with_body('a = 1\nb = 2\nc = 3\n', 'ok')
1602
transport = MemoryTransport()
1603
branch = self.make_remote_branch(transport, client)
1604
config = branch.get_config()
1605
self.assertEqual(u'2', config.get_user_option('b'))
1607
def test_set_option(self):
1608
client = FakeClient()
1609
client.add_expected_call(
1610
'Branch.get_stacked_on_url', ('memory:///',),
1611
'error', ('NotStacked',),)
1612
client.add_expected_call(
1613
'Branch.lock_write', ('memory:///', '', ''),
1614
'success', ('ok', 'branch token', 'repo token'))
1615
client.add_expected_call(
1616
'Branch.set_config_option', ('memory:///', 'branch token',
1617
'repo token', 'foo', 'bar', ''),
1619
client.add_expected_call(
1620
'Branch.unlock', ('memory:///', 'branch token', 'repo token'),
1622
transport = MemoryTransport()
1623
branch = self.make_remote_branch(transport, client)
1625
config = branch._get_config()
1626
config.set_option('foo', 'bar')
1628
self.assertFinished(client)
1630
def test_backwards_compat_set_option(self):
1631
self.setup_smart_server_with_call_log()
1632
branch = self.make_branch('.')
1633
verb = 'Branch.set_config_option'
1634
self.disable_verb(verb)
1636
self.addCleanup(branch.unlock)
1637
self.reset_smart_call_log()
1638
branch._get_config().set_option('value', 'name')
1639
self.assertLength(10, self.hpss_calls)
1640
self.assertEqual('value', branch._get_config().get_option('name'))
960
1643
class TestBranchLockWrite(RemoteBranchTestCase):
1208
1983
errors.UnexpectedSmartServerResponse,
1209
1984
repo.get_parent_map, ['a-revision-id'])
1986
def test_get_parent_map_negative_caches_missing_keys(self):
1987
self.setup_smart_server_with_call_log()
1988
repo = self.make_repository('foo')
1989
self.assertIsInstance(repo, RemoteRepository)
1991
self.addCleanup(repo.unlock)
1992
self.reset_smart_call_log()
1993
graph = repo.get_graph()
1994
self.assertEqual({},
1995
graph.get_parent_map(['some-missing', 'other-missing']))
1996
self.assertLength(1, self.hpss_calls)
1997
# No call if we repeat this
1998
self.reset_smart_call_log()
1999
graph = repo.get_graph()
2000
self.assertEqual({},
2001
graph.get_parent_map(['some-missing', 'other-missing']))
2002
self.assertLength(0, self.hpss_calls)
2003
# Asking for more unknown keys makes a request.
2004
self.reset_smart_call_log()
2005
graph = repo.get_graph()
2006
self.assertEqual({},
2007
graph.get_parent_map(['some-missing', 'other-missing',
2009
self.assertLength(1, self.hpss_calls)
2011
def disableExtraResults(self):
2012
self.overrideAttr(SmartServerRepositoryGetParentMap,
2013
'no_extra_results', True)
2015
def test_null_cached_missing_and_stop_key(self):
2016
self.setup_smart_server_with_call_log()
2017
# Make a branch with a single revision.
2018
builder = self.make_branch_builder('foo')
2019
builder.start_series()
2020
builder.build_snapshot('first', None, [
2021
('add', ('', 'root-id', 'directory', ''))])
2022
builder.finish_series()
2023
branch = builder.get_branch()
2024
repo = branch.repository
2025
self.assertIsInstance(repo, RemoteRepository)
2026
# Stop the server from sending extra results.
2027
self.disableExtraResults()
2029
self.addCleanup(repo.unlock)
2030
self.reset_smart_call_log()
2031
graph = repo.get_graph()
2032
# Query for 'first' and 'null:'. Because 'null:' is a parent of
2033
# 'first' it will be a candidate for the stop_keys of subsequent
2034
# requests, and because 'null:' was queried but not returned it will be
2035
# cached as missing.
2036
self.assertEqual({'first': ('null:',)},
2037
graph.get_parent_map(['first', 'null:']))
2038
# Now query for another key. This request will pass along a recipe of
2039
# start and stop keys describing the already cached results, and this
2040
# recipe's revision count must be correct (or else it will trigger an
2041
# error from the server).
2042
self.assertEqual({}, graph.get_parent_map(['another-key']))
2043
# This assertion guards against disableExtraResults silently failing to
2044
# work, thus invalidating the test.
2045
self.assertLength(2, self.hpss_calls)
2047
def test_get_parent_map_gets_ghosts_from_result(self):
2048
# asking for a revision should negatively cache close ghosts in its
2050
self.setup_smart_server_with_call_log()
2051
tree = self.make_branch_and_memory_tree('foo')
2054
builder = treebuilder.TreeBuilder()
2055
builder.start_tree(tree)
2057
builder.finish_tree()
2058
tree.set_parent_ids(['non-existant'], allow_leftmost_as_ghost=True)
2059
rev_id = tree.commit('')
2063
self.addCleanup(tree.unlock)
2064
repo = tree.branch.repository
2065
self.assertIsInstance(repo, RemoteRepository)
2067
repo.get_parent_map([rev_id])
2068
self.reset_smart_call_log()
2069
# Now asking for rev_id's ghost parent should not make calls
2070
self.assertEqual({}, repo.get_parent_map(['non-existant']))
2071
self.assertLength(0, self.hpss_calls)
2074
class TestGetParentMapAllowsNew(tests.TestCaseWithTransport):
2076
def test_allows_new_revisions(self):
2077
"""get_parent_map's results can be updated by commit."""
2078
smart_server = test_server.SmartTCPServer_for_testing()
2079
self.start_server(smart_server)
2080
self.make_branch('branch')
2081
branch = Branch.open(smart_server.get_url() + '/branch')
2082
tree = branch.create_checkout('tree', lightweight=True)
2084
self.addCleanup(tree.unlock)
2085
graph = tree.branch.repository.get_graph()
2086
# This provides an opportunity for the missing rev-id to be cached.
2087
self.assertEqual({}, graph.get_parent_map(['rev1']))
2088
tree.commit('message', rev_id='rev1')
2089
graph = tree.branch.repository.get_graph()
2090
self.assertEqual({'rev1': ('null:',)}, graph.get_parent_map(['rev1']))
1212
2093
class TestRepositoryGetRevisionGraph(TestRemoteRepository):
1214
2095
def test_null_revision(self):
1215
2096
# a null revision has the predictable result {}, we should have no wire
1216
2097
# traffic when calling it with this argument
1217
2098
transport_path = 'empty'
1218
2099
repo, client = self.setup_fake_client_and_repository(transport_path)
1219
2100
client.add_success_response('notused')
1220
result = self.applyDeprecated(one_four, repo.get_revision_graph,
2101
# actual RemoteRepository.get_revision_graph is gone, but there's an
2102
# equivalent private method for testing
2103
result = repo._get_revision_graph(NULL_REVISION)
1222
2104
self.assertEqual([], client._calls)
1223
2105
self.assertEqual({}, result)
1277
2161
repo, client = self.setup_fake_client_and_repository(transport_path)
1278
2162
client.add_error_response('AnUnexpectedError')
1279
2163
e = self.assertRaises(errors.UnknownErrorFromSmartServer,
1280
self.applyDeprecated, one_four, repo.get_revision_graph, revid)
2164
repo._get_revision_graph, revid)
1281
2165
self.assertEqual(('AnUnexpectedError',), e.error_tuple)
2168
class TestRepositoryGetRevIdForRevno(TestRemoteRepository):
2171
repo, client = self.setup_fake_client_and_repository('quack')
2172
client.add_expected_call(
2173
'Repository.get_rev_id_for_revno', ('quack/', 5, (42, 'rev-foo')),
2174
'success', ('ok', 'rev-five'))
2175
result = repo.get_rev_id_for_revno(5, (42, 'rev-foo'))
2176
self.assertEqual((True, 'rev-five'), result)
2177
self.assertFinished(client)
2179
def test_history_incomplete(self):
2180
repo, client = self.setup_fake_client_and_repository('quack')
2181
client.add_expected_call(
2182
'Repository.get_rev_id_for_revno', ('quack/', 5, (42, 'rev-foo')),
2183
'success', ('history-incomplete', 10, 'rev-ten'))
2184
result = repo.get_rev_id_for_revno(5, (42, 'rev-foo'))
2185
self.assertEqual((False, (10, 'rev-ten')), result)
2186
self.assertFinished(client)
2188
def test_history_incomplete_with_fallback(self):
2189
"""A 'history-incomplete' response causes the fallback repository to be
2190
queried too, if one is set.
2192
# Make a repo with a fallback repo, both using a FakeClient.
2193
format = remote.response_tuple_to_repo_format(
2194
('yes', 'no', 'yes', self.get_repo_format().network_name()))
2195
repo, client = self.setup_fake_client_and_repository('quack')
2196
repo._format = format
2197
fallback_repo, ignored = self.setup_fake_client_and_repository(
2199
fallback_repo._client = client
2200
fallback_repo._format = format
2201
repo.add_fallback_repository(fallback_repo)
2202
# First the client should ask the primary repo
2203
client.add_expected_call(
2204
'Repository.get_rev_id_for_revno', ('quack/', 1, (42, 'rev-foo')),
2205
'success', ('history-incomplete', 2, 'rev-two'))
2206
# Then it should ask the fallback, using revno/revid from the
2207
# history-incomplete response as the known revno/revid.
2208
client.add_expected_call(
2209
'Repository.get_rev_id_for_revno',('fallback/', 1, (2, 'rev-two')),
2210
'success', ('ok', 'rev-one'))
2211
result = repo.get_rev_id_for_revno(1, (42, 'rev-foo'))
2212
self.assertEqual((True, 'rev-one'), result)
2213
self.assertFinished(client)
2215
def test_nosuchrevision(self):
2216
# 'nosuchrevision' is returned when the known-revid is not found in the
2217
# remote repo. The client translates that response to NoSuchRevision.
2218
repo, client = self.setup_fake_client_and_repository('quack')
2219
client.add_expected_call(
2220
'Repository.get_rev_id_for_revno', ('quack/', 5, (42, 'rev-foo')),
2221
'error', ('nosuchrevision', 'rev-foo'))
2223
errors.NoSuchRevision,
2224
repo.get_rev_id_for_revno, 5, (42, 'rev-foo'))
2225
self.assertFinished(client)
2227
def test_branch_fallback_locking(self):
2228
"""RemoteBranch.get_rev_id takes a read lock, and tries to call the
2229
get_rev_id_for_revno verb. If the verb is unknown the VFS fallback
2230
will be invoked, which will fail if the repo is unlocked.
2232
self.setup_smart_server_with_call_log()
2233
tree = self.make_branch_and_memory_tree('.')
2235
rev1 = tree.commit('First')
2236
rev2 = tree.commit('Second')
2238
branch = tree.branch
2239
self.assertFalse(branch.is_locked())
2240
self.reset_smart_call_log()
2241
verb = 'Repository.get_rev_id_for_revno'
2242
self.disable_verb(verb)
2243
self.assertEqual(rev1, branch.get_rev_id(1))
2244
self.assertLength(1, [call for call in self.hpss_calls if
2245
call.call.method == verb])
1284
2248
class TestRepositoryIsShared(TestRemoteRepository):
1286
2250
def test_is_shared(self):
1375
2365
self.assertEqual([], client._calls)
2368
class TestRepositoryInsertStreamBase(TestRemoteRepository):
2369
"""Base class for Repository.insert_stream and .insert_stream_1.19
2373
def checkInsertEmptyStream(self, repo, client):
2374
"""Insert an empty stream, checking the result.
2376
This checks that there are no resume_tokens or missing_keys, and that
2377
the client is finished.
2379
sink = repo._get_sink()
2380
fmt = repository.RepositoryFormat.get_default_format()
2381
resume_tokens, missing_keys = sink.insert_stream([], fmt, [])
2382
self.assertEqual([], resume_tokens)
2383
self.assertEqual(set(), missing_keys)
2384
self.assertFinished(client)
2387
class TestRepositoryInsertStream(TestRepositoryInsertStreamBase):
2388
"""Tests for using Repository.insert_stream verb when the _1.19 variant is
2391
This test case is very similar to TestRepositoryInsertStream_1_19.
2395
TestRemoteRepository.setUp(self)
2396
self.disable_verb('Repository.insert_stream_1.19')
2398
def test_unlocked_repo(self):
2399
transport_path = 'quack'
2400
repo, client = self.setup_fake_client_and_repository(transport_path)
2401
client.add_expected_call(
2402
'Repository.insert_stream_1.19', ('quack/', ''),
2403
'unknown', ('Repository.insert_stream_1.19',))
2404
client.add_expected_call(
2405
'Repository.insert_stream', ('quack/', ''),
2407
client.add_expected_call(
2408
'Repository.insert_stream', ('quack/', ''),
2410
self.checkInsertEmptyStream(repo, client)
2412
def test_locked_repo_with_no_lock_token(self):
2413
transport_path = 'quack'
2414
repo, client = self.setup_fake_client_and_repository(transport_path)
2415
client.add_expected_call(
2416
'Repository.lock_write', ('quack/', ''),
2417
'success', ('ok', ''))
2418
client.add_expected_call(
2419
'Repository.insert_stream_1.19', ('quack/', ''),
2420
'unknown', ('Repository.insert_stream_1.19',))
2421
client.add_expected_call(
2422
'Repository.insert_stream', ('quack/', ''),
2424
client.add_expected_call(
2425
'Repository.insert_stream', ('quack/', ''),
2428
self.checkInsertEmptyStream(repo, client)
2430
def test_locked_repo_with_lock_token(self):
2431
transport_path = 'quack'
2432
repo, client = self.setup_fake_client_and_repository(transport_path)
2433
client.add_expected_call(
2434
'Repository.lock_write', ('quack/', ''),
2435
'success', ('ok', 'a token'))
2436
client.add_expected_call(
2437
'Repository.insert_stream_1.19', ('quack/', '', 'a token'),
2438
'unknown', ('Repository.insert_stream_1.19',))
2439
client.add_expected_call(
2440
'Repository.insert_stream_locked', ('quack/', '', 'a token'),
2442
client.add_expected_call(
2443
'Repository.insert_stream_locked', ('quack/', '', 'a token'),
2446
self.checkInsertEmptyStream(repo, client)
2448
def test_stream_with_inventory_deltas(self):
2449
"""'inventory-deltas' substreams cannot be sent to the
2450
Repository.insert_stream verb, because not all servers that implement
2451
that verb will accept them. So when one is encountered the RemoteSink
2452
immediately stops using that verb and falls back to VFS insert_stream.
2454
transport_path = 'quack'
2455
repo, client = self.setup_fake_client_and_repository(transport_path)
2456
client.add_expected_call(
2457
'Repository.insert_stream_1.19', ('quack/', ''),
2458
'unknown', ('Repository.insert_stream_1.19',))
2459
client.add_expected_call(
2460
'Repository.insert_stream', ('quack/', ''),
2462
client.add_expected_call(
2463
'Repository.insert_stream', ('quack/', ''),
2465
# Create a fake real repository for insert_stream to fall back on, so
2466
# that we can directly see the records the RemoteSink passes to the
2471
def insert_stream(self, stream, src_format, resume_tokens):
2472
for substream_kind, substream in stream:
2473
self.records.append(
2474
(substream_kind, [record.key for record in substream]))
2475
return ['fake tokens'], ['fake missing keys']
2476
fake_real_sink = FakeRealSink()
2477
class FakeRealRepository:
2478
def _get_sink(self):
2479
return fake_real_sink
2480
def is_in_write_group(self):
2482
def refresh_data(self):
2484
repo._real_repository = FakeRealRepository()
2485
sink = repo._get_sink()
2486
fmt = repository.RepositoryFormat.get_default_format()
2487
stream = self.make_stream_with_inv_deltas(fmt)
2488
resume_tokens, missing_keys = sink.insert_stream(stream, fmt, [])
2489
# Every record from the first inventory delta should have been sent to
2491
expected_records = [
2492
('inventory-deltas', [('rev2',), ('rev3',)]),
2493
('texts', [('some-rev', 'some-file')])]
2494
self.assertEqual(expected_records, fake_real_sink.records)
2495
# The return values from the real sink's insert_stream are propagated
2496
# back to the original caller.
2497
self.assertEqual(['fake tokens'], resume_tokens)
2498
self.assertEqual(['fake missing keys'], missing_keys)
2499
self.assertFinished(client)
2501
def make_stream_with_inv_deltas(self, fmt):
2502
"""Make a simple stream with an inventory delta followed by more
2503
records and more substreams to test that all records and substreams
2504
from that point on are used.
2506
This sends, in order:
2507
* inventories substream: rev1, rev2, rev3. rev2 and rev3 are
2509
* texts substream: (some-rev, some-file)
2511
# Define a stream using generators so that it isn't rewindable.
2512
inv = inventory.Inventory(revision_id='rev1')
2513
inv.root.revision = 'rev1'
2514
def stream_with_inv_delta():
2515
yield ('inventories', inventories_substream())
2516
yield ('inventory-deltas', inventory_delta_substream())
2518
versionedfile.FulltextContentFactory(
2519
('some-rev', 'some-file'), (), None, 'content')])
2520
def inventories_substream():
2521
# An empty inventory fulltext. This will be streamed normally.
2522
text = fmt._serializer.write_inventory_to_string(inv)
2523
yield versionedfile.FulltextContentFactory(
2524
('rev1',), (), None, text)
2525
def inventory_delta_substream():
2526
# An inventory delta. This can't be streamed via this verb, so it
2527
# will trigger a fallback to VFS insert_stream.
2528
entry = inv.make_entry(
2529
'directory', 'newdir', inv.root.file_id, 'newdir-id')
2530
entry.revision = 'ghost'
2531
delta = [(None, 'newdir', 'newdir-id', entry)]
2532
serializer = inventory_delta.InventoryDeltaSerializer(
2533
versioned_root=True, tree_references=False)
2534
lines = serializer.delta_to_lines('rev1', 'rev2', delta)
2535
yield versionedfile.ChunkedContentFactory(
2536
('rev2',), (('rev1',)), None, lines)
2538
lines = serializer.delta_to_lines('rev1', 'rev3', delta)
2539
yield versionedfile.ChunkedContentFactory(
2540
('rev3',), (('rev1',)), None, lines)
2541
return stream_with_inv_delta()
2544
class TestRepositoryInsertStream_1_19(TestRepositoryInsertStreamBase):
2546
def test_unlocked_repo(self):
2547
transport_path = 'quack'
2548
repo, client = self.setup_fake_client_and_repository(transport_path)
2549
client.add_expected_call(
2550
'Repository.insert_stream_1.19', ('quack/', ''),
2552
client.add_expected_call(
2553
'Repository.insert_stream_1.19', ('quack/', ''),
2555
self.checkInsertEmptyStream(repo, client)
2557
def test_locked_repo_with_no_lock_token(self):
2558
transport_path = 'quack'
2559
repo, client = self.setup_fake_client_and_repository(transport_path)
2560
client.add_expected_call(
2561
'Repository.lock_write', ('quack/', ''),
2562
'success', ('ok', ''))
2563
client.add_expected_call(
2564
'Repository.insert_stream_1.19', ('quack/', ''),
2566
client.add_expected_call(
2567
'Repository.insert_stream_1.19', ('quack/', ''),
2570
self.checkInsertEmptyStream(repo, client)
2572
def test_locked_repo_with_lock_token(self):
2573
transport_path = 'quack'
2574
repo, client = self.setup_fake_client_and_repository(transport_path)
2575
client.add_expected_call(
2576
'Repository.lock_write', ('quack/', ''),
2577
'success', ('ok', 'a token'))
2578
client.add_expected_call(
2579
'Repository.insert_stream_1.19', ('quack/', '', 'a token'),
2581
client.add_expected_call(
2582
'Repository.insert_stream_1.19', ('quack/', '', 'a token'),
2585
self.checkInsertEmptyStream(repo, client)
1378
2588
class TestRepositoryTarball(TestRemoteRepository):
1380
2590
# This is a canned tarball reponse we can validate against
1429
2639
src_repo.copy_content_into(dest_repo)
2642
class _StubRealPackRepository(object):
2644
def __init__(self, calls):
2646
self._pack_collection = _StubPackCollection(calls)
2648
def is_in_write_group(self):
2651
def refresh_data(self):
2652
self.calls.append(('pack collection reload_pack_names',))
2655
class _StubPackCollection(object):
2657
def __init__(self, calls):
2661
self.calls.append(('pack collection autopack',))
2664
class TestRemotePackRepositoryAutoPack(TestRemoteRepository):
2665
"""Tests for RemoteRepository.autopack implementation."""
2668
"""When the server returns 'ok' and there's no _real_repository, then
2669
nothing else happens: the autopack method is done.
2671
transport_path = 'quack'
2672
repo, client = self.setup_fake_client_and_repository(transport_path)
2673
client.add_expected_call(
2674
'PackRepository.autopack', ('quack/',), 'success', ('ok',))
2676
self.assertFinished(client)
2678
def test_ok_with_real_repo(self):
2679
"""When the server returns 'ok' and there is a _real_repository, then
2680
the _real_repository's reload_pack_name's method will be called.
2682
transport_path = 'quack'
2683
repo, client = self.setup_fake_client_and_repository(transport_path)
2684
client.add_expected_call(
2685
'PackRepository.autopack', ('quack/',),
2687
repo._real_repository = _StubRealPackRepository(client._calls)
2690
[('call', 'PackRepository.autopack', ('quack/',)),
2691
('pack collection reload_pack_names',)],
2694
def test_backwards_compatibility(self):
2695
"""If the server does not recognise the PackRepository.autopack verb,
2696
fallback to the real_repository's implementation.
2698
transport_path = 'quack'
2699
repo, client = self.setup_fake_client_and_repository(transport_path)
2700
client.add_unknown_method_response('PackRepository.autopack')
2701
def stub_ensure_real():
2702
client._calls.append(('_ensure_real',))
2703
repo._real_repository = _StubRealPackRepository(client._calls)
2704
repo._ensure_real = stub_ensure_real
2707
[('call', 'PackRepository.autopack', ('quack/',)),
2709
('pack collection autopack',)],
1432
2713
class TestErrorTranslationBase(tests.TestCaseWithMemoryTransport):
1433
2714
"""Base class for unit tests for bzrlib.remote._translate_error."""
1530
2820
expected_error = errors.DivergedBranches(branch, other_branch)
1531
2821
self.assertEqual(expected_error, translated_error)
2823
def test_ReadError_no_args(self):
2825
translated_error = self.translateTuple(('ReadError',), path=path)
2826
expected_error = errors.ReadError(path)
2827
self.assertEqual(expected_error, translated_error)
2829
def test_ReadError(self):
2831
translated_error = self.translateTuple(('ReadError', path))
2832
expected_error = errors.ReadError(path)
2833
self.assertEqual(expected_error, translated_error)
2835
def test_IncompatibleRepositories(self):
2836
translated_error = self.translateTuple(('IncompatibleRepositories',
2837
"repo1", "repo2", "details here"))
2838
expected_error = errors.IncompatibleRepositories("repo1", "repo2",
2840
self.assertEqual(expected_error, translated_error)
2842
def test_PermissionDenied_no_args(self):
2844
translated_error = self.translateTuple(('PermissionDenied',), path=path)
2845
expected_error = errors.PermissionDenied(path)
2846
self.assertEqual(expected_error, translated_error)
2848
def test_PermissionDenied_one_arg(self):
2850
translated_error = self.translateTuple(('PermissionDenied', path))
2851
expected_error = errors.PermissionDenied(path)
2852
self.assertEqual(expected_error, translated_error)
2854
def test_PermissionDenied_one_arg_and_context(self):
2855
"""Given a choice between a path from the local context and a path on
2856
the wire, _translate_error prefers the path from the local context.
2858
local_path = 'local path'
2859
remote_path = 'remote path'
2860
translated_error = self.translateTuple(
2861
('PermissionDenied', remote_path), path=local_path)
2862
expected_error = errors.PermissionDenied(local_path)
2863
self.assertEqual(expected_error, translated_error)
2865
def test_PermissionDenied_two_args(self):
2867
extra = 'a string with extra info'
2868
translated_error = self.translateTuple(
2869
('PermissionDenied', path, extra))
2870
expected_error = errors.PermissionDenied(path, extra)
2871
self.assertEqual(expected_error, translated_error)
1534
2874
class TestErrorTranslationRobustness(TestErrorTranslationBase):
1535
2875
"""Unit tests for bzrlib.remote._translate_error's robustness.
1537
2877
TestErrorTranslationSuccess is for cases where _translate_error can
1538
2878
translate successfully. This class about how _translate_err behaves when
1539
2879
it fails to translate: it re-raises the original error.
1614
2966
remote_repo.unlock()
2968
def prepare_stacked_remote_branch(self):
2969
"""Get stacked_upon and stacked branches with content in each."""
2970
self.setup_smart_server_with_call_log()
2971
tree1 = self.make_branch_and_tree('tree1', format='1.9')
2972
tree1.commit('rev1', rev_id='rev1')
2973
tree2 = tree1.branch.bzrdir.sprout('tree2', stacked=True
2974
).open_workingtree()
2975
local_tree = tree2.branch.create_checkout('local')
2976
local_tree.commit('local changes make me feel good.')
2977
branch2 = Branch.open(self.get_url('tree2'))
2979
self.addCleanup(branch2.unlock)
2980
return tree1.branch, branch2
2982
def test_stacked_get_parent_map(self):
2983
# the public implementation of get_parent_map obeys stacking
2984
_, branch = self.prepare_stacked_remote_branch()
2985
repo = branch.repository
2986
self.assertEqual(['rev1'], repo.get_parent_map(['rev1']).keys())
2988
def test_unstacked_get_parent_map(self):
2989
# _unstacked_provider.get_parent_map ignores stacking
2990
_, branch = self.prepare_stacked_remote_branch()
2991
provider = branch.repository._unstacked_provider
2992
self.assertEqual([], provider.get_parent_map(['rev1']).keys())
2994
def fetch_stream_to_rev_order(self, stream):
2996
for kind, substream in stream:
2997
if not kind == 'revisions':
3000
for content in substream:
3001
result.append(content.key[-1])
3004
def get_ordered_revs(self, format, order, branch_factory=None):
3005
"""Get a list of the revisions in a stream to format format.
3007
:param format: The format of the target.
3008
:param order: the order that target should have requested.
3009
:param branch_factory: A callable to create a trunk and stacked branch
3010
to fetch from. If none, self.prepare_stacked_remote_branch is used.
3011
:result: The revision ids in the stream, in the order seen,
3012
the topological order of revisions in the source.
3014
unordered_format = bzrdir.format_registry.get(format)()
3015
target_repository_format = unordered_format.repository_format
3017
self.assertEqual(order, target_repository_format._fetch_order)
3018
if branch_factory is None:
3019
branch_factory = self.prepare_stacked_remote_branch
3020
_, stacked = branch_factory()
3021
source = stacked.repository._get_source(target_repository_format)
3022
tip = stacked.last_revision()
3023
revs = stacked.repository.get_ancestry(tip)
3024
search = graph.PendingAncestryResult([tip], stacked.repository)
3025
self.reset_smart_call_log()
3026
stream = source.get_stream(search)
3029
# We trust that if a revision is in the stream the rest of the new
3030
# content for it is too, as per our main fetch tests; here we are
3031
# checking that the revisions are actually included at all, and their
3033
return self.fetch_stream_to_rev_order(stream), revs
3035
def test_stacked_get_stream_unordered(self):
3036
# Repository._get_source.get_stream() from a stacked repository with
3037
# unordered yields the full data from both stacked and stacked upon
3039
rev_ord, expected_revs = self.get_ordered_revs('1.9', 'unordered')
3040
self.assertEqual(set(expected_revs), set(rev_ord))
3041
# Getting unordered results should have made a streaming data request
3042
# from the server, then one from the backing branch.
3043
self.assertLength(2, self.hpss_calls)
3045
def test_stacked_on_stacked_get_stream_unordered(self):
3046
# Repository._get_source.get_stream() from a stacked repository which
3047
# is itself stacked yields the full data from all three sources.
3048
def make_stacked_stacked():
3049
_, stacked = self.prepare_stacked_remote_branch()
3050
tree = stacked.bzrdir.sprout('tree3', stacked=True
3051
).open_workingtree()
3052
local_tree = tree.branch.create_checkout('local-tree3')
3053
local_tree.commit('more local changes are better')
3054
branch = Branch.open(self.get_url('tree3'))
3056
self.addCleanup(branch.unlock)
3058
rev_ord, expected_revs = self.get_ordered_revs('1.9', 'unordered',
3059
branch_factory=make_stacked_stacked)
3060
self.assertEqual(set(expected_revs), set(rev_ord))
3061
# Getting unordered results should have made a streaming data request
3062
# from the server, and one from each backing repo
3063
self.assertLength(3, self.hpss_calls)
3065
def test_stacked_get_stream_topological(self):
3066
# Repository._get_source.get_stream() from a stacked repository with
3067
# topological sorting yields the full data from both stacked and
3068
# stacked upon sources in topological order.
3069
rev_ord, expected_revs = self.get_ordered_revs('knit', 'topological')
3070
self.assertEqual(expected_revs, rev_ord)
3071
# Getting topological sort requires VFS calls still - one of which is
3072
# pushing up from the bound branch.
3073
self.assertLength(13, self.hpss_calls)
3075
def test_stacked_get_stream_groupcompress(self):
3076
# Repository._get_source.get_stream() from a stacked repository with
3077
# groupcompress sorting yields the full data from both stacked and
3078
# stacked upon sources in groupcompress order.
3079
raise tests.TestSkipped('No groupcompress ordered format available')
3080
rev_ord, expected_revs = self.get_ordered_revs('dev5', 'groupcompress')
3081
self.assertEqual(expected_revs, reversed(rev_ord))
3082
# Getting unordered results should have made a streaming data request
3083
# from the backing branch, and one from the stacked on branch.
3084
self.assertLength(2, self.hpss_calls)
3086
def test_stacked_pull_more_than_stacking_has_bug_360791(self):
3087
# When pulling some fixed amount of content that is more than the
3088
# source has (because some is coming from a fallback branch, no error
3089
# should be received. This was reported as bug 360791.
3090
# Need three branches: a trunk, a stacked branch, and a preexisting
3091
# branch pulling content from stacked and trunk.
3092
self.setup_smart_server_with_call_log()
3093
trunk = self.make_branch_and_tree('trunk', format="1.9-rich-root")
3094
r1 = trunk.commit('start')
3095
stacked_branch = trunk.branch.create_clone_on_transport(
3096
self.get_transport('stacked'), stacked_on=trunk.branch.base)
3097
local = self.make_branch('local', format='1.9-rich-root')
3098
local.repository.fetch(stacked_branch.repository,
3099
stacked_branch.last_revision())
3102
class TestRemoteBranchEffort(tests.TestCaseWithTransport):
3105
super(TestRemoteBranchEffort, self).setUp()
3106
# Create a smart server that publishes whatever the backing VFS server
3108
self.smart_server = test_server.SmartTCPServer_for_testing()
3109
self.start_server(self.smart_server, self.get_server())
3110
# Log all HPSS calls into self.hpss_calls.
3111
_SmartClient.hooks.install_named_hook(
3112
'call', self.capture_hpss_call, None)
3113
self.hpss_calls = []
3115
def capture_hpss_call(self, params):
3116
self.hpss_calls.append(params.method)
3118
def test_copy_content_into_avoids_revision_history(self):
3119
local = self.make_branch('local')
3120
remote_backing_tree = self.make_branch_and_tree('remote')
3121
remote_backing_tree.commit("Commit.")
3122
remote_branch_url = self.smart_server.get_url() + 'remote'
3123
remote_branch = bzrdir.BzrDir.open(remote_branch_url).open_branch()
3124
local.repository.fetch(remote_branch.repository)
3125
self.hpss_calls = []
3126
remote_branch.copy_content_into(local)
3127
self.assertFalse('Branch.revision_history' in self.hpss_calls)