112
134
b = BzrDir.open_from_transport(self.transport).open_branch()
113
135
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"
137
def test_remote_bzrdir_repr(self):
138
b = BzrDir.open_from_transport(self.transport)
139
self.assertStartsWith(str(b), 'RemoteBzrDir(')
141
def test_remote_branch_format_supports_stacking(self):
143
self.make_branch('unstackable', format='pack-0.92')
144
b = BzrDir.open_from_transport(t.clone('unstackable')).open_branch()
145
self.assertFalse(b._format.supports_stacking())
146
self.make_branch('stackable', format='1.9')
147
b = BzrDir.open_from_transport(t.clone('stackable')).open_branch()
148
self.assertTrue(b._format.supports_stacking())
150
def test_remote_repo_format_supports_external_references(self):
152
bd = self.make_bzrdir('unstackable', format='pack-0.92')
153
r = bd.create_repository()
154
self.assertFalse(r._format.supports_external_lookups)
155
r = BzrDir.open_from_transport(t.clone('unstackable')).open_repository()
156
self.assertFalse(r._format.supports_external_lookups)
157
bd = self.make_bzrdir('stackable', format='1.9')
158
r = bd.create_repository()
159
self.assertTrue(r._format.supports_external_lookups)
160
r = BzrDir.open_from_transport(t.clone('stackable')).open_repository()
161
self.assertTrue(r._format.supports_external_lookups)
163
def test_remote_branch_set_append_revisions_only(self):
164
# Make a format 1.9 branch, which supports append_revisions_only
165
branch = self.make_branch('branch', format='1.9')
166
config = branch.get_config()
167
branch.set_append_revisions_only(True)
169
'True', config.get_user_option('append_revisions_only'))
170
branch.set_append_revisions_only(False)
172
'False', config.get_user_option('append_revisions_only'))
174
def test_remote_branch_set_append_revisions_only_upgrade_reqd(self):
175
branch = self.make_branch('branch', format='knit')
176
config = branch.get_config()
178
errors.UpgradeRequired, branch.set_append_revisions_only, True)
148
181
class FakeProtocol(object):
359
422
AssertionError, client_medium._remember_remote_is_before, (1, 9))
362
class TestBzrDirOpenBranch(tests.TestCase):
425
class TestBzrDirCloningMetaDir(TestRemote):
427
def test_backwards_compat(self):
428
self.setup_smart_server_with_call_log()
429
a_dir = self.make_bzrdir('.')
430
self.reset_smart_call_log()
431
verb = 'BzrDir.cloning_metadir'
432
self.disable_verb(verb)
433
format = a_dir.cloning_metadir()
434
call_count = len([call for call in self.hpss_calls if
435
call.call.method == verb])
436
self.assertEqual(1, call_count)
438
def test_branch_reference(self):
439
transport = self.get_transport('quack')
440
referenced = self.make_branch('referenced')
441
expected = referenced.bzrdir.cloning_metadir()
442
client = FakeClient(transport.base)
443
client.add_expected_call(
444
'BzrDir.cloning_metadir', ('quack/', 'False'),
445
'error', ('BranchReference',)),
446
client.add_expected_call(
447
'BzrDir.open_branchV3', ('quack/',),
448
'success', ('ref', self.get_url('referenced'))),
449
a_bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
451
result = a_bzrdir.cloning_metadir()
452
# We should have got a control dir matching the referenced branch.
453
self.assertEqual(bzrdir.BzrDirMetaFormat1, type(result))
454
self.assertEqual(expected._repository_format, result._repository_format)
455
self.assertEqual(expected._branch_format, result._branch_format)
456
self.assertFinished(client)
458
def test_current_server(self):
459
transport = self.get_transport('.')
460
transport = transport.clone('quack')
461
self.make_bzrdir('quack')
462
client = FakeClient(transport.base)
463
reference_bzrdir_format = bzrdir.format_registry.get('default')()
464
control_name = reference_bzrdir_format.network_name()
465
client.add_expected_call(
466
'BzrDir.cloning_metadir', ('quack/', 'False'),
467
'success', (control_name, '', ('branch', ''))),
468
a_bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
470
result = a_bzrdir.cloning_metadir()
471
# We should have got a reference control dir with default branch and
472
# repository formats.
473
# This pokes a little, just to be sure.
474
self.assertEqual(bzrdir.BzrDirMetaFormat1, type(result))
475
self.assertEqual(None, result._repository_format)
476
self.assertEqual(None, result._branch_format)
477
self.assertFinished(client)
480
class TestBzrDirOpen(TestRemote):
482
def make_fake_client_and_transport(self, path='quack'):
483
transport = MemoryTransport()
484
transport.mkdir(path)
485
transport = transport.clone(path)
486
client = FakeClient(transport.base)
487
return client, transport
489
def test_absent(self):
490
client, transport = self.make_fake_client_and_transport()
491
client.add_expected_call(
492
'BzrDir.open_2.1', ('quack/',), 'success', ('no',))
493
self.assertRaises(errors.NotBranchError, RemoteBzrDir, transport,
494
remote.RemoteBzrDirFormat(), _client=client, _force_probe=True)
495
self.assertFinished(client)
497
def test_present_without_workingtree(self):
498
client, transport = self.make_fake_client_and_transport()
499
client.add_expected_call(
500
'BzrDir.open_2.1', ('quack/',), 'success', ('yes', 'no'))
501
bd = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
502
_client=client, _force_probe=True)
503
self.assertIsInstance(bd, RemoteBzrDir)
504
self.assertFalse(bd.has_workingtree())
505
self.assertRaises(errors.NoWorkingTree, bd.open_workingtree)
506
self.assertFinished(client)
508
def test_present_with_workingtree(self):
509
client, transport = self.make_fake_client_and_transport()
510
client.add_expected_call(
511
'BzrDir.open_2.1', ('quack/',), 'success', ('yes', 'yes'))
512
bd = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
513
_client=client, _force_probe=True)
514
self.assertIsInstance(bd, RemoteBzrDir)
515
self.assertTrue(bd.has_workingtree())
516
self.assertRaises(errors.NotLocalUrl, bd.open_workingtree)
517
self.assertFinished(client)
519
def test_backwards_compat(self):
520
client, transport = self.make_fake_client_and_transport()
521
client.add_expected_call(
522
'BzrDir.open_2.1', ('quack/',), 'unknown', ('BzrDir.open_2.1',))
523
client.add_expected_call(
524
'BzrDir.open', ('quack/',), 'success', ('yes',))
525
bd = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
526
_client=client, _force_probe=True)
527
self.assertIsInstance(bd, RemoteBzrDir)
528
self.assertFinished(client)
531
class TestBzrDirOpenBranch(TestRemote):
533
def test_backwards_compat(self):
534
self.setup_smart_server_with_call_log()
535
self.make_branch('.')
536
a_dir = BzrDir.open(self.get_url('.'))
537
self.reset_smart_call_log()
538
verb = 'BzrDir.open_branchV3'
539
self.disable_verb(verb)
540
format = a_dir.open_branch()
541
call_count = len([call for call in self.hpss_calls if
542
call.call.method == verb])
543
self.assertEqual(1, call_count)
364
545
def test_branch_present(self):
546
reference_format = self.get_repo_format()
547
network_name = reference_format.network_name()
548
branch_network_name = self.get_branch_format().network_name()
365
549
transport = MemoryTransport()
366
550
transport.mkdir('quack')
367
551
transport = transport.clone('quack')
368
552
client = FakeClient(transport.base)
369
553
client.add_expected_call(
370
'BzrDir.open_branch', ('quack/',),
371
'success', ('ok', ''))
554
'BzrDir.open_branchV3', ('quack/',),
555
'success', ('branch', branch_network_name))
372
556
client.add_expected_call(
373
'BzrDir.find_repositoryV2', ('quack/',),
374
'success', ('ok', '', 'no', 'no', 'no'))
557
'BzrDir.find_repositoryV3', ('quack/',),
558
'success', ('ok', '', 'no', 'no', 'no', network_name))
375
559
client.add_expected_call(
376
560
'Branch.get_stacked_on_url', ('quack/',),
377
561
'error', ('NotStacked',))
378
bzrdir = RemoteBzrDir(transport, _client=client)
562
bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
379
564
result = bzrdir.open_branch()
380
565
self.assertIsInstance(result, RemoteBranch)
381
566
self.assertEqual(bzrdir, result.bzrdir)
382
client.finished_test()
567
self.assertFinished(client)
384
569
def test_branch_missing(self):
385
570
transport = MemoryTransport()
468
663
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')
666
class TestBzrDirCreateBranch(TestRemote):
668
def test_backwards_compat(self):
669
self.setup_smart_server_with_call_log()
670
repo = self.make_repository('.')
671
self.reset_smart_call_log()
672
self.disable_verb('BzrDir.create_branch')
673
branch = repo.bzrdir.create_branch()
674
create_branch_call_count = len([call for call in self.hpss_calls if
675
call.call.method == 'BzrDir.create_branch'])
676
self.assertEqual(1, create_branch_call_count)
678
def test_current_server(self):
679
transport = self.get_transport('.')
680
transport = transport.clone('quack')
681
self.make_repository('quack')
682
client = FakeClient(transport.base)
683
reference_bzrdir_format = bzrdir.format_registry.get('default')()
684
reference_format = reference_bzrdir_format.get_branch_format()
685
network_name = reference_format.network_name()
686
reference_repo_fmt = reference_bzrdir_format.repository_format
687
reference_repo_name = reference_repo_fmt.network_name()
688
client.add_expected_call(
689
'BzrDir.create_branch', ('quack/', network_name),
690
'success', ('ok', network_name, '', 'no', 'no', 'yes',
691
reference_repo_name))
692
a_bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
694
branch = a_bzrdir.create_branch()
695
# We should have got a remote branch
696
self.assertIsInstance(branch, remote.RemoteBranch)
697
# its format should have the settings from the response
698
format = branch._format
699
self.assertEqual(network_name, format.network_name())
702
class TestBzrDirCreateRepository(TestRemote):
704
def test_backwards_compat(self):
705
self.setup_smart_server_with_call_log()
706
bzrdir = self.make_bzrdir('.')
707
self.reset_smart_call_log()
708
self.disable_verb('BzrDir.create_repository')
709
repo = bzrdir.create_repository()
710
create_repo_call_count = len([call for call in self.hpss_calls if
711
call.call.method == 'BzrDir.create_repository'])
712
self.assertEqual(1, create_repo_call_count)
714
def test_current_server(self):
715
transport = self.get_transport('.')
716
transport = transport.clone('quack')
717
self.make_bzrdir('quack')
718
client = FakeClient(transport.base)
719
reference_bzrdir_format = bzrdir.format_registry.get('default')()
720
reference_format = reference_bzrdir_format.repository_format
721
network_name = reference_format.network_name()
722
client.add_expected_call(
723
'BzrDir.create_repository', ('quack/',
724
'Bazaar repository format 2a (needs bzr 1.16 or later)\n',
726
'success', ('ok', 'yes', 'yes', 'yes', network_name))
727
a_bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
729
repo = a_bzrdir.create_repository()
730
# We should have got a remote repository
731
self.assertIsInstance(repo, remote.RemoteRepository)
732
# its format should have the settings from the response
733
format = repo._format
734
self.assertTrue(format.rich_root_data)
735
self.assertTrue(format.supports_tree_reference)
736
self.assertTrue(format.supports_external_lookups)
737
self.assertEqual(network_name, format.network_name())
740
class TestBzrDirOpenRepository(TestRemote):
742
def test_backwards_compat_1_2_3(self):
743
# fallback all the way to the first version.
744
reference_format = self.get_repo_format()
745
network_name = reference_format.network_name()
746
server_url = 'bzr://example.com/'
747
self.permit_url(server_url)
748
client = FakeClient(server_url)
749
client.add_unknown_method_response('BzrDir.find_repositoryV3')
750
client.add_unknown_method_response('BzrDir.find_repositoryV2')
479
751
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/',))],
752
# A real repository instance will be created to determine the network
754
client.add_success_response_with_body(
755
"Bazaar-NG meta directory, format 1\n", 'ok')
756
client.add_success_response_with_body(
757
reference_format.get_format_string(), 'ok')
758
# PackRepository wants to do a stat
759
client.add_success_response('stat', '0', '65535')
760
remote_transport = RemoteTransport(server_url + 'quack/', medium=False,
762
bzrdir = RemoteBzrDir(remote_transport, remote.RemoteBzrDirFormat(),
764
repo = bzrdir.open_repository()
766
[('call', 'BzrDir.find_repositoryV3', ('quack/',)),
767
('call', 'BzrDir.find_repositoryV2', ('quack/',)),
768
('call', 'BzrDir.find_repository', ('quack/',)),
769
('call_expecting_body', 'get', ('/quack/.bzr/branch-format',)),
770
('call_expecting_body', 'get', ('/quack/.bzr/repository/format',)),
771
('call', 'stat', ('/quack/.bzr/repository',)),
774
self.assertEqual(network_name, repo._format.network_name())
776
def test_backwards_compat_2(self):
777
# fallback to find_repositoryV2
778
reference_format = self.get_repo_format()
779
network_name = reference_format.network_name()
780
server_url = 'bzr://example.com/'
781
self.permit_url(server_url)
782
client = FakeClient(server_url)
783
client.add_unknown_method_response('BzrDir.find_repositoryV3')
784
client.add_success_response('ok', '', 'no', 'no', 'no')
785
# A real repository instance will be created to determine the network
787
client.add_success_response_with_body(
788
"Bazaar-NG meta directory, format 1\n", 'ok')
789
client.add_success_response_with_body(
790
reference_format.get_format_string(), 'ok')
791
# PackRepository wants to do a stat
792
client.add_success_response('stat', '0', '65535')
793
remote_transport = RemoteTransport(server_url + 'quack/', medium=False,
795
bzrdir = RemoteBzrDir(remote_transport, remote.RemoteBzrDirFormat(),
797
repo = bzrdir.open_repository()
799
[('call', 'BzrDir.find_repositoryV3', ('quack/',)),
800
('call', 'BzrDir.find_repositoryV2', ('quack/',)),
801
('call_expecting_body', 'get', ('/quack/.bzr/branch-format',)),
802
('call_expecting_body', 'get', ('/quack/.bzr/repository/format',)),
803
('call', 'stat', ('/quack/.bzr/repository',)),
806
self.assertEqual(network_name, repo._format.network_name())
808
def test_current_server(self):
809
reference_format = self.get_repo_format()
810
network_name = reference_format.network_name()
811
transport = MemoryTransport()
812
transport.mkdir('quack')
813
transport = transport.clone('quack')
814
client = FakeClient(transport.base)
815
client.add_success_response('ok', '', 'no', 'no', 'no', network_name)
816
bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
818
repo = bzrdir.open_repository()
820
[('call', 'BzrDir.find_repositoryV3', ('quack/',))],
822
self.assertEqual(network_name, repo._format.network_name())
825
class TestBzrDirFormatInitializeEx(TestRemote):
827
def test_success(self):
828
"""Simple test for typical successful call."""
829
fmt = bzrdir.RemoteBzrDirFormat()
830
default_format_name = BzrDirFormat.get_default_format().network_name()
831
transport = self.get_transport()
832
client = FakeClient(transport.base)
833
client.add_expected_call(
834
'BzrDirFormat.initialize_ex_1.16',
835
(default_format_name, 'path', 'False', 'False', 'False', '',
836
'', '', '', 'False'),
838
('.', 'no', 'no', 'yes', 'repo fmt', 'repo bzrdir fmt',
839
'bzrdir fmt', 'False', '', '', 'repo lock token'))
840
# XXX: It would be better to call fmt.initialize_on_transport_ex, but
841
# it's currently hard to test that without supplying a real remote
842
# transport connected to a real server.
843
result = fmt._initialize_on_transport_ex_rpc(client, 'path',
844
transport, False, False, False, None, None, None, None, False)
845
self.assertFinished(client)
847
def test_error(self):
848
"""Error responses are translated, e.g. 'PermissionDenied' raises the
849
corresponding error from the client.
851
fmt = bzrdir.RemoteBzrDirFormat()
852
default_format_name = BzrDirFormat.get_default_format().network_name()
853
transport = self.get_transport()
854
client = FakeClient(transport.base)
855
client.add_expected_call(
856
'BzrDirFormat.initialize_ex_1.16',
857
(default_format_name, 'path', 'False', 'False', 'False', '',
858
'', '', '', 'False'),
860
('PermissionDenied', 'path', 'extra info'))
861
# XXX: It would be better to call fmt.initialize_on_transport_ex, but
862
# it's currently hard to test that without supplying a real remote
863
# transport connected to a real server.
864
err = self.assertRaises(errors.PermissionDenied,
865
fmt._initialize_on_transport_ex_rpc, client, 'path', transport,
866
False, False, False, None, None, None, None, False)
867
self.assertEqual('path', err.path)
868
self.assertEqual(': extra info', err.extra)
869
self.assertFinished(client)
871
def test_error_from_real_server(self):
872
"""Integration test for error translation."""
873
transport = self.make_smart_server('foo')
874
transport = transport.clone('no-such-path')
875
fmt = bzrdir.RemoteBzrDirFormat()
876
err = self.assertRaises(errors.NoSuchFile,
877
fmt.initialize_on_transport_ex, transport, create_prefix=False)
488
880
class OldSmartClient(object):
513
905
return OldSmartClient()
516
class RemoteBranchTestCase(tests.TestCase):
908
class RemoteBzrDirTestCase(TestRemote):
910
def make_remote_bzrdir(self, transport, client):
911
"""Make a RemotebzrDir using 'client' as the _client."""
912
return RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
916
class RemoteBranchTestCase(RemoteBzrDirTestCase):
918
def lock_remote_branch(self, branch):
919
"""Trick a RemoteBranch into thinking it is locked."""
920
branch._lock_mode = 'w'
921
branch._lock_count = 2
922
branch._lock_token = 'branch token'
923
branch._repo_lock_token = 'repo token'
924
branch.repository._lock_mode = 'w'
925
branch.repository._lock_count = 2
926
branch.repository._lock_token = 'repo token'
518
928
def make_remote_branch(self, transport, client):
519
929
"""Make a RemoteBranch using 'client' as its _SmartClient.
521
931
A RemoteBzrDir and RemoteRepository will also be created to fill out
522
932
the RemoteBranch, albeit with stub values for some of their attributes.
524
934
# we do not want bzrdir to make any remote calls, so use False as its
525
935
# _client. If it tries to make a remote call, this will fail
527
bzrdir = RemoteBzrDir(transport, _client=False)
937
bzrdir = self.make_remote_bzrdir(transport, False)
528
938
repo = RemoteRepository(bzrdir, None, _client=client)
529
return RemoteBranch(bzrdir, repo, _client=client)
939
branch_format = self.get_branch_format()
940
format = RemoteBranchFormat(network_name=branch_format.network_name())
941
return RemoteBranch(bzrdir, repo, _client=client, format=format)
944
class TestBranchGetParent(RemoteBranchTestCase):
946
def test_no_parent(self):
947
# in an empty branch we decode the response properly
948
transport = MemoryTransport()
949
client = FakeClient(transport.base)
950
client.add_expected_call(
951
'Branch.get_stacked_on_url', ('quack/',),
952
'error', ('NotStacked',))
953
client.add_expected_call(
954
'Branch.get_parent', ('quack/',),
956
transport.mkdir('quack')
957
transport = transport.clone('quack')
958
branch = self.make_remote_branch(transport, client)
959
result = branch.get_parent()
960
self.assertFinished(client)
961
self.assertEqual(None, result)
963
def test_parent_relative(self):
964
transport = MemoryTransport()
965
client = FakeClient(transport.base)
966
client.add_expected_call(
967
'Branch.get_stacked_on_url', ('kwaak/',),
968
'error', ('NotStacked',))
969
client.add_expected_call(
970
'Branch.get_parent', ('kwaak/',),
971
'success', ('../foo/',))
972
transport.mkdir('kwaak')
973
transport = transport.clone('kwaak')
974
branch = self.make_remote_branch(transport, client)
975
result = branch.get_parent()
976
self.assertEqual(transport.clone('../foo').base, result)
978
def test_parent_absolute(self):
979
transport = MemoryTransport()
980
client = FakeClient(transport.base)
981
client.add_expected_call(
982
'Branch.get_stacked_on_url', ('kwaak/',),
983
'error', ('NotStacked',))
984
client.add_expected_call(
985
'Branch.get_parent', ('kwaak/',),
986
'success', ('http://foo/',))
987
transport.mkdir('kwaak')
988
transport = transport.clone('kwaak')
989
branch = self.make_remote_branch(transport, client)
990
result = branch.get_parent()
991
self.assertEqual('http://foo/', result)
992
self.assertFinished(client)
995
class TestBranchSetParentLocation(RemoteBranchTestCase):
997
def test_no_parent(self):
998
# We call the verb when setting parent to None
999
transport = MemoryTransport()
1000
client = FakeClient(transport.base)
1001
client.add_expected_call(
1002
'Branch.get_stacked_on_url', ('quack/',),
1003
'error', ('NotStacked',))
1004
client.add_expected_call(
1005
'Branch.set_parent_location', ('quack/', 'b', 'r', ''),
1007
transport.mkdir('quack')
1008
transport = transport.clone('quack')
1009
branch = self.make_remote_branch(transport, client)
1010
branch._lock_token = 'b'
1011
branch._repo_lock_token = 'r'
1012
branch._set_parent_location(None)
1013
self.assertFinished(client)
1015
def test_parent(self):
1016
transport = MemoryTransport()
1017
client = FakeClient(transport.base)
1018
client.add_expected_call(
1019
'Branch.get_stacked_on_url', ('kwaak/',),
1020
'error', ('NotStacked',))
1021
client.add_expected_call(
1022
'Branch.set_parent_location', ('kwaak/', 'b', 'r', 'foo'),
1024
transport.mkdir('kwaak')
1025
transport = transport.clone('kwaak')
1026
branch = self.make_remote_branch(transport, client)
1027
branch._lock_token = 'b'
1028
branch._repo_lock_token = 'r'
1029
branch._set_parent_location('foo')
1030
self.assertFinished(client)
1032
def test_backwards_compat(self):
1033
self.setup_smart_server_with_call_log()
1034
branch = self.make_branch('.')
1035
self.reset_smart_call_log()
1036
verb = 'Branch.set_parent_location'
1037
self.disable_verb(verb)
1038
branch.set_parent('http://foo/')
1039
self.assertLength(12, self.hpss_calls)
1042
class TestBranchGetTagsBytes(RemoteBranchTestCase):
1044
def test_backwards_compat(self):
1045
self.setup_smart_server_with_call_log()
1046
branch = self.make_branch('.')
1047
self.reset_smart_call_log()
1048
verb = 'Branch.get_tags_bytes'
1049
self.disable_verb(verb)
1050
branch.tags.get_tag_dict()
1051
call_count = len([call for call in self.hpss_calls if
1052
call.call.method == verb])
1053
self.assertEqual(1, call_count)
1055
def test_trivial(self):
1056
transport = MemoryTransport()
1057
client = FakeClient(transport.base)
1058
client.add_expected_call(
1059
'Branch.get_stacked_on_url', ('quack/',),
1060
'error', ('NotStacked',))
1061
client.add_expected_call(
1062
'Branch.get_tags_bytes', ('quack/',),
1064
transport.mkdir('quack')
1065
transport = transport.clone('quack')
1066
branch = self.make_remote_branch(transport, client)
1067
result = branch.tags.get_tag_dict()
1068
self.assertFinished(client)
1069
self.assertEqual({}, result)
1072
class TestBranchSetTagsBytes(RemoteBranchTestCase):
1074
def test_trivial(self):
1075
transport = MemoryTransport()
1076
client = FakeClient(transport.base)
1077
client.add_expected_call(
1078
'Branch.get_stacked_on_url', ('quack/',),
1079
'error', ('NotStacked',))
1080
client.add_expected_call(
1081
'Branch.set_tags_bytes', ('quack/', 'branch token', 'repo token'),
1083
transport.mkdir('quack')
1084
transport = transport.clone('quack')
1085
branch = self.make_remote_branch(transport, client)
1086
self.lock_remote_branch(branch)
1087
branch._set_tags_bytes('tags bytes')
1088
self.assertFinished(client)
1089
self.assertEqual('tags bytes', client._calls[-1][-1])
1091
def test_backwards_compatible(self):
1092
transport = MemoryTransport()
1093
client = FakeClient(transport.base)
1094
client.add_expected_call(
1095
'Branch.get_stacked_on_url', ('quack/',),
1096
'error', ('NotStacked',))
1097
client.add_expected_call(
1098
'Branch.set_tags_bytes', ('quack/', 'branch token', 'repo token'),
1099
'unknown', ('Branch.set_tags_bytes',))
1100
transport.mkdir('quack')
1101
transport = transport.clone('quack')
1102
branch = self.make_remote_branch(transport, client)
1103
self.lock_remote_branch(branch)
1104
class StubRealBranch(object):
1107
def _set_tags_bytes(self, bytes):
1108
self.calls.append(('set_tags_bytes', bytes))
1109
real_branch = StubRealBranch()
1110
branch._real_branch = real_branch
1111
branch._set_tags_bytes('tags bytes')
1112
# Call a second time, to exercise the 'remote version already inferred'
1114
branch._set_tags_bytes('tags bytes')
1115
self.assertFinished(client)
1117
[('set_tags_bytes', 'tags bytes')] * 2, real_branch.calls)
532
1120
class TestBranchLastRevisionInfo(RemoteBranchTestCase):
566
1154
self.assertEqual((2, revid), result)
569
class TestBranch_get_stacked_on_url(tests.TestCaseWithMemoryTransport):
1157
class TestBranch_get_stacked_on_url(TestRemote):
570
1158
"""Test Branch._get_stacked_on_url rpc"""
572
1160
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:///')
1161
# test that asking for a stacked on url the server can't access works.
1162
# This isn't perfect, but then as we're in the same process there
1163
# really isn't anything we can do to be 100% sure that the server
1164
# doesn't just open in - this test probably needs to be rewritten using
1165
# a spawn()ed server.
1166
stacked_branch = self.make_branch('stacked', format='1.9')
1167
memory_branch = self.make_branch('base', format='1.9')
1168
vfs_url = self.get_vfs_only_url('base')
1169
stacked_branch.set_stacked_on_url(vfs_url)
1170
transport = stacked_branch.bzrdir.root_transport
575
1171
client = FakeClient(transport.base)
576
1172
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)
1173
'Branch.get_stacked_on_url', ('stacked/',),
1174
'success', ('ok', vfs_url))
1175
# XXX: Multiple calls are bad, this second call documents what is
1177
client.add_expected_call(
1178
'Branch.get_stacked_on_url', ('stacked/',),
1179
'success', ('ok', vfs_url))
1180
bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
1182
repo_fmt = remote.RemoteRepositoryFormat()
1183
repo_fmt._custom_format = stacked_branch.repository._format
1184
branch = RemoteBranch(bzrdir, RemoteRepository(bzrdir, repo_fmt),
581
1186
result = branch.get_stacked_on_url()
583
'file:///stacked/on', result)
1187
self.assertEqual(vfs_url, result)
585
1189
def test_backwards_compatible(self):
586
1190
# like with bzr1.6 with no Branch.get_stacked_on_url rpc
938
1571
self.assertEqual('rejection message', err.msg)
941
class TestBranchControlGetBranchConf(tests.TestCaseWithMemoryTransport):
942
"""Getting the branch configuration should use an abstract method not vfs.
1574
class TestBranchGetSetConfig(RemoteBranchTestCase):
945
1576
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/',))],
1577
# in an empty branch we decode the response properly
1578
client = FakeClient()
1579
client.add_expected_call(
1580
'Branch.get_stacked_on_url', ('memory:///',),
1581
'error', ('NotStacked',),)
1582
client.add_success_response_with_body('# config file body', 'ok')
1583
transport = MemoryTransport()
1584
branch = self.make_remote_branch(transport, client)
1585
config = branch.get_config()
1586
config.has_explicit_nickname()
1588
[('call', 'Branch.get_stacked_on_url', ('memory:///',)),
1589
('call_expecting_body', 'Branch.get_config_file', ('memory:///',))],
1592
def test_get_multi_line_branch_conf(self):
1593
# Make sure that multiple-line branch.conf files are supported
1595
# https://bugs.edge.launchpad.net/bzr/+bug/354075
1596
client = FakeClient()
1597
client.add_expected_call(
1598
'Branch.get_stacked_on_url', ('memory:///',),
1599
'error', ('NotStacked',),)
1600
client.add_success_response_with_body('a = 1\nb = 2\nc = 3\n', 'ok')
1601
transport = MemoryTransport()
1602
branch = self.make_remote_branch(transport, client)
1603
config = branch.get_config()
1604
self.assertEqual(u'2', config.get_user_option('b'))
1606
def test_set_option(self):
1607
client = FakeClient()
1608
client.add_expected_call(
1609
'Branch.get_stacked_on_url', ('memory:///',),
1610
'error', ('NotStacked',),)
1611
client.add_expected_call(
1612
'Branch.lock_write', ('memory:///', '', ''),
1613
'success', ('ok', 'branch token', 'repo token'))
1614
client.add_expected_call(
1615
'Branch.set_config_option', ('memory:///', 'branch token',
1616
'repo token', 'foo', 'bar', ''),
1618
client.add_expected_call(
1619
'Branch.unlock', ('memory:///', 'branch token', 'repo token'),
1621
transport = MemoryTransport()
1622
branch = self.make_remote_branch(transport, client)
1624
config = branch._get_config()
1625
config.set_option('foo', 'bar')
1627
self.assertFinished(client)
1629
def test_backwards_compat_set_option(self):
1630
self.setup_smart_server_with_call_log()
1631
branch = self.make_branch('.')
1632
verb = 'Branch.set_config_option'
1633
self.disable_verb(verb)
1635
self.addCleanup(branch.unlock)
1636
self.reset_smart_call_log()
1637
branch._get_config().set_option('value', 'name')
1638
self.assertLength(10, self.hpss_calls)
1639
self.assertEqual('value', branch._get_config().get_option('name'))
967
1642
class TestBranchLockWrite(RemoteBranchTestCase):
1243
1982
errors.UnexpectedSmartServerResponse,
1244
1983
repo.get_parent_map, ['a-revision-id'])
1985
def test_get_parent_map_negative_caches_missing_keys(self):
1986
self.setup_smart_server_with_call_log()
1987
repo = self.make_repository('foo')
1988
self.assertIsInstance(repo, RemoteRepository)
1990
self.addCleanup(repo.unlock)
1991
self.reset_smart_call_log()
1992
graph = repo.get_graph()
1993
self.assertEqual({},
1994
graph.get_parent_map(['some-missing', 'other-missing']))
1995
self.assertLength(1, self.hpss_calls)
1996
# No call if we repeat this
1997
self.reset_smart_call_log()
1998
graph = repo.get_graph()
1999
self.assertEqual({},
2000
graph.get_parent_map(['some-missing', 'other-missing']))
2001
self.assertLength(0, self.hpss_calls)
2002
# Asking for more unknown keys makes a request.
2003
self.reset_smart_call_log()
2004
graph = repo.get_graph()
2005
self.assertEqual({},
2006
graph.get_parent_map(['some-missing', 'other-missing',
2008
self.assertLength(1, self.hpss_calls)
2010
def disableExtraResults(self):
2011
old_flag = SmartServerRepositoryGetParentMap.no_extra_results
2012
SmartServerRepositoryGetParentMap.no_extra_results = True
2014
SmartServerRepositoryGetParentMap.no_extra_results = old_flag
2015
self.addCleanup(reset_values)
2017
def test_null_cached_missing_and_stop_key(self):
2018
self.setup_smart_server_with_call_log()
2019
# Make a branch with a single revision.
2020
builder = self.make_branch_builder('foo')
2021
builder.start_series()
2022
builder.build_snapshot('first', None, [
2023
('add', ('', 'root-id', 'directory', ''))])
2024
builder.finish_series()
2025
branch = builder.get_branch()
2026
repo = branch.repository
2027
self.assertIsInstance(repo, RemoteRepository)
2028
# Stop the server from sending extra results.
2029
self.disableExtraResults()
2031
self.addCleanup(repo.unlock)
2032
self.reset_smart_call_log()
2033
graph = repo.get_graph()
2034
# Query for 'first' and 'null:'. Because 'null:' is a parent of
2035
# 'first' it will be a candidate for the stop_keys of subsequent
2036
# requests, and because 'null:' was queried but not returned it will be
2037
# cached as missing.
2038
self.assertEqual({'first': ('null:',)},
2039
graph.get_parent_map(['first', 'null:']))
2040
# Now query for another key. This request will pass along a recipe of
2041
# start and stop keys describing the already cached results, and this
2042
# recipe's revision count must be correct (or else it will trigger an
2043
# error from the server).
2044
self.assertEqual({}, graph.get_parent_map(['another-key']))
2045
# This assertion guards against disableExtraResults silently failing to
2046
# work, thus invalidating the test.
2047
self.assertLength(2, self.hpss_calls)
2049
def test_get_parent_map_gets_ghosts_from_result(self):
2050
# asking for a revision should negatively cache close ghosts in its
2052
self.setup_smart_server_with_call_log()
2053
tree = self.make_branch_and_memory_tree('foo')
2056
builder = treebuilder.TreeBuilder()
2057
builder.start_tree(tree)
2059
builder.finish_tree()
2060
tree.set_parent_ids(['non-existant'], allow_leftmost_as_ghost=True)
2061
rev_id = tree.commit('')
2065
self.addCleanup(tree.unlock)
2066
repo = tree.branch.repository
2067
self.assertIsInstance(repo, RemoteRepository)
2069
repo.get_parent_map([rev_id])
2070
self.reset_smart_call_log()
2071
# Now asking for rev_id's ghost parent should not make calls
2072
self.assertEqual({}, repo.get_parent_map(['non-existant']))
2073
self.assertLength(0, self.hpss_calls)
1247
2076
class TestGetParentMapAllowsNew(tests.TestCaseWithTransport):
1249
2078
def test_allows_new_revisions(self):
1250
2079
"""get_parent_map's results can be updated by commit."""
1251
2080
smart_server = server.SmartTCPServer_for_testing()
1252
smart_server.setUp()
1253
self.addCleanup(smart_server.tearDown)
2081
self.start_server(smart_server)
1254
2082
self.make_branch('branch')
1255
2083
branch = Branch.open(smart_server.get_url() + '/branch')
1256
2084
tree = branch.create_checkout('tree', lightweight=True)
1332
2163
repo, client = self.setup_fake_client_and_repository(transport_path)
1333
2164
client.add_error_response('AnUnexpectedError')
1334
2165
e = self.assertRaises(errors.UnknownErrorFromSmartServer,
1335
self.applyDeprecated, one_four, repo.get_revision_graph, revid)
2166
repo._get_revision_graph, revid)
1336
2167
self.assertEqual(('AnUnexpectedError',), e.error_tuple)
2170
class TestRepositoryGetRevIdForRevno(TestRemoteRepository):
2173
repo, client = self.setup_fake_client_and_repository('quack')
2174
client.add_expected_call(
2175
'Repository.get_rev_id_for_revno', ('quack/', 5, (42, 'rev-foo')),
2176
'success', ('ok', 'rev-five'))
2177
result = repo.get_rev_id_for_revno(5, (42, 'rev-foo'))
2178
self.assertEqual((True, 'rev-five'), result)
2179
self.assertFinished(client)
2181
def test_history_incomplete(self):
2182
repo, client = self.setup_fake_client_and_repository('quack')
2183
client.add_expected_call(
2184
'Repository.get_rev_id_for_revno', ('quack/', 5, (42, 'rev-foo')),
2185
'success', ('history-incomplete', 10, 'rev-ten'))
2186
result = repo.get_rev_id_for_revno(5, (42, 'rev-foo'))
2187
self.assertEqual((False, (10, 'rev-ten')), result)
2188
self.assertFinished(client)
2190
def test_history_incomplete_with_fallback(self):
2191
"""A 'history-incomplete' response causes the fallback repository to be
2192
queried too, if one is set.
2194
# Make a repo with a fallback repo, both using a FakeClient.
2195
format = remote.response_tuple_to_repo_format(
2196
('yes', 'no', 'yes', 'fake-network-name'))
2197
repo, client = self.setup_fake_client_and_repository('quack')
2198
repo._format = format
2199
fallback_repo, ignored = self.setup_fake_client_and_repository(
2201
fallback_repo._client = client
2202
repo.add_fallback_repository(fallback_repo)
2203
# First the client should ask the primary repo
2204
client.add_expected_call(
2205
'Repository.get_rev_id_for_revno', ('quack/', 1, (42, 'rev-foo')),
2206
'success', ('history-incomplete', 2, 'rev-two'))
2207
# Then it should ask the fallback, using revno/revid from the
2208
# history-incomplete response as the known revno/revid.
2209
client.add_expected_call(
2210
'Repository.get_rev_id_for_revno',('fallback/', 1, (2, 'rev-two')),
2211
'success', ('ok', 'rev-one'))
2212
result = repo.get_rev_id_for_revno(1, (42, 'rev-foo'))
2213
self.assertEqual((True, 'rev-one'), result)
2214
self.assertFinished(client)
2216
def test_nosuchrevision(self):
2217
# 'nosuchrevision' is returned when the known-revid is not found in the
2218
# remote repo. The client translates that response to NoSuchRevision.
2219
repo, client = self.setup_fake_client_and_repository('quack')
2220
client.add_expected_call(
2221
'Repository.get_rev_id_for_revno', ('quack/', 5, (42, 'rev-foo')),
2222
'error', ('nosuchrevision', 'rev-foo'))
2224
errors.NoSuchRevision,
2225
repo.get_rev_id_for_revno, 5, (42, 'rev-foo'))
2226
self.assertFinished(client)
2228
def test_branch_fallback_locking(self):
2229
"""RemoteBranch.get_rev_id takes a read lock, and tries to call the
2230
get_rev_id_for_revno verb. If the verb is unknown the VFS fallback
2231
will be invoked, which will fail if the repo is unlocked.
2233
self.setup_smart_server_with_call_log()
2234
tree = self.make_branch_and_memory_tree('.')
2236
rev1 = tree.commit('First')
2237
rev2 = tree.commit('Second')
2239
branch = tree.branch
2240
self.assertFalse(branch.is_locked())
2241
self.reset_smart_call_log()
2242
verb = 'Repository.get_rev_id_for_revno'
2243
self.disable_verb(verb)
2244
self.assertEqual(rev1, branch.get_rev_id(1))
2245
self.assertLength(1, [call for call in self.hpss_calls if
2246
call.call.method == verb])
1339
2249
class TestRepositoryIsShared(TestRemoteRepository):
1341
2251
def test_is_shared(self):
1430
2366
self.assertEqual([], client._calls)
2369
class TestRepositoryInsertStreamBase(TestRemoteRepository):
2370
"""Base class for Repository.insert_stream and .insert_stream_1.19
2374
def checkInsertEmptyStream(self, repo, client):
2375
"""Insert an empty stream, checking the result.
2377
This checks that there are no resume_tokens or missing_keys, and that
2378
the client is finished.
2380
sink = repo._get_sink()
2381
fmt = repository.RepositoryFormat.get_default_format()
2382
resume_tokens, missing_keys = sink.insert_stream([], fmt, [])
2383
self.assertEqual([], resume_tokens)
2384
self.assertEqual(set(), missing_keys)
2385
self.assertFinished(client)
2388
class TestRepositoryInsertStream(TestRepositoryInsertStreamBase):
2389
"""Tests for using Repository.insert_stream verb when the _1.19 variant is
2392
This test case is very similar to TestRepositoryInsertStream_1_19.
2396
TestRemoteRepository.setUp(self)
2397
self.disable_verb('Repository.insert_stream_1.19')
2399
def test_unlocked_repo(self):
2400
transport_path = 'quack'
2401
repo, client = self.setup_fake_client_and_repository(transport_path)
2402
client.add_expected_call(
2403
'Repository.insert_stream_1.19', ('quack/', ''),
2404
'unknown', ('Repository.insert_stream_1.19',))
2405
client.add_expected_call(
2406
'Repository.insert_stream', ('quack/', ''),
2408
client.add_expected_call(
2409
'Repository.insert_stream', ('quack/', ''),
2411
self.checkInsertEmptyStream(repo, client)
2413
def test_locked_repo_with_no_lock_token(self):
2414
transport_path = 'quack'
2415
repo, client = self.setup_fake_client_and_repository(transport_path)
2416
client.add_expected_call(
2417
'Repository.lock_write', ('quack/', ''),
2418
'success', ('ok', ''))
2419
client.add_expected_call(
2420
'Repository.insert_stream_1.19', ('quack/', ''),
2421
'unknown', ('Repository.insert_stream_1.19',))
2422
client.add_expected_call(
2423
'Repository.insert_stream', ('quack/', ''),
2425
client.add_expected_call(
2426
'Repository.insert_stream', ('quack/', ''),
2429
self.checkInsertEmptyStream(repo, client)
2431
def test_locked_repo_with_lock_token(self):
2432
transport_path = 'quack'
2433
repo, client = self.setup_fake_client_and_repository(transport_path)
2434
client.add_expected_call(
2435
'Repository.lock_write', ('quack/', ''),
2436
'success', ('ok', 'a token'))
2437
client.add_expected_call(
2438
'Repository.insert_stream_1.19', ('quack/', '', 'a token'),
2439
'unknown', ('Repository.insert_stream_1.19',))
2440
client.add_expected_call(
2441
'Repository.insert_stream_locked', ('quack/', '', 'a token'),
2443
client.add_expected_call(
2444
'Repository.insert_stream_locked', ('quack/', '', 'a token'),
2447
self.checkInsertEmptyStream(repo, client)
2449
def test_stream_with_inventory_deltas(self):
2450
"""'inventory-deltas' substreams cannot be sent to the
2451
Repository.insert_stream verb, because not all servers that implement
2452
that verb will accept them. So when one is encountered the RemoteSink
2453
immediately stops using that verb and falls back to VFS insert_stream.
2455
transport_path = 'quack'
2456
repo, client = self.setup_fake_client_and_repository(transport_path)
2457
client.add_expected_call(
2458
'Repository.insert_stream_1.19', ('quack/', ''),
2459
'unknown', ('Repository.insert_stream_1.19',))
2460
client.add_expected_call(
2461
'Repository.insert_stream', ('quack/', ''),
2463
client.add_expected_call(
2464
'Repository.insert_stream', ('quack/', ''),
2466
# Create a fake real repository for insert_stream to fall back on, so
2467
# that we can directly see the records the RemoteSink passes to the
2472
def insert_stream(self, stream, src_format, resume_tokens):
2473
for substream_kind, substream in stream:
2474
self.records.append(
2475
(substream_kind, [record.key for record in substream]))
2476
return ['fake tokens'], ['fake missing keys']
2477
fake_real_sink = FakeRealSink()
2478
class FakeRealRepository:
2479
def _get_sink(self):
2480
return fake_real_sink
2481
def is_in_write_group(self):
2483
def refresh_data(self):
2485
repo._real_repository = FakeRealRepository()
2486
sink = repo._get_sink()
2487
fmt = repository.RepositoryFormat.get_default_format()
2488
stream = self.make_stream_with_inv_deltas(fmt)
2489
resume_tokens, missing_keys = sink.insert_stream(stream, fmt, [])
2490
# Every record from the first inventory delta should have been sent to
2492
expected_records = [
2493
('inventory-deltas', [('rev2',), ('rev3',)]),
2494
('texts', [('some-rev', 'some-file')])]
2495
self.assertEqual(expected_records, fake_real_sink.records)
2496
# The return values from the real sink's insert_stream are propagated
2497
# back to the original caller.
2498
self.assertEqual(['fake tokens'], resume_tokens)
2499
self.assertEqual(['fake missing keys'], missing_keys)
2500
self.assertFinished(client)
2502
def make_stream_with_inv_deltas(self, fmt):
2503
"""Make a simple stream with an inventory delta followed by more
2504
records and more substreams to test that all records and substreams
2505
from that point on are used.
2507
This sends, in order:
2508
* inventories substream: rev1, rev2, rev3. rev2 and rev3 are
2510
* texts substream: (some-rev, some-file)
2512
# Define a stream using generators so that it isn't rewindable.
2513
inv = inventory.Inventory(revision_id='rev1')
2514
inv.root.revision = 'rev1'
2515
def stream_with_inv_delta():
2516
yield ('inventories', inventories_substream())
2517
yield ('inventory-deltas', inventory_delta_substream())
2519
versionedfile.FulltextContentFactory(
2520
('some-rev', 'some-file'), (), None, 'content')])
2521
def inventories_substream():
2522
# An empty inventory fulltext. This will be streamed normally.
2523
text = fmt._serializer.write_inventory_to_string(inv)
2524
yield versionedfile.FulltextContentFactory(
2525
('rev1',), (), None, text)
2526
def inventory_delta_substream():
2527
# An inventory delta. This can't be streamed via this verb, so it
2528
# will trigger a fallback to VFS insert_stream.
2529
entry = inv.make_entry(
2530
'directory', 'newdir', inv.root.file_id, 'newdir-id')
2531
entry.revision = 'ghost'
2532
delta = [(None, 'newdir', 'newdir-id', entry)]
2533
serializer = inventory_delta.InventoryDeltaSerializer(
2534
versioned_root=True, tree_references=False)
2535
lines = serializer.delta_to_lines('rev1', 'rev2', delta)
2536
yield versionedfile.ChunkedContentFactory(
2537
('rev2',), (('rev1',)), None, lines)
2539
lines = serializer.delta_to_lines('rev1', 'rev3', delta)
2540
yield versionedfile.ChunkedContentFactory(
2541
('rev3',), (('rev1',)), None, lines)
2542
return stream_with_inv_delta()
2545
class TestRepositoryInsertStream_1_19(TestRepositoryInsertStreamBase):
2547
def test_unlocked_repo(self):
2548
transport_path = 'quack'
2549
repo, client = self.setup_fake_client_and_repository(transport_path)
2550
client.add_expected_call(
2551
'Repository.insert_stream_1.19', ('quack/', ''),
2553
client.add_expected_call(
2554
'Repository.insert_stream_1.19', ('quack/', ''),
2556
self.checkInsertEmptyStream(repo, client)
2558
def test_locked_repo_with_no_lock_token(self):
2559
transport_path = 'quack'
2560
repo, client = self.setup_fake_client_and_repository(transport_path)
2561
client.add_expected_call(
2562
'Repository.lock_write', ('quack/', ''),
2563
'success', ('ok', ''))
2564
client.add_expected_call(
2565
'Repository.insert_stream_1.19', ('quack/', ''),
2567
client.add_expected_call(
2568
'Repository.insert_stream_1.19', ('quack/', ''),
2571
self.checkInsertEmptyStream(repo, client)
2573
def test_locked_repo_with_lock_token(self):
2574
transport_path = 'quack'
2575
repo, client = self.setup_fake_client_and_repository(transport_path)
2576
client.add_expected_call(
2577
'Repository.lock_write', ('quack/', ''),
2578
'success', ('ok', 'a token'))
2579
client.add_expected_call(
2580
'Repository.insert_stream_1.19', ('quack/', '', 'a token'),
2582
client.add_expected_call(
2583
'Repository.insert_stream_1.19', ('quack/', '', 'a token'),
2586
self.checkInsertEmptyStream(repo, client)
1433
2589
class TestRepositoryTarball(TestRemoteRepository):
1435
2591
# This is a canned tarball reponse we can validate against
1793
2967
remote_repo.unlock()
1795
2969
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')
2970
"""Get stacked_upon and stacked branches with content in each."""
2971
self.setup_smart_server_with_call_log()
2972
tree1 = self.make_branch_and_tree('tree1', format='1.9')
1800
2973
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')
2974
tree2 = tree1.branch.bzrdir.sprout('tree2', stacked=True
2975
).open_workingtree()
2976
local_tree = tree2.branch.create_checkout('local')
2977
local_tree.commit('local changes make me feel good.')
2978
branch2 = Branch.open(self.get_url('tree2'))
1804
2979
branch2.lock_read()
1805
2980
self.addCleanup(branch2.unlock)
2981
return tree1.branch, branch2
1808
2983
def test_stacked_get_parent_map(self):
1809
2984
# the public implementation of get_parent_map obeys stacking
1810
branch = self.prepare_stacked_remote_branch()
2985
_, branch = self.prepare_stacked_remote_branch()
1811
2986
repo = branch.repository
1812
2987
self.assertEqual(['rev1'], repo.get_parent_map(['rev1']).keys())
1814
2989
def test_unstacked_get_parent_map(self):
1815
2990
# _unstacked_provider.get_parent_map ignores stacking
1816
branch = self.prepare_stacked_remote_branch()
2991
_, branch = self.prepare_stacked_remote_branch()
1817
2992
provider = branch.repository._unstacked_provider
1818
2993
self.assertEqual([], provider.get_parent_map(['rev1']).keys())
2995
def fetch_stream_to_rev_order(self, stream):
2997
for kind, substream in stream:
2998
if not kind == 'revisions':
3001
for content in substream:
3002
result.append(content.key[-1])
3005
def get_ordered_revs(self, format, order, branch_factory=None):
3006
"""Get a list of the revisions in a stream to format format.
3008
:param format: The format of the target.
3009
:param order: the order that target should have requested.
3010
:param branch_factory: A callable to create a trunk and stacked branch
3011
to fetch from. If none, self.prepare_stacked_remote_branch is used.
3012
:result: The revision ids in the stream, in the order seen,
3013
the topological order of revisions in the source.
3015
unordered_format = bzrdir.format_registry.get(format)()
3016
target_repository_format = unordered_format.repository_format
3018
self.assertEqual(order, target_repository_format._fetch_order)
3019
if branch_factory is None:
3020
branch_factory = self.prepare_stacked_remote_branch
3021
_, stacked = branch_factory()
3022
source = stacked.repository._get_source(target_repository_format)
3023
tip = stacked.last_revision()
3024
revs = stacked.repository.get_ancestry(tip)
3025
search = graph.PendingAncestryResult([tip], stacked.repository)
3026
self.reset_smart_call_log()
3027
stream = source.get_stream(search)
3030
# We trust that if a revision is in the stream the rest of the new
3031
# content for it is too, as per our main fetch tests; here we are
3032
# checking that the revisions are actually included at all, and their
3034
return self.fetch_stream_to_rev_order(stream), revs
3036
def test_stacked_get_stream_unordered(self):
3037
# Repository._get_source.get_stream() from a stacked repository with
3038
# unordered yields the full data from both stacked and stacked upon
3040
rev_ord, expected_revs = self.get_ordered_revs('1.9', 'unordered')
3041
self.assertEqual(set(expected_revs), set(rev_ord))
3042
# Getting unordered results should have made a streaming data request
3043
# from the server, then one from the backing branch.
3044
self.assertLength(2, self.hpss_calls)
3046
def test_stacked_on_stacked_get_stream_unordered(self):
3047
# Repository._get_source.get_stream() from a stacked repository which
3048
# is itself stacked yields the full data from all three sources.
3049
def make_stacked_stacked():
3050
_, stacked = self.prepare_stacked_remote_branch()
3051
tree = stacked.bzrdir.sprout('tree3', stacked=True
3052
).open_workingtree()
3053
local_tree = tree.branch.create_checkout('local-tree3')
3054
local_tree.commit('more local changes are better')
3055
branch = Branch.open(self.get_url('tree3'))
3057
self.addCleanup(branch.unlock)
3059
rev_ord, expected_revs = self.get_ordered_revs('1.9', 'unordered',
3060
branch_factory=make_stacked_stacked)
3061
self.assertEqual(set(expected_revs), set(rev_ord))
3062
# Getting unordered results should have made a streaming data request
3063
# from the server, and one from each backing repo
3064
self.assertLength(3, self.hpss_calls)
3066
def test_stacked_get_stream_topological(self):
3067
# Repository._get_source.get_stream() from a stacked repository with
3068
# topological sorting yields the full data from both stacked and
3069
# stacked upon sources in topological order.
3070
rev_ord, expected_revs = self.get_ordered_revs('knit', 'topological')
3071
self.assertEqual(expected_revs, rev_ord)
3072
# Getting topological sort requires VFS calls still - one of which is
3073
# pushing up from the bound branch.
3074
self.assertLength(13, self.hpss_calls)
3076
def test_stacked_get_stream_groupcompress(self):
3077
# Repository._get_source.get_stream() from a stacked repository with
3078
# groupcompress sorting yields the full data from both stacked and
3079
# stacked upon sources in groupcompress order.
3080
raise tests.TestSkipped('No groupcompress ordered format available')
3081
rev_ord, expected_revs = self.get_ordered_revs('dev5', 'groupcompress')
3082
self.assertEqual(expected_revs, reversed(rev_ord))
3083
# Getting unordered results should have made a streaming data request
3084
# from the backing branch, and one from the stacked on branch.
3085
self.assertLength(2, self.hpss_calls)
3087
def test_stacked_pull_more_than_stacking_has_bug_360791(self):
3088
# When pulling some fixed amount of content that is more than the
3089
# source has (because some is coming from a fallback branch, no error
3090
# should be received. This was reported as bug 360791.
3091
# Need three branches: a trunk, a stacked branch, and a preexisting
3092
# branch pulling content from stacked and trunk.
3093
self.setup_smart_server_with_call_log()
3094
trunk = self.make_branch_and_tree('trunk', format="1.9-rich-root")
3095
r1 = trunk.commit('start')
3096
stacked_branch = trunk.branch.create_clone_on_transport(
3097
self.get_transport('stacked'), stacked_on=trunk.branch.base)
3098
local = self.make_branch('local', format='1.9-rich-root')
3099
local.repository.fetch(stacked_branch.repository,
3100
stacked_branch.last_revision())
1821
3103
class TestRemoteBranchEffort(tests.TestCaseWithTransport):