112
135
b = BzrDir.open_from_transport(self.transport).open_branch()
113
136
self.assertStartsWith(str(b), 'RemoteBranch(')
116
class FakeRemoteTransport(object):
117
"""This class provides the minimum support for use in place of a RemoteTransport.
119
It doesn't actually transmit requests, but rather expects them to be
120
handled by a FakeClient which holds canned responses. It does not allow
121
any vfs access, therefore is not suitable for testing any operation that
122
will fallback to vfs access. Backing the test by an instance of this
123
class guarantees that it's - done using non-vfs operations.
126
_default_url = 'fakeremotetransport://host/path/'
128
def __init__(self, url=None):
130
url = self._default_url
134
return "%r(%r)" % (self.__class__.__name__,
137
def clone(self, relpath):
138
return FakeRemoteTransport(urlutils.join(self.base, relpath))
140
def get(self, relpath):
141
# only get is specifically stubbed out, because it's usually the first
142
# thing we do. anything else will fail with an AttributeError.
143
raise AssertionError("%r doesn't support file access to %r"
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)
148
182
class FakeProtocol(object):
359
423
AssertionError, client_medium._remember_remote_is_before, (1, 9))
362
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)
364
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()
365
550
transport = MemoryTransport()
366
551
transport.mkdir('quack')
367
552
transport = transport.clone('quack')
368
553
client = FakeClient(transport.base)
369
554
client.add_expected_call(
370
'BzrDir.open_branch', ('quack/',),
371
'success', ('ok', ''))
555
'BzrDir.open_branchV3', ('quack/',),
556
'success', ('branch', branch_network_name))
372
557
client.add_expected_call(
373
'BzrDir.find_repositoryV2', ('quack/',),
374
'success', ('ok', '', 'no', 'no', 'no'))
558
'BzrDir.find_repositoryV3', ('quack/',),
559
'success', ('ok', '', 'no', 'no', 'no', network_name))
375
560
client.add_expected_call(
376
561
'Branch.get_stacked_on_url', ('quack/',),
377
562
'error', ('NotStacked',))
378
bzrdir = RemoteBzrDir(transport, _client=client)
563
bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
379
565
result = bzrdir.open_branch()
380
566
self.assertIsInstance(result, RemoteBranch)
381
567
self.assertEqual(bzrdir, result.bzrdir)
382
client.finished_test()
568
self.assertFinished(client)
384
570
def test_branch_missing(self):
385
571
transport = MemoryTransport()
468
664
RemoteBzrDirFormat.probe_transport, OldServerTransport())
471
class TestBzrDirOpenRepository(tests.TestCase):
473
def test_backwards_compat_1_2(self):
474
transport = MemoryTransport()
475
transport.mkdir('quack')
476
transport = transport.clone('quack')
477
client = FakeClient(transport.base)
478
client.add_unknown_method_response('RemoteRepository.find_repositoryV2')
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')
479
752
client.add_success_response('ok', '', 'no', 'no')
480
bzrdir = RemoteBzrDir(transport, _client=client)
481
repo = bzrdir.open_repository()
483
[('call', 'BzrDir.find_repositoryV2', ('quack/',)),
484
('call', 'BzrDir.find_repository', ('quack/',))],
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)
488
881
class OldSmartClient(object):
513
906
return OldSmartClient()
516
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'
518
929
def make_remote_branch(self, transport, client):
519
930
"""Make a RemoteBranch using 'client' as its _SmartClient.
521
932
A RemoteBzrDir and RemoteRepository will also be created to fill out
522
933
the RemoteBranch, albeit with stub values for some of their attributes.
524
935
# we do not want bzrdir to make any remote calls, so use False as its
525
936
# _client. If it tries to make a remote call, this will fail
527
bzrdir = RemoteBzrDir(transport, _client=False)
938
bzrdir = self.make_remote_bzrdir(transport, False)
528
939
repo = RemoteRepository(bzrdir, None, _client=client)
529
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)
532
1121
class TestBranchLastRevisionInfo(RemoteBranchTestCase):
566
1155
self.assertEqual((2, revid), result)
569
class TestBranch_get_stacked_on_url(tests.TestCaseWithMemoryTransport):
1158
class TestBranch_get_stacked_on_url(TestRemote):
570
1159
"""Test Branch._get_stacked_on_url rpc"""
572
1161
def test_get_stacked_on_invalid_url(self):
573
raise tests.KnownFailure('opening a branch requires the server to open the fallback repository')
574
transport = FakeRemoteTransport('fakeremotetransport:///')
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
575
1172
client = FakeClient(transport.base)
576
1173
client.add_expected_call(
577
'Branch.get_stacked_on_url', ('.',),
578
'success', ('ok', 'file:///stacked/on'))
579
bzrdir = RemoteBzrDir(transport, _client=client)
580
branch = RemoteBranch(bzrdir, None, _client=client)
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),
581
1187
result = branch.get_stacked_on_url()
583
'file:///stacked/on', result)
1188
self.assertEqual(vfs_url, result)
585
1190
def test_backwards_compatible(self):
586
1191
# like with bzr1.6 with no Branch.get_stacked_on_url rpc
938
1572
self.assertEqual('rejection message', err.msg)
941
class TestBranchControlGetBranchConf(tests.TestCaseWithMemoryTransport):
942
"""Getting the branch configuration should use an abstract method not vfs.
1575
class TestBranchGetSetConfig(RemoteBranchTestCase):
945
1577
def test_get_branch_conf(self):
946
raise tests.KnownFailure('branch.conf is not retrieved by get_config_file')
947
## # We should see that branch.get_config() does a single rpc to get the
948
## # remote configuration file, abstracting away where that is stored on
949
## # the server. However at the moment it always falls back to using the
950
## # vfs, and this would need some changes in config.py.
952
## # in an empty branch we decode the response properly
953
## client = FakeClient([(('ok', ), '# config file body')], self.get_url())
954
## # we need to make a real branch because the remote_branch.control_files
955
## # will trigger _ensure_real.
956
## branch = self.make_branch('quack')
957
## transport = branch.bzrdir.root_transport
958
## # we do not want bzrdir to make any remote calls
959
## bzrdir = RemoteBzrDir(transport, _client=False)
960
## branch = RemoteBranch(bzrdir, None, _client=client)
961
## config = branch.get_config()
963
## [('call_expecting_body', 'Branch.get_config_file', ('quack/',))],
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'))
967
1643
class TestBranchLockWrite(RemoteBranchTestCase):
1243
1983
errors.UnexpectedSmartServerResponse,
1244
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)
1247
2074
class TestGetParentMapAllowsNew(tests.TestCaseWithTransport):
1249
2076
def test_allows_new_revisions(self):
1250
2077
"""get_parent_map's results can be updated by commit."""
1251
smart_server = server.SmartTCPServer_for_testing()
1252
smart_server.setUp()
1253
self.addCleanup(smart_server.tearDown)
2078
smart_server = test_server.SmartTCPServer_for_testing()
2079
self.start_server(smart_server)
1254
2080
self.make_branch('branch')
1255
2081
branch = Branch.open(smart_server.get_url() + '/branch')
1256
2082
tree = branch.create_checkout('tree', lightweight=True)
1332
2161
repo, client = self.setup_fake_client_and_repository(transport_path)
1333
2162
client.add_error_response('AnUnexpectedError')
1334
2163
e = self.assertRaises(errors.UnknownErrorFromSmartServer,
1335
self.applyDeprecated, one_four, repo.get_revision_graph, revid)
2164
repo._get_revision_graph, revid)
1336
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', 'fake-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
repo.add_fallback_repository(fallback_repo)
2201
# First the client should ask the primary repo
2202
client.add_expected_call(
2203
'Repository.get_rev_id_for_revno', ('quack/', 1, (42, 'rev-foo')),
2204
'success', ('history-incomplete', 2, 'rev-two'))
2205
# Then it should ask the fallback, using revno/revid from the
2206
# history-incomplete response as the known revno/revid.
2207
client.add_expected_call(
2208
'Repository.get_rev_id_for_revno',('fallback/', 1, (2, 'rev-two')),
2209
'success', ('ok', 'rev-one'))
2210
result = repo.get_rev_id_for_revno(1, (42, 'rev-foo'))
2211
self.assertEqual((True, 'rev-one'), result)
2212
self.assertFinished(client)
2214
def test_nosuchrevision(self):
2215
# 'nosuchrevision' is returned when the known-revid is not found in the
2216
# remote repo. The client translates that response to NoSuchRevision.
2217
repo, client = self.setup_fake_client_and_repository('quack')
2218
client.add_expected_call(
2219
'Repository.get_rev_id_for_revno', ('quack/', 5, (42, 'rev-foo')),
2220
'error', ('nosuchrevision', 'rev-foo'))
2222
errors.NoSuchRevision,
2223
repo.get_rev_id_for_revno, 5, (42, 'rev-foo'))
2224
self.assertFinished(client)
2226
def test_branch_fallback_locking(self):
2227
"""RemoteBranch.get_rev_id takes a read lock, and tries to call the
2228
get_rev_id_for_revno verb. If the verb is unknown the VFS fallback
2229
will be invoked, which will fail if the repo is unlocked.
2231
self.setup_smart_server_with_call_log()
2232
tree = self.make_branch_and_memory_tree('.')
2234
rev1 = tree.commit('First')
2235
rev2 = tree.commit('Second')
2237
branch = tree.branch
2238
self.assertFalse(branch.is_locked())
2239
self.reset_smart_call_log()
2240
verb = 'Repository.get_rev_id_for_revno'
2241
self.disable_verb(verb)
2242
self.assertEqual(rev1, branch.get_rev_id(1))
2243
self.assertLength(1, [call for call in self.hpss_calls if
2244
call.call.method == verb])
1339
2247
class TestRepositoryIsShared(TestRemoteRepository):
1341
2249
def test_is_shared(self):
1430
2364
self.assertEqual([], client._calls)
2367
class TestRepositoryInsertStreamBase(TestRemoteRepository):
2368
"""Base class for Repository.insert_stream and .insert_stream_1.19
2372
def checkInsertEmptyStream(self, repo, client):
2373
"""Insert an empty stream, checking the result.
2375
This checks that there are no resume_tokens or missing_keys, and that
2376
the client is finished.
2378
sink = repo._get_sink()
2379
fmt = repository.RepositoryFormat.get_default_format()
2380
resume_tokens, missing_keys = sink.insert_stream([], fmt, [])
2381
self.assertEqual([], resume_tokens)
2382
self.assertEqual(set(), missing_keys)
2383
self.assertFinished(client)
2386
class TestRepositoryInsertStream(TestRepositoryInsertStreamBase):
2387
"""Tests for using Repository.insert_stream verb when the _1.19 variant is
2390
This test case is very similar to TestRepositoryInsertStream_1_19.
2394
TestRemoteRepository.setUp(self)
2395
self.disable_verb('Repository.insert_stream_1.19')
2397
def test_unlocked_repo(self):
2398
transport_path = 'quack'
2399
repo, client = self.setup_fake_client_and_repository(transport_path)
2400
client.add_expected_call(
2401
'Repository.insert_stream_1.19', ('quack/', ''),
2402
'unknown', ('Repository.insert_stream_1.19',))
2403
client.add_expected_call(
2404
'Repository.insert_stream', ('quack/', ''),
2406
client.add_expected_call(
2407
'Repository.insert_stream', ('quack/', ''),
2409
self.checkInsertEmptyStream(repo, client)
2411
def test_locked_repo_with_no_lock_token(self):
2412
transport_path = 'quack'
2413
repo, client = self.setup_fake_client_and_repository(transport_path)
2414
client.add_expected_call(
2415
'Repository.lock_write', ('quack/', ''),
2416
'success', ('ok', ''))
2417
client.add_expected_call(
2418
'Repository.insert_stream_1.19', ('quack/', ''),
2419
'unknown', ('Repository.insert_stream_1.19',))
2420
client.add_expected_call(
2421
'Repository.insert_stream', ('quack/', ''),
2423
client.add_expected_call(
2424
'Repository.insert_stream', ('quack/', ''),
2427
self.checkInsertEmptyStream(repo, client)
2429
def test_locked_repo_with_lock_token(self):
2430
transport_path = 'quack'
2431
repo, client = self.setup_fake_client_and_repository(transport_path)
2432
client.add_expected_call(
2433
'Repository.lock_write', ('quack/', ''),
2434
'success', ('ok', 'a token'))
2435
client.add_expected_call(
2436
'Repository.insert_stream_1.19', ('quack/', '', 'a token'),
2437
'unknown', ('Repository.insert_stream_1.19',))
2438
client.add_expected_call(
2439
'Repository.insert_stream_locked', ('quack/', '', 'a token'),
2441
client.add_expected_call(
2442
'Repository.insert_stream_locked', ('quack/', '', 'a token'),
2445
self.checkInsertEmptyStream(repo, client)
2447
def test_stream_with_inventory_deltas(self):
2448
"""'inventory-deltas' substreams cannot be sent to the
2449
Repository.insert_stream verb, because not all servers that implement
2450
that verb will accept them. So when one is encountered the RemoteSink
2451
immediately stops using that verb and falls back to VFS insert_stream.
2453
transport_path = 'quack'
2454
repo, client = self.setup_fake_client_and_repository(transport_path)
2455
client.add_expected_call(
2456
'Repository.insert_stream_1.19', ('quack/', ''),
2457
'unknown', ('Repository.insert_stream_1.19',))
2458
client.add_expected_call(
2459
'Repository.insert_stream', ('quack/', ''),
2461
client.add_expected_call(
2462
'Repository.insert_stream', ('quack/', ''),
2464
# Create a fake real repository for insert_stream to fall back on, so
2465
# that we can directly see the records the RemoteSink passes to the
2470
def insert_stream(self, stream, src_format, resume_tokens):
2471
for substream_kind, substream in stream:
2472
self.records.append(
2473
(substream_kind, [record.key for record in substream]))
2474
return ['fake tokens'], ['fake missing keys']
2475
fake_real_sink = FakeRealSink()
2476
class FakeRealRepository:
2477
def _get_sink(self):
2478
return fake_real_sink
2479
def is_in_write_group(self):
2481
def refresh_data(self):
2483
repo._real_repository = FakeRealRepository()
2484
sink = repo._get_sink()
2485
fmt = repository.RepositoryFormat.get_default_format()
2486
stream = self.make_stream_with_inv_deltas(fmt)
2487
resume_tokens, missing_keys = sink.insert_stream(stream, fmt, [])
2488
# Every record from the first inventory delta should have been sent to
2490
expected_records = [
2491
('inventory-deltas', [('rev2',), ('rev3',)]),
2492
('texts', [('some-rev', 'some-file')])]
2493
self.assertEqual(expected_records, fake_real_sink.records)
2494
# The return values from the real sink's insert_stream are propagated
2495
# back to the original caller.
2496
self.assertEqual(['fake tokens'], resume_tokens)
2497
self.assertEqual(['fake missing keys'], missing_keys)
2498
self.assertFinished(client)
2500
def make_stream_with_inv_deltas(self, fmt):
2501
"""Make a simple stream with an inventory delta followed by more
2502
records and more substreams to test that all records and substreams
2503
from that point on are used.
2505
This sends, in order:
2506
* inventories substream: rev1, rev2, rev3. rev2 and rev3 are
2508
* texts substream: (some-rev, some-file)
2510
# Define a stream using generators so that it isn't rewindable.
2511
inv = inventory.Inventory(revision_id='rev1')
2512
inv.root.revision = 'rev1'
2513
def stream_with_inv_delta():
2514
yield ('inventories', inventories_substream())
2515
yield ('inventory-deltas', inventory_delta_substream())
2517
versionedfile.FulltextContentFactory(
2518
('some-rev', 'some-file'), (), None, 'content')])
2519
def inventories_substream():
2520
# An empty inventory fulltext. This will be streamed normally.
2521
text = fmt._serializer.write_inventory_to_string(inv)
2522
yield versionedfile.FulltextContentFactory(
2523
('rev1',), (), None, text)
2524
def inventory_delta_substream():
2525
# An inventory delta. This can't be streamed via this verb, so it
2526
# will trigger a fallback to VFS insert_stream.
2527
entry = inv.make_entry(
2528
'directory', 'newdir', inv.root.file_id, 'newdir-id')
2529
entry.revision = 'ghost'
2530
delta = [(None, 'newdir', 'newdir-id', entry)]
2531
serializer = inventory_delta.InventoryDeltaSerializer(
2532
versioned_root=True, tree_references=False)
2533
lines = serializer.delta_to_lines('rev1', 'rev2', delta)
2534
yield versionedfile.ChunkedContentFactory(
2535
('rev2',), (('rev1',)), None, lines)
2537
lines = serializer.delta_to_lines('rev1', 'rev3', delta)
2538
yield versionedfile.ChunkedContentFactory(
2539
('rev3',), (('rev1',)), None, lines)
2540
return stream_with_inv_delta()
2543
class TestRepositoryInsertStream_1_19(TestRepositoryInsertStreamBase):
2545
def test_unlocked_repo(self):
2546
transport_path = 'quack'
2547
repo, client = self.setup_fake_client_and_repository(transport_path)
2548
client.add_expected_call(
2549
'Repository.insert_stream_1.19', ('quack/', ''),
2551
client.add_expected_call(
2552
'Repository.insert_stream_1.19', ('quack/', ''),
2554
self.checkInsertEmptyStream(repo, client)
2556
def test_locked_repo_with_no_lock_token(self):
2557
transport_path = 'quack'
2558
repo, client = self.setup_fake_client_and_repository(transport_path)
2559
client.add_expected_call(
2560
'Repository.lock_write', ('quack/', ''),
2561
'success', ('ok', ''))
2562
client.add_expected_call(
2563
'Repository.insert_stream_1.19', ('quack/', ''),
2565
client.add_expected_call(
2566
'Repository.insert_stream_1.19', ('quack/', ''),
2569
self.checkInsertEmptyStream(repo, client)
2571
def test_locked_repo_with_lock_token(self):
2572
transport_path = 'quack'
2573
repo, client = self.setup_fake_client_and_repository(transport_path)
2574
client.add_expected_call(
2575
'Repository.lock_write', ('quack/', ''),
2576
'success', ('ok', 'a token'))
2577
client.add_expected_call(
2578
'Repository.insert_stream_1.19', ('quack/', '', 'a token'),
2580
client.add_expected_call(
2581
'Repository.insert_stream_1.19', ('quack/', '', 'a token'),
2584
self.checkInsertEmptyStream(repo, client)
1433
2587
class TestRepositoryTarball(TestRemoteRepository):
1435
2589
# This is a canned tarball reponse we can validate against
1793
2965
remote_repo.unlock()
1795
2967
def prepare_stacked_remote_branch(self):
1796
smart_server = server.SmartTCPServer_for_testing()
1797
smart_server.setUp()
1798
self.addCleanup(smart_server.tearDown)
1799
tree1 = self.make_branch_and_tree('tree1')
2968
"""Get stacked_upon and stacked branches with content in each."""
2969
self.setup_smart_server_with_call_log()
2970
tree1 = self.make_branch_and_tree('tree1', format='1.9')
1800
2971
tree1.commit('rev1', rev_id='rev1')
1801
tree2 = self.make_branch_and_tree('tree2', format='1.6')
1802
tree2.branch.set_stacked_on_url(tree1.branch.base)
1803
branch2 = Branch.open(smart_server.get_url() + '/tree2')
2972
tree2 = tree1.branch.bzrdir.sprout('tree2', stacked=True
2973
).open_workingtree()
2974
local_tree = tree2.branch.create_checkout('local')
2975
local_tree.commit('local changes make me feel good.')
2976
branch2 = Branch.open(self.get_url('tree2'))
1804
2977
branch2.lock_read()
1805
2978
self.addCleanup(branch2.unlock)
2979
return tree1.branch, branch2
1808
2981
def test_stacked_get_parent_map(self):
1809
2982
# the public implementation of get_parent_map obeys stacking
1810
branch = self.prepare_stacked_remote_branch()
2983
_, branch = self.prepare_stacked_remote_branch()
1811
2984
repo = branch.repository
1812
2985
self.assertEqual(['rev1'], repo.get_parent_map(['rev1']).keys())
1814
2987
def test_unstacked_get_parent_map(self):
1815
2988
# _unstacked_provider.get_parent_map ignores stacking
1816
branch = self.prepare_stacked_remote_branch()
2989
_, branch = self.prepare_stacked_remote_branch()
1817
2990
provider = branch.repository._unstacked_provider
1818
2991
self.assertEqual([], provider.get_parent_map(['rev1']).keys())
2993
def fetch_stream_to_rev_order(self, stream):
2995
for kind, substream in stream:
2996
if not kind == 'revisions':
2999
for content in substream:
3000
result.append(content.key[-1])
3003
def get_ordered_revs(self, format, order, branch_factory=None):
3004
"""Get a list of the revisions in a stream to format format.
3006
:param format: The format of the target.
3007
:param order: the order that target should have requested.
3008
:param branch_factory: A callable to create a trunk and stacked branch
3009
to fetch from. If none, self.prepare_stacked_remote_branch is used.
3010
:result: The revision ids in the stream, in the order seen,
3011
the topological order of revisions in the source.
3013
unordered_format = bzrdir.format_registry.get(format)()
3014
target_repository_format = unordered_format.repository_format
3016
self.assertEqual(order, target_repository_format._fetch_order)
3017
if branch_factory is None:
3018
branch_factory = self.prepare_stacked_remote_branch
3019
_, stacked = branch_factory()
3020
source = stacked.repository._get_source(target_repository_format)
3021
tip = stacked.last_revision()
3022
revs = stacked.repository.get_ancestry(tip)
3023
search = graph.PendingAncestryResult([tip], stacked.repository)
3024
self.reset_smart_call_log()
3025
stream = source.get_stream(search)
3028
# We trust that if a revision is in the stream the rest of the new
3029
# content for it is too, as per our main fetch tests; here we are
3030
# checking that the revisions are actually included at all, and their
3032
return self.fetch_stream_to_rev_order(stream), revs
3034
def test_stacked_get_stream_unordered(self):
3035
# Repository._get_source.get_stream() from a stacked repository with
3036
# unordered yields the full data from both stacked and stacked upon
3038
rev_ord, expected_revs = self.get_ordered_revs('1.9', 'unordered')
3039
self.assertEqual(set(expected_revs), set(rev_ord))
3040
# Getting unordered results should have made a streaming data request
3041
# from the server, then one from the backing branch.
3042
self.assertLength(2, self.hpss_calls)
3044
def test_stacked_on_stacked_get_stream_unordered(self):
3045
# Repository._get_source.get_stream() from a stacked repository which
3046
# is itself stacked yields the full data from all three sources.
3047
def make_stacked_stacked():
3048
_, stacked = self.prepare_stacked_remote_branch()
3049
tree = stacked.bzrdir.sprout('tree3', stacked=True
3050
).open_workingtree()
3051
local_tree = tree.branch.create_checkout('local-tree3')
3052
local_tree.commit('more local changes are better')
3053
branch = Branch.open(self.get_url('tree3'))
3055
self.addCleanup(branch.unlock)
3057
rev_ord, expected_revs = self.get_ordered_revs('1.9', 'unordered',
3058
branch_factory=make_stacked_stacked)
3059
self.assertEqual(set(expected_revs), set(rev_ord))
3060
# Getting unordered results should have made a streaming data request
3061
# from the server, and one from each backing repo
3062
self.assertLength(3, self.hpss_calls)
3064
def test_stacked_get_stream_topological(self):
3065
# Repository._get_source.get_stream() from a stacked repository with
3066
# topological sorting yields the full data from both stacked and
3067
# stacked upon sources in topological order.
3068
rev_ord, expected_revs = self.get_ordered_revs('knit', 'topological')
3069
self.assertEqual(expected_revs, rev_ord)
3070
# Getting topological sort requires VFS calls still - one of which is
3071
# pushing up from the bound branch.
3072
self.assertLength(13, self.hpss_calls)
3074
def test_stacked_get_stream_groupcompress(self):
3075
# Repository._get_source.get_stream() from a stacked repository with
3076
# groupcompress sorting yields the full data from both stacked and
3077
# stacked upon sources in groupcompress order.
3078
raise tests.TestSkipped('No groupcompress ordered format available')
3079
rev_ord, expected_revs = self.get_ordered_revs('dev5', 'groupcompress')
3080
self.assertEqual(expected_revs, reversed(rev_ord))
3081
# Getting unordered results should have made a streaming data request
3082
# from the backing branch, and one from the stacked on branch.
3083
self.assertLength(2, self.hpss_calls)
3085
def test_stacked_pull_more_than_stacking_has_bug_360791(self):
3086
# When pulling some fixed amount of content that is more than the
3087
# source has (because some is coming from a fallback branch, no error
3088
# should be received. This was reported as bug 360791.
3089
# Need three branches: a trunk, a stacked branch, and a preexisting
3090
# branch pulling content from stacked and trunk.
3091
self.setup_smart_server_with_call_log()
3092
trunk = self.make_branch_and_tree('trunk', format="1.9-rich-root")
3093
r1 = trunk.commit('start')
3094
stacked_branch = trunk.branch.create_clone_on_transport(
3095
self.get_transport('stacked'), stacked_on=trunk.branch.base)
3096
local = self.make_branch('local', format='1.9-rich-root')
3097
local.repository.fetch(stacked_branch.repository,
3098
stacked_branch.last_revision())
1821
3101
class TestRemoteBranchEffort(tests.TestCaseWithTransport):