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_branch_format_supports_stacking(self):
139
self.make_branch('unstackable', format='pack-0.92')
140
b = BzrDir.open_from_transport(t.clone('unstackable')).open_branch()
141
self.assertFalse(b._format.supports_stacking())
142
self.make_branch('stackable', format='1.9')
143
b = BzrDir.open_from_transport(t.clone('stackable')).open_branch()
144
self.assertTrue(b._format.supports_stacking())
146
def test_remote_repo_format_supports_external_references(self):
148
bd = self.make_bzrdir('unstackable', format='pack-0.92')
149
r = bd.create_repository()
150
self.assertFalse(r._format.supports_external_lookups)
151
r = BzrDir.open_from_transport(t.clone('unstackable')).open_repository()
152
self.assertFalse(r._format.supports_external_lookups)
153
bd = self.make_bzrdir('stackable', format='1.9')
154
r = bd.create_repository()
155
self.assertTrue(r._format.supports_external_lookups)
156
r = BzrDir.open_from_transport(t.clone('stackable')).open_repository()
157
self.assertTrue(r._format.supports_external_lookups)
159
def test_remote_branch_set_append_revisions_only(self):
160
# Make a format 1.9 branch, which supports append_revisions_only
161
branch = self.make_branch('branch', format='1.9')
162
config = branch.get_config()
163
branch.set_append_revisions_only(True)
165
'True', config.get_user_option('append_revisions_only'))
166
branch.set_append_revisions_only(False)
168
'False', config.get_user_option('append_revisions_only'))
170
def test_remote_branch_set_append_revisions_only_upgrade_reqd(self):
171
branch = self.make_branch('branch', format='knit')
172
config = branch.get_config()
174
errors.UpgradeRequired, branch.set_append_revisions_only, True)
148
177
class FakeProtocol(object):
359
418
AssertionError, client_medium._remember_remote_is_before, (1, 9))
362
class TestBzrDirOpenBranch(tests.TestCase):
421
class TestBzrDirCloningMetaDir(TestRemote):
423
def test_backwards_compat(self):
424
self.setup_smart_server_with_call_log()
425
a_dir = self.make_bzrdir('.')
426
self.reset_smart_call_log()
427
verb = 'BzrDir.cloning_metadir'
428
self.disable_verb(verb)
429
format = a_dir.cloning_metadir()
430
call_count = len([call for call in self.hpss_calls if
431
call.call.method == verb])
432
self.assertEqual(1, call_count)
434
def test_branch_reference(self):
435
transport = self.get_transport('quack')
436
referenced = self.make_branch('referenced')
437
expected = referenced.bzrdir.cloning_metadir()
438
client = FakeClient(transport.base)
439
client.add_expected_call(
440
'BzrDir.cloning_metadir', ('quack/', 'False'),
441
'error', ('BranchReference',)),
442
client.add_expected_call(
443
'BzrDir.open_branchV2', ('quack/',),
444
'success', ('ref', self.get_url('referenced'))),
445
a_bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
447
result = a_bzrdir.cloning_metadir()
448
# We should have got a control dir matching the referenced branch.
449
self.assertEqual(bzrdir.BzrDirMetaFormat1, type(result))
450
self.assertEqual(expected._repository_format, result._repository_format)
451
self.assertEqual(expected._branch_format, result._branch_format)
452
self.assertFinished(client)
454
def test_current_server(self):
455
transport = self.get_transport('.')
456
transport = transport.clone('quack')
457
self.make_bzrdir('quack')
458
client = FakeClient(transport.base)
459
reference_bzrdir_format = bzrdir.format_registry.get('default')()
460
control_name = reference_bzrdir_format.network_name()
461
client.add_expected_call(
462
'BzrDir.cloning_metadir', ('quack/', 'False'),
463
'success', (control_name, '', ('branch', ''))),
464
a_bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
466
result = a_bzrdir.cloning_metadir()
467
# We should have got a reference control dir with default branch and
468
# repository formats.
469
# This pokes a little, just to be sure.
470
self.assertEqual(bzrdir.BzrDirMetaFormat1, type(result))
471
self.assertEqual(None, result._repository_format)
472
self.assertEqual(None, result._branch_format)
473
self.assertFinished(client)
476
class TestBzrDirOpen(TestRemote):
478
def make_fake_client_and_transport(self, path='quack'):
479
transport = MemoryTransport()
480
transport.mkdir(path)
481
transport = transport.clone(path)
482
client = FakeClient(transport.base)
483
return client, transport
485
def test_absent(self):
486
client, transport = self.make_fake_client_and_transport()
487
client.add_expected_call(
488
'BzrDir.open_2.1', ('quack/',), 'success', ('no',))
489
self.assertRaises(errors.NotBranchError, RemoteBzrDir, transport,
490
remote.RemoteBzrDirFormat(), _client=client, _force_probe=True)
491
self.assertFinished(client)
493
def test_present_without_workingtree(self):
494
client, transport = self.make_fake_client_and_transport()
495
client.add_expected_call(
496
'BzrDir.open_2.1', ('quack/',), 'success', ('yes', 'no'))
497
bd = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
498
_client=client, _force_probe=True)
499
self.assertIsInstance(bd, RemoteBzrDir)
500
self.assertFalse(bd.has_workingtree())
501
self.assertRaises(errors.NoWorkingTree, bd.open_workingtree)
502
self.assertFinished(client)
504
def test_present_with_workingtree(self):
505
client, transport = self.make_fake_client_and_transport()
506
client.add_expected_call(
507
'BzrDir.open_2.1', ('quack/',), 'success', ('yes', 'yes'))
508
bd = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
509
_client=client, _force_probe=True)
510
self.assertIsInstance(bd, RemoteBzrDir)
511
self.assertTrue(bd.has_workingtree())
512
self.assertRaises(errors.NotLocalUrl, bd.open_workingtree)
513
self.assertFinished(client)
515
def test_backwards_compat(self):
516
client, transport = self.make_fake_client_and_transport()
517
client.add_expected_call(
518
'BzrDir.open_2.1', ('quack/',), 'unknown', ('BzrDir.open_2.1',))
519
client.add_expected_call(
520
'BzrDir.open', ('quack/',), 'success', ('yes',))
521
bd = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
522
_client=client, _force_probe=True)
523
self.assertIsInstance(bd, RemoteBzrDir)
524
self.assertFinished(client)
527
class TestBzrDirOpenBranch(TestRemote):
529
def test_backwards_compat(self):
530
self.setup_smart_server_with_call_log()
531
self.make_branch('.')
532
a_dir = BzrDir.open(self.get_url('.'))
533
self.reset_smart_call_log()
534
verb = 'BzrDir.open_branchV2'
535
self.disable_verb(verb)
536
format = a_dir.open_branch()
537
call_count = len([call for call in self.hpss_calls if
538
call.call.method == verb])
539
self.assertEqual(1, call_count)
364
541
def test_branch_present(self):
542
reference_format = self.get_repo_format()
543
network_name = reference_format.network_name()
544
branch_network_name = self.get_branch_format().network_name()
365
545
transport = MemoryTransport()
366
546
transport.mkdir('quack')
367
547
transport = transport.clone('quack')
368
548
client = FakeClient(transport.base)
369
549
client.add_expected_call(
370
'BzrDir.open_branch', ('quack/',),
371
'success', ('ok', ''))
550
'BzrDir.open_branchV2', ('quack/',),
551
'success', ('branch', branch_network_name))
372
552
client.add_expected_call(
373
'BzrDir.find_repositoryV2', ('quack/',),
374
'success', ('ok', '', 'no', 'no', 'no'))
553
'BzrDir.find_repositoryV3', ('quack/',),
554
'success', ('ok', '', 'no', 'no', 'no', network_name))
375
555
client.add_expected_call(
376
556
'Branch.get_stacked_on_url', ('quack/',),
377
557
'error', ('NotStacked',))
378
bzrdir = RemoteBzrDir(transport, _client=client)
558
bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
379
560
result = bzrdir.open_branch()
380
561
self.assertIsInstance(result, RemoteBranch)
381
562
self.assertEqual(bzrdir, result.bzrdir)
382
client.finished_test()
563
self.assertFinished(client)
384
565
def test_branch_missing(self):
385
566
transport = MemoryTransport()
468
659
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')
662
class TestBzrDirCreateBranch(TestRemote):
664
def test_backwards_compat(self):
665
self.setup_smart_server_with_call_log()
666
repo = self.make_repository('.')
667
self.reset_smart_call_log()
668
self.disable_verb('BzrDir.create_branch')
669
branch = repo.bzrdir.create_branch()
670
create_branch_call_count = len([call for call in self.hpss_calls if
671
call.call.method == 'BzrDir.create_branch'])
672
self.assertEqual(1, create_branch_call_count)
674
def test_current_server(self):
675
transport = self.get_transport('.')
676
transport = transport.clone('quack')
677
self.make_repository('quack')
678
client = FakeClient(transport.base)
679
reference_bzrdir_format = bzrdir.format_registry.get('default')()
680
reference_format = reference_bzrdir_format.get_branch_format()
681
network_name = reference_format.network_name()
682
reference_repo_fmt = reference_bzrdir_format.repository_format
683
reference_repo_name = reference_repo_fmt.network_name()
684
client.add_expected_call(
685
'BzrDir.create_branch', ('quack/', network_name),
686
'success', ('ok', network_name, '', 'no', 'no', 'yes',
687
reference_repo_name))
688
a_bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
690
branch = a_bzrdir.create_branch()
691
# We should have got a remote branch
692
self.assertIsInstance(branch, remote.RemoteBranch)
693
# its format should have the settings from the response
694
format = branch._format
695
self.assertEqual(network_name, format.network_name())
698
class TestBzrDirCreateRepository(TestRemote):
700
def test_backwards_compat(self):
701
self.setup_smart_server_with_call_log()
702
bzrdir = self.make_bzrdir('.')
703
self.reset_smart_call_log()
704
self.disable_verb('BzrDir.create_repository')
705
repo = bzrdir.create_repository()
706
create_repo_call_count = len([call for call in self.hpss_calls if
707
call.call.method == 'BzrDir.create_repository'])
708
self.assertEqual(1, create_repo_call_count)
710
def test_current_server(self):
711
transport = self.get_transport('.')
712
transport = transport.clone('quack')
713
self.make_bzrdir('quack')
714
client = FakeClient(transport.base)
715
reference_bzrdir_format = bzrdir.format_registry.get('default')()
716
reference_format = reference_bzrdir_format.repository_format
717
network_name = reference_format.network_name()
718
client.add_expected_call(
719
'BzrDir.create_repository', ('quack/',
720
'Bazaar repository format 2a (needs bzr 1.16 or later)\n',
722
'success', ('ok', 'yes', 'yes', 'yes', network_name))
723
a_bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
725
repo = a_bzrdir.create_repository()
726
# We should have got a remote repository
727
self.assertIsInstance(repo, remote.RemoteRepository)
728
# its format should have the settings from the response
729
format = repo._format
730
self.assertTrue(format.rich_root_data)
731
self.assertTrue(format.supports_tree_reference)
732
self.assertTrue(format.supports_external_lookups)
733
self.assertEqual(network_name, format.network_name())
736
class TestBzrDirOpenRepository(TestRemote):
738
def test_backwards_compat_1_2_3(self):
739
# fallback all the way to the first version.
740
reference_format = self.get_repo_format()
741
network_name = reference_format.network_name()
742
server_url = 'bzr://example.com/'
743
self.permit_url(server_url)
744
client = FakeClient(server_url)
745
client.add_unknown_method_response('BzrDir.find_repositoryV3')
746
client.add_unknown_method_response('BzrDir.find_repositoryV2')
479
747
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/',))],
748
# A real repository instance will be created to determine the network
750
client.add_success_response_with_body(
751
"Bazaar-NG meta directory, format 1\n", 'ok')
752
client.add_success_response_with_body(
753
reference_format.get_format_string(), 'ok')
754
# PackRepository wants to do a stat
755
client.add_success_response('stat', '0', '65535')
756
remote_transport = RemoteTransport(server_url + 'quack/', medium=False,
758
bzrdir = RemoteBzrDir(remote_transport, remote.RemoteBzrDirFormat(),
760
repo = bzrdir.open_repository()
762
[('call', 'BzrDir.find_repositoryV3', ('quack/',)),
763
('call', 'BzrDir.find_repositoryV2', ('quack/',)),
764
('call', 'BzrDir.find_repository', ('quack/',)),
765
('call_expecting_body', 'get', ('/quack/.bzr/branch-format',)),
766
('call_expecting_body', 'get', ('/quack/.bzr/repository/format',)),
767
('call', 'stat', ('/quack/.bzr/repository',)),
770
self.assertEqual(network_name, repo._format.network_name())
772
def test_backwards_compat_2(self):
773
# fallback to find_repositoryV2
774
reference_format = self.get_repo_format()
775
network_name = reference_format.network_name()
776
server_url = 'bzr://example.com/'
777
self.permit_url(server_url)
778
client = FakeClient(server_url)
779
client.add_unknown_method_response('BzrDir.find_repositoryV3')
780
client.add_success_response('ok', '', 'no', 'no', 'no')
781
# A real repository instance will be created to determine the network
783
client.add_success_response_with_body(
784
"Bazaar-NG meta directory, format 1\n", 'ok')
785
client.add_success_response_with_body(
786
reference_format.get_format_string(), 'ok')
787
# PackRepository wants to do a stat
788
client.add_success_response('stat', '0', '65535')
789
remote_transport = RemoteTransport(server_url + 'quack/', medium=False,
791
bzrdir = RemoteBzrDir(remote_transport, remote.RemoteBzrDirFormat(),
793
repo = bzrdir.open_repository()
795
[('call', 'BzrDir.find_repositoryV3', ('quack/',)),
796
('call', 'BzrDir.find_repositoryV2', ('quack/',)),
797
('call_expecting_body', 'get', ('/quack/.bzr/branch-format',)),
798
('call_expecting_body', 'get', ('/quack/.bzr/repository/format',)),
799
('call', 'stat', ('/quack/.bzr/repository',)),
802
self.assertEqual(network_name, repo._format.network_name())
804
def test_current_server(self):
805
reference_format = self.get_repo_format()
806
network_name = reference_format.network_name()
807
transport = MemoryTransport()
808
transport.mkdir('quack')
809
transport = transport.clone('quack')
810
client = FakeClient(transport.base)
811
client.add_success_response('ok', '', 'no', 'no', 'no', network_name)
812
bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
814
repo = bzrdir.open_repository()
816
[('call', 'BzrDir.find_repositoryV3', ('quack/',))],
818
self.assertEqual(network_name, repo._format.network_name())
821
class TestBzrDirFormatInitializeEx(TestRemote):
823
def test_success(self):
824
"""Simple test for typical successful call."""
825
fmt = bzrdir.RemoteBzrDirFormat()
826
default_format_name = BzrDirFormat.get_default_format().network_name()
827
transport = self.get_transport()
828
client = FakeClient(transport.base)
829
client.add_expected_call(
830
'BzrDirFormat.initialize_ex_1.16',
831
(default_format_name, 'path', 'False', 'False', 'False', '',
832
'', '', '', 'False'),
834
('.', 'no', 'no', 'yes', 'repo fmt', 'repo bzrdir fmt',
835
'bzrdir fmt', 'False', '', '', 'repo lock token'))
836
# XXX: It would be better to call fmt.initialize_on_transport_ex, but
837
# it's currently hard to test that without supplying a real remote
838
# transport connected to a real server.
839
result = fmt._initialize_on_transport_ex_rpc(client, 'path',
840
transport, False, False, False, None, None, None, None, False)
841
self.assertFinished(client)
843
def test_error(self):
844
"""Error responses are translated, e.g. 'PermissionDenied' raises the
845
corresponding error from the client.
847
fmt = bzrdir.RemoteBzrDirFormat()
848
default_format_name = BzrDirFormat.get_default_format().network_name()
849
transport = self.get_transport()
850
client = FakeClient(transport.base)
851
client.add_expected_call(
852
'BzrDirFormat.initialize_ex_1.16',
853
(default_format_name, 'path', 'False', 'False', 'False', '',
854
'', '', '', 'False'),
856
('PermissionDenied', 'path', 'extra info'))
857
# XXX: It would be better to call fmt.initialize_on_transport_ex, but
858
# it's currently hard to test that without supplying a real remote
859
# transport connected to a real server.
860
err = self.assertRaises(errors.PermissionDenied,
861
fmt._initialize_on_transport_ex_rpc, client, 'path', transport,
862
False, False, False, None, None, None, None, False)
863
self.assertEqual('path', err.path)
864
self.assertEqual(': extra info', err.extra)
865
self.assertFinished(client)
867
def test_error_from_real_server(self):
868
"""Integration test for error translation."""
869
transport = self.make_smart_server('foo')
870
transport = transport.clone('no-such-path')
871
fmt = bzrdir.RemoteBzrDirFormat()
872
err = self.assertRaises(errors.NoSuchFile,
873
fmt.initialize_on_transport_ex, transport, create_prefix=False)
488
876
class OldSmartClient(object):
513
901
return OldSmartClient()
516
class RemoteBranchTestCase(tests.TestCase):
904
class RemoteBzrDirTestCase(TestRemote):
906
def make_remote_bzrdir(self, transport, client):
907
"""Make a RemotebzrDir using 'client' as the _client."""
908
return RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
912
class RemoteBranchTestCase(RemoteBzrDirTestCase):
914
def lock_remote_branch(self, branch):
915
"""Trick a RemoteBranch into thinking it is locked."""
916
branch._lock_mode = 'w'
917
branch._lock_count = 2
918
branch._lock_token = 'branch token'
919
branch._repo_lock_token = 'repo token'
920
branch.repository._lock_mode = 'w'
921
branch.repository._lock_count = 2
922
branch.repository._lock_token = 'repo token'
518
924
def make_remote_branch(self, transport, client):
519
925
"""Make a RemoteBranch using 'client' as its _SmartClient.
521
927
A RemoteBzrDir and RemoteRepository will also be created to fill out
522
928
the RemoteBranch, albeit with stub values for some of their attributes.
524
930
# we do not want bzrdir to make any remote calls, so use False as its
525
931
# _client. If it tries to make a remote call, this will fail
527
bzrdir = RemoteBzrDir(transport, _client=False)
933
bzrdir = self.make_remote_bzrdir(transport, False)
528
934
repo = RemoteRepository(bzrdir, None, _client=client)
529
return RemoteBranch(bzrdir, repo, _client=client)
935
branch_format = self.get_branch_format()
936
format = RemoteBranchFormat(network_name=branch_format.network_name())
937
return RemoteBranch(bzrdir, repo, _client=client, format=format)
940
class TestBranchGetParent(RemoteBranchTestCase):
942
def test_no_parent(self):
943
# in an empty branch we decode the response properly
944
transport = MemoryTransport()
945
client = FakeClient(transport.base)
946
client.add_expected_call(
947
'Branch.get_stacked_on_url', ('quack/',),
948
'error', ('NotStacked',))
949
client.add_expected_call(
950
'Branch.get_parent', ('quack/',),
952
transport.mkdir('quack')
953
transport = transport.clone('quack')
954
branch = self.make_remote_branch(transport, client)
955
result = branch.get_parent()
956
self.assertFinished(client)
957
self.assertEqual(None, result)
959
def test_parent_relative(self):
960
transport = MemoryTransport()
961
client = FakeClient(transport.base)
962
client.add_expected_call(
963
'Branch.get_stacked_on_url', ('kwaak/',),
964
'error', ('NotStacked',))
965
client.add_expected_call(
966
'Branch.get_parent', ('kwaak/',),
967
'success', ('../foo/',))
968
transport.mkdir('kwaak')
969
transport = transport.clone('kwaak')
970
branch = self.make_remote_branch(transport, client)
971
result = branch.get_parent()
972
self.assertEqual(transport.clone('../foo').base, result)
974
def test_parent_absolute(self):
975
transport = MemoryTransport()
976
client = FakeClient(transport.base)
977
client.add_expected_call(
978
'Branch.get_stacked_on_url', ('kwaak/',),
979
'error', ('NotStacked',))
980
client.add_expected_call(
981
'Branch.get_parent', ('kwaak/',),
982
'success', ('http://foo/',))
983
transport.mkdir('kwaak')
984
transport = transport.clone('kwaak')
985
branch = self.make_remote_branch(transport, client)
986
result = branch.get_parent()
987
self.assertEqual('http://foo/', result)
988
self.assertFinished(client)
991
class TestBranchSetParentLocation(RemoteBranchTestCase):
993
def test_no_parent(self):
994
# We call the verb when setting parent to None
995
transport = MemoryTransport()
996
client = FakeClient(transport.base)
997
client.add_expected_call(
998
'Branch.get_stacked_on_url', ('quack/',),
999
'error', ('NotStacked',))
1000
client.add_expected_call(
1001
'Branch.set_parent_location', ('quack/', 'b', 'r', ''),
1003
transport.mkdir('quack')
1004
transport = transport.clone('quack')
1005
branch = self.make_remote_branch(transport, client)
1006
branch._lock_token = 'b'
1007
branch._repo_lock_token = 'r'
1008
branch._set_parent_location(None)
1009
self.assertFinished(client)
1011
def test_parent(self):
1012
transport = MemoryTransport()
1013
client = FakeClient(transport.base)
1014
client.add_expected_call(
1015
'Branch.get_stacked_on_url', ('kwaak/',),
1016
'error', ('NotStacked',))
1017
client.add_expected_call(
1018
'Branch.set_parent_location', ('kwaak/', 'b', 'r', 'foo'),
1020
transport.mkdir('kwaak')
1021
transport = transport.clone('kwaak')
1022
branch = self.make_remote_branch(transport, client)
1023
branch._lock_token = 'b'
1024
branch._repo_lock_token = 'r'
1025
branch._set_parent_location('foo')
1026
self.assertFinished(client)
1028
def test_backwards_compat(self):
1029
self.setup_smart_server_with_call_log()
1030
branch = self.make_branch('.')
1031
self.reset_smart_call_log()
1032
verb = 'Branch.set_parent_location'
1033
self.disable_verb(verb)
1034
branch.set_parent('http://foo/')
1035
self.assertLength(12, self.hpss_calls)
1038
class TestBranchGetTagsBytes(RemoteBranchTestCase):
1040
def test_backwards_compat(self):
1041
self.setup_smart_server_with_call_log()
1042
branch = self.make_branch('.')
1043
self.reset_smart_call_log()
1044
verb = 'Branch.get_tags_bytes'
1045
self.disable_verb(verb)
1046
branch.tags.get_tag_dict()
1047
call_count = len([call for call in self.hpss_calls if
1048
call.call.method == verb])
1049
self.assertEqual(1, call_count)
1051
def test_trivial(self):
1052
transport = MemoryTransport()
1053
client = FakeClient(transport.base)
1054
client.add_expected_call(
1055
'Branch.get_stacked_on_url', ('quack/',),
1056
'error', ('NotStacked',))
1057
client.add_expected_call(
1058
'Branch.get_tags_bytes', ('quack/',),
1060
transport.mkdir('quack')
1061
transport = transport.clone('quack')
1062
branch = self.make_remote_branch(transport, client)
1063
result = branch.tags.get_tag_dict()
1064
self.assertFinished(client)
1065
self.assertEqual({}, result)
1068
class TestBranchSetTagsBytes(RemoteBranchTestCase):
1070
def test_trivial(self):
1071
transport = MemoryTransport()
1072
client = FakeClient(transport.base)
1073
client.add_expected_call(
1074
'Branch.get_stacked_on_url', ('quack/',),
1075
'error', ('NotStacked',))
1076
client.add_expected_call(
1077
'Branch.set_tags_bytes', ('quack/', 'branch token', 'repo token'),
1079
transport.mkdir('quack')
1080
transport = transport.clone('quack')
1081
branch = self.make_remote_branch(transport, client)
1082
self.lock_remote_branch(branch)
1083
branch._set_tags_bytes('tags bytes')
1084
self.assertFinished(client)
1085
self.assertEqual('tags bytes', client._calls[-1][-1])
1087
def test_backwards_compatible(self):
1088
transport = MemoryTransport()
1089
client = FakeClient(transport.base)
1090
client.add_expected_call(
1091
'Branch.get_stacked_on_url', ('quack/',),
1092
'error', ('NotStacked',))
1093
client.add_expected_call(
1094
'Branch.set_tags_bytes', ('quack/', 'branch token', 'repo token'),
1095
'unknown', ('Branch.set_tags_bytes',))
1096
transport.mkdir('quack')
1097
transport = transport.clone('quack')
1098
branch = self.make_remote_branch(transport, client)
1099
self.lock_remote_branch(branch)
1100
class StubRealBranch(object):
1103
def _set_tags_bytes(self, bytes):
1104
self.calls.append(('set_tags_bytes', bytes))
1105
real_branch = StubRealBranch()
1106
branch._real_branch = real_branch
1107
branch._set_tags_bytes('tags bytes')
1108
# Call a second time, to exercise the 'remote version already inferred'
1110
branch._set_tags_bytes('tags bytes')
1111
self.assertFinished(client)
1113
[('set_tags_bytes', 'tags bytes')] * 2, real_branch.calls)
532
1116
class TestBranchLastRevisionInfo(RemoteBranchTestCase):
566
1150
self.assertEqual((2, revid), result)
569
class TestBranch_get_stacked_on_url(tests.TestCaseWithMemoryTransport):
1153
class TestBranch_get_stacked_on_url(TestRemote):
570
1154
"""Test Branch._get_stacked_on_url rpc"""
572
1156
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:///')
1157
# test that asking for a stacked on url the server can't access works.
1158
# This isn't perfect, but then as we're in the same process there
1159
# really isn't anything we can do to be 100% sure that the server
1160
# doesn't just open in - this test probably needs to be rewritten using
1161
# a spawn()ed server.
1162
stacked_branch = self.make_branch('stacked', format='1.9')
1163
memory_branch = self.make_branch('base', format='1.9')
1164
vfs_url = self.get_vfs_only_url('base')
1165
stacked_branch.set_stacked_on_url(vfs_url)
1166
transport = stacked_branch.bzrdir.root_transport
575
1167
client = FakeClient(transport.base)
576
1168
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)
1169
'Branch.get_stacked_on_url', ('stacked/',),
1170
'success', ('ok', vfs_url))
1171
# XXX: Multiple calls are bad, this second call documents what is
1173
client.add_expected_call(
1174
'Branch.get_stacked_on_url', ('stacked/',),
1175
'success', ('ok', vfs_url))
1176
bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
1178
repo_fmt = remote.RemoteRepositoryFormat()
1179
repo_fmt._custom_format = stacked_branch.repository._format
1180
branch = RemoteBranch(bzrdir, RemoteRepository(bzrdir, repo_fmt),
581
1182
result = branch.get_stacked_on_url()
583
'file:///stacked/on', result)
1183
self.assertEqual(vfs_url, result)
585
1185
def test_backwards_compatible(self):
586
1186
# like with bzr1.6 with no Branch.get_stacked_on_url rpc
938
1567
self.assertEqual('rejection message', err.msg)
941
class TestBranchControlGetBranchConf(tests.TestCaseWithMemoryTransport):
942
"""Getting the branch configuration should use an abstract method not vfs.
1570
class TestBranchGetSetConfig(RemoteBranchTestCase):
945
1572
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/',))],
1573
# in an empty branch we decode the response properly
1574
client = FakeClient()
1575
client.add_expected_call(
1576
'Branch.get_stacked_on_url', ('memory:///',),
1577
'error', ('NotStacked',),)
1578
client.add_success_response_with_body('# config file body', 'ok')
1579
transport = MemoryTransport()
1580
branch = self.make_remote_branch(transport, client)
1581
config = branch.get_config()
1582
config.has_explicit_nickname()
1584
[('call', 'Branch.get_stacked_on_url', ('memory:///',)),
1585
('call_expecting_body', 'Branch.get_config_file', ('memory:///',))],
1588
def test_get_multi_line_branch_conf(self):
1589
# Make sure that multiple-line branch.conf files are supported
1591
# https://bugs.edge.launchpad.net/bzr/+bug/354075
1592
client = FakeClient()
1593
client.add_expected_call(
1594
'Branch.get_stacked_on_url', ('memory:///',),
1595
'error', ('NotStacked',),)
1596
client.add_success_response_with_body('a = 1\nb = 2\nc = 3\n', 'ok')
1597
transport = MemoryTransport()
1598
branch = self.make_remote_branch(transport, client)
1599
config = branch.get_config()
1600
self.assertEqual(u'2', config.get_user_option('b'))
1602
def test_set_option(self):
1603
client = FakeClient()
1604
client.add_expected_call(
1605
'Branch.get_stacked_on_url', ('memory:///',),
1606
'error', ('NotStacked',),)
1607
client.add_expected_call(
1608
'Branch.lock_write', ('memory:///', '', ''),
1609
'success', ('ok', 'branch token', 'repo token'))
1610
client.add_expected_call(
1611
'Branch.set_config_option', ('memory:///', 'branch token',
1612
'repo token', 'foo', 'bar', ''),
1614
client.add_expected_call(
1615
'Branch.unlock', ('memory:///', 'branch token', 'repo token'),
1617
transport = MemoryTransport()
1618
branch = self.make_remote_branch(transport, client)
1620
config = branch._get_config()
1621
config.set_option('foo', 'bar')
1623
self.assertFinished(client)
1625
def test_backwards_compat_set_option(self):
1626
self.setup_smart_server_with_call_log()
1627
branch = self.make_branch('.')
1628
verb = 'Branch.set_config_option'
1629
self.disable_verb(verb)
1631
self.addCleanup(branch.unlock)
1632
self.reset_smart_call_log()
1633
branch._get_config().set_option('value', 'name')
1634
self.assertLength(10, self.hpss_calls)
1635
self.assertEqual('value', branch._get_config().get_option('name'))
967
1638
class TestBranchLockWrite(RemoteBranchTestCase):
1243
1978
errors.UnexpectedSmartServerResponse,
1244
1979
repo.get_parent_map, ['a-revision-id'])
1981
def test_get_parent_map_negative_caches_missing_keys(self):
1982
self.setup_smart_server_with_call_log()
1983
repo = self.make_repository('foo')
1984
self.assertIsInstance(repo, RemoteRepository)
1986
self.addCleanup(repo.unlock)
1987
self.reset_smart_call_log()
1988
graph = repo.get_graph()
1989
self.assertEqual({},
1990
graph.get_parent_map(['some-missing', 'other-missing']))
1991
self.assertLength(1, self.hpss_calls)
1992
# No call if we repeat this
1993
self.reset_smart_call_log()
1994
graph = repo.get_graph()
1995
self.assertEqual({},
1996
graph.get_parent_map(['some-missing', 'other-missing']))
1997
self.assertLength(0, self.hpss_calls)
1998
# Asking for more unknown keys makes a request.
1999
self.reset_smart_call_log()
2000
graph = repo.get_graph()
2001
self.assertEqual({},
2002
graph.get_parent_map(['some-missing', 'other-missing',
2004
self.assertLength(1, self.hpss_calls)
2006
def disableExtraResults(self):
2007
old_flag = SmartServerRepositoryGetParentMap.no_extra_results
2008
SmartServerRepositoryGetParentMap.no_extra_results = True
2010
SmartServerRepositoryGetParentMap.no_extra_results = old_flag
2011
self.addCleanup(reset_values)
2013
def test_null_cached_missing_and_stop_key(self):
2014
self.setup_smart_server_with_call_log()
2015
# Make a branch with a single revision.
2016
builder = self.make_branch_builder('foo')
2017
builder.start_series()
2018
builder.build_snapshot('first', None, [
2019
('add', ('', 'root-id', 'directory', ''))])
2020
builder.finish_series()
2021
branch = builder.get_branch()
2022
repo = branch.repository
2023
self.assertIsInstance(repo, RemoteRepository)
2024
# Stop the server from sending extra results.
2025
self.disableExtraResults()
2027
self.addCleanup(repo.unlock)
2028
self.reset_smart_call_log()
2029
graph = repo.get_graph()
2030
# Query for 'first' and 'null:'. Because 'null:' is a parent of
2031
# 'first' it will be a candidate for the stop_keys of subsequent
2032
# requests, and because 'null:' was queried but not returned it will be
2033
# cached as missing.
2034
self.assertEqual({'first': ('null:',)},
2035
graph.get_parent_map(['first', 'null:']))
2036
# Now query for another key. This request will pass along a recipe of
2037
# start and stop keys describing the already cached results, and this
2038
# recipe's revision count must be correct (or else it will trigger an
2039
# error from the server).
2040
self.assertEqual({}, graph.get_parent_map(['another-key']))
2041
# This assertion guards against disableExtraResults silently failing to
2042
# work, thus invalidating the test.
2043
self.assertLength(2, self.hpss_calls)
2045
def test_get_parent_map_gets_ghosts_from_result(self):
2046
# asking for a revision should negatively cache close ghosts in its
2048
self.setup_smart_server_with_call_log()
2049
tree = self.make_branch_and_memory_tree('foo')
2052
builder = treebuilder.TreeBuilder()
2053
builder.start_tree(tree)
2055
builder.finish_tree()
2056
tree.set_parent_ids(['non-existant'], allow_leftmost_as_ghost=True)
2057
rev_id = tree.commit('')
2061
self.addCleanup(tree.unlock)
2062
repo = tree.branch.repository
2063
self.assertIsInstance(repo, RemoteRepository)
2065
repo.get_parent_map([rev_id])
2066
self.reset_smart_call_log()
2067
# Now asking for rev_id's ghost parent should not make calls
2068
self.assertEqual({}, repo.get_parent_map(['non-existant']))
2069
self.assertLength(0, self.hpss_calls)
1247
2072
class TestGetParentMapAllowsNew(tests.TestCaseWithTransport):
1249
2074
def test_allows_new_revisions(self):
1250
2075
"""get_parent_map's results can be updated by commit."""
1251
2076
smart_server = server.SmartTCPServer_for_testing()
1252
smart_server.setUp()
1253
self.addCleanup(smart_server.tearDown)
2077
self.start_server(smart_server)
1254
2078
self.make_branch('branch')
1255
2079
branch = Branch.open(smart_server.get_url() + '/branch')
1256
2080
tree = branch.create_checkout('tree', lightweight=True)
1332
2159
repo, client = self.setup_fake_client_and_repository(transport_path)
1333
2160
client.add_error_response('AnUnexpectedError')
1334
2161
e = self.assertRaises(errors.UnknownErrorFromSmartServer,
1335
self.applyDeprecated, one_four, repo.get_revision_graph, revid)
2162
repo._get_revision_graph, revid)
1336
2163
self.assertEqual(('AnUnexpectedError',), e.error_tuple)
2166
class TestRepositoryGetRevIdForRevno(TestRemoteRepository):
2169
repo, client = self.setup_fake_client_and_repository('quack')
2170
client.add_expected_call(
2171
'Repository.get_rev_id_for_revno', ('quack/', 5, (42, 'rev-foo')),
2172
'success', ('ok', 'rev-five'))
2173
result = repo.get_rev_id_for_revno(5, (42, 'rev-foo'))
2174
self.assertEqual((True, 'rev-five'), result)
2175
self.assertFinished(client)
2177
def test_history_incomplete(self):
2178
repo, client = self.setup_fake_client_and_repository('quack')
2179
client.add_expected_call(
2180
'Repository.get_rev_id_for_revno', ('quack/', 5, (42, 'rev-foo')),
2181
'success', ('history-incomplete', 10, 'rev-ten'))
2182
result = repo.get_rev_id_for_revno(5, (42, 'rev-foo'))
2183
self.assertEqual((False, (10, 'rev-ten')), result)
2184
self.assertFinished(client)
2186
def test_history_incomplete_with_fallback(self):
2187
"""A 'history-incomplete' response causes the fallback repository to be
2188
queried too, if one is set.
2190
# Make a repo with a fallback repo, both using a FakeClient.
2191
format = remote.response_tuple_to_repo_format(
2192
('yes', 'no', 'yes', 'fake-network-name'))
2193
repo, client = self.setup_fake_client_and_repository('quack')
2194
repo._format = format
2195
fallback_repo, ignored = self.setup_fake_client_and_repository(
2197
fallback_repo._client = client
2198
repo.add_fallback_repository(fallback_repo)
2199
# First the client should ask the primary repo
2200
client.add_expected_call(
2201
'Repository.get_rev_id_for_revno', ('quack/', 1, (42, 'rev-foo')),
2202
'success', ('history-incomplete', 2, 'rev-two'))
2203
# Then it should ask the fallback, using revno/revid from the
2204
# history-incomplete response as the known revno/revid.
2205
client.add_expected_call(
2206
'Repository.get_rev_id_for_revno',('fallback/', 1, (2, 'rev-two')),
2207
'success', ('ok', 'rev-one'))
2208
result = repo.get_rev_id_for_revno(1, (42, 'rev-foo'))
2209
self.assertEqual((True, 'rev-one'), result)
2210
self.assertFinished(client)
2212
def test_nosuchrevision(self):
2213
# 'nosuchrevision' is returned when the known-revid is not found in the
2214
# remote repo. The client translates that response to NoSuchRevision.
2215
repo, client = self.setup_fake_client_and_repository('quack')
2216
client.add_expected_call(
2217
'Repository.get_rev_id_for_revno', ('quack/', 5, (42, 'rev-foo')),
2218
'error', ('nosuchrevision', 'rev-foo'))
2220
errors.NoSuchRevision,
2221
repo.get_rev_id_for_revno, 5, (42, 'rev-foo'))
2222
self.assertFinished(client)
2224
def test_branch_fallback_locking(self):
2225
"""RemoteBranch.get_rev_id takes a read lock, and tries to call the
2226
get_rev_id_for_revno verb. If the verb is unknown the VFS fallback
2227
will be invoked, which will fail if the repo is unlocked.
2229
self.setup_smart_server_with_call_log()
2230
tree = self.make_branch_and_memory_tree('.')
2232
rev1 = tree.commit('First')
2233
rev2 = tree.commit('Second')
2235
branch = tree.branch
2236
self.assertFalse(branch.is_locked())
2237
self.reset_smart_call_log()
2238
verb = 'Repository.get_rev_id_for_revno'
2239
self.disable_verb(verb)
2240
self.assertEqual(rev1, branch.get_rev_id(1))
2241
self.assertLength(1, [call for call in self.hpss_calls if
2242
call.call.method == verb])
1339
2245
class TestRepositoryIsShared(TestRemoteRepository):
1341
2247
def test_is_shared(self):
1430
2362
self.assertEqual([], client._calls)
2365
class TestRepositoryInsertStreamBase(TestRemoteRepository):
2366
"""Base class for Repository.insert_stream and .insert_stream_1.19
2370
def checkInsertEmptyStream(self, repo, client):
2371
"""Insert an empty stream, checking the result.
2373
This checks that there are no resume_tokens or missing_keys, and that
2374
the client is finished.
2376
sink = repo._get_sink()
2377
fmt = repository.RepositoryFormat.get_default_format()
2378
resume_tokens, missing_keys = sink.insert_stream([], fmt, [])
2379
self.assertEqual([], resume_tokens)
2380
self.assertEqual(set(), missing_keys)
2381
self.assertFinished(client)
2384
class TestRepositoryInsertStream(TestRepositoryInsertStreamBase):
2385
"""Tests for using Repository.insert_stream verb when the _1.19 variant is
2388
This test case is very similar to TestRepositoryInsertStream_1_19.
2392
TestRemoteRepository.setUp(self)
2393
self.disable_verb('Repository.insert_stream_1.19')
2395
def test_unlocked_repo(self):
2396
transport_path = 'quack'
2397
repo, client = self.setup_fake_client_and_repository(transport_path)
2398
client.add_expected_call(
2399
'Repository.insert_stream_1.19', ('quack/', ''),
2400
'unknown', ('Repository.insert_stream_1.19',))
2401
client.add_expected_call(
2402
'Repository.insert_stream', ('quack/', ''),
2404
client.add_expected_call(
2405
'Repository.insert_stream', ('quack/', ''),
2407
self.checkInsertEmptyStream(repo, client)
2409
def test_locked_repo_with_no_lock_token(self):
2410
transport_path = 'quack'
2411
repo, client = self.setup_fake_client_and_repository(transport_path)
2412
client.add_expected_call(
2413
'Repository.lock_write', ('quack/', ''),
2414
'success', ('ok', ''))
2415
client.add_expected_call(
2416
'Repository.insert_stream_1.19', ('quack/', ''),
2417
'unknown', ('Repository.insert_stream_1.19',))
2418
client.add_expected_call(
2419
'Repository.insert_stream', ('quack/', ''),
2421
client.add_expected_call(
2422
'Repository.insert_stream', ('quack/', ''),
2425
self.checkInsertEmptyStream(repo, client)
2427
def test_locked_repo_with_lock_token(self):
2428
transport_path = 'quack'
2429
repo, client = self.setup_fake_client_and_repository(transport_path)
2430
client.add_expected_call(
2431
'Repository.lock_write', ('quack/', ''),
2432
'success', ('ok', 'a token'))
2433
client.add_expected_call(
2434
'Repository.insert_stream_1.19', ('quack/', '', 'a token'),
2435
'unknown', ('Repository.insert_stream_1.19',))
2436
client.add_expected_call(
2437
'Repository.insert_stream_locked', ('quack/', '', 'a token'),
2439
client.add_expected_call(
2440
'Repository.insert_stream_locked', ('quack/', '', 'a token'),
2443
self.checkInsertEmptyStream(repo, client)
2445
def test_stream_with_inventory_deltas(self):
2446
"""'inventory-deltas' substreams cannot be sent to the
2447
Repository.insert_stream verb, because not all servers that implement
2448
that verb will accept them. So when one is encountered the RemoteSink
2449
immediately stops using that verb and falls back to VFS insert_stream.
2451
transport_path = 'quack'
2452
repo, client = self.setup_fake_client_and_repository(transport_path)
2453
client.add_expected_call(
2454
'Repository.insert_stream_1.19', ('quack/', ''),
2455
'unknown', ('Repository.insert_stream_1.19',))
2456
client.add_expected_call(
2457
'Repository.insert_stream', ('quack/', ''),
2459
client.add_expected_call(
2460
'Repository.insert_stream', ('quack/', ''),
2462
# Create a fake real repository for insert_stream to fall back on, so
2463
# that we can directly see the records the RemoteSink passes to the
2468
def insert_stream(self, stream, src_format, resume_tokens):
2469
for substream_kind, substream in stream:
2470
self.records.append(
2471
(substream_kind, [record.key for record in substream]))
2472
return ['fake tokens'], ['fake missing keys']
2473
fake_real_sink = FakeRealSink()
2474
class FakeRealRepository:
2475
def _get_sink(self):
2476
return fake_real_sink
2477
def is_in_write_group(self):
2479
def refresh_data(self):
2481
repo._real_repository = FakeRealRepository()
2482
sink = repo._get_sink()
2483
fmt = repository.RepositoryFormat.get_default_format()
2484
stream = self.make_stream_with_inv_deltas(fmt)
2485
resume_tokens, missing_keys = sink.insert_stream(stream, fmt, [])
2486
# Every record from the first inventory delta should have been sent to
2488
expected_records = [
2489
('inventory-deltas', [('rev2',), ('rev3',)]),
2490
('texts', [('some-rev', 'some-file')])]
2491
self.assertEqual(expected_records, fake_real_sink.records)
2492
# The return values from the real sink's insert_stream are propagated
2493
# back to the original caller.
2494
self.assertEqual(['fake tokens'], resume_tokens)
2495
self.assertEqual(['fake missing keys'], missing_keys)
2496
self.assertFinished(client)
2498
def make_stream_with_inv_deltas(self, fmt):
2499
"""Make a simple stream with an inventory delta followed by more
2500
records and more substreams to test that all records and substreams
2501
from that point on are used.
2503
This sends, in order:
2504
* inventories substream: rev1, rev2, rev3. rev2 and rev3 are
2506
* texts substream: (some-rev, some-file)
2508
# Define a stream using generators so that it isn't rewindable.
2509
inv = inventory.Inventory(revision_id='rev1')
2510
inv.root.revision = 'rev1'
2511
def stream_with_inv_delta():
2512
yield ('inventories', inventories_substream())
2513
yield ('inventory-deltas', inventory_delta_substream())
2515
versionedfile.FulltextContentFactory(
2516
('some-rev', 'some-file'), (), None, 'content')])
2517
def inventories_substream():
2518
# An empty inventory fulltext. This will be streamed normally.
2519
text = fmt._serializer.write_inventory_to_string(inv)
2520
yield versionedfile.FulltextContentFactory(
2521
('rev1',), (), None, text)
2522
def inventory_delta_substream():
2523
# An inventory delta. This can't be streamed via this verb, so it
2524
# will trigger a fallback to VFS insert_stream.
2525
entry = inv.make_entry(
2526
'directory', 'newdir', inv.root.file_id, 'newdir-id')
2527
entry.revision = 'ghost'
2528
delta = [(None, 'newdir', 'newdir-id', entry)]
2529
serializer = inventory_delta.InventoryDeltaSerializer(
2530
versioned_root=True, tree_references=False)
2531
lines = serializer.delta_to_lines('rev1', 'rev2', delta)
2532
yield versionedfile.ChunkedContentFactory(
2533
('rev2',), (('rev1',)), None, lines)
2535
lines = serializer.delta_to_lines('rev1', 'rev3', delta)
2536
yield versionedfile.ChunkedContentFactory(
2537
('rev3',), (('rev1',)), None, lines)
2538
return stream_with_inv_delta()
2541
class TestRepositoryInsertStream_1_19(TestRepositoryInsertStreamBase):
2543
def test_unlocked_repo(self):
2544
transport_path = 'quack'
2545
repo, client = self.setup_fake_client_and_repository(transport_path)
2546
client.add_expected_call(
2547
'Repository.insert_stream_1.19', ('quack/', ''),
2549
client.add_expected_call(
2550
'Repository.insert_stream_1.19', ('quack/', ''),
2552
self.checkInsertEmptyStream(repo, client)
2554
def test_locked_repo_with_no_lock_token(self):
2555
transport_path = 'quack'
2556
repo, client = self.setup_fake_client_and_repository(transport_path)
2557
client.add_expected_call(
2558
'Repository.lock_write', ('quack/', ''),
2559
'success', ('ok', ''))
2560
client.add_expected_call(
2561
'Repository.insert_stream_1.19', ('quack/', ''),
2563
client.add_expected_call(
2564
'Repository.insert_stream_1.19', ('quack/', ''),
2567
self.checkInsertEmptyStream(repo, client)
2569
def test_locked_repo_with_lock_token(self):
2570
transport_path = 'quack'
2571
repo, client = self.setup_fake_client_and_repository(transport_path)
2572
client.add_expected_call(
2573
'Repository.lock_write', ('quack/', ''),
2574
'success', ('ok', 'a token'))
2575
client.add_expected_call(
2576
'Repository.insert_stream_1.19', ('quack/', '', 'a token'),
2578
client.add_expected_call(
2579
'Repository.insert_stream_1.19', ('quack/', '', 'a token'),
2582
self.checkInsertEmptyStream(repo, client)
1433
2585
class TestRepositoryTarball(TestRemoteRepository):
1435
2587
# This is a canned tarball reponse we can validate against
1793
2954
remote_repo.unlock()
1795
2956
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')
2957
"""Get stacked_upon and stacked branches with content in each."""
2958
self.setup_smart_server_with_call_log()
2959
tree1 = self.make_branch_and_tree('tree1', format='1.9')
1800
2960
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')
2961
tree2 = tree1.branch.bzrdir.sprout('tree2', stacked=True
2962
).open_workingtree()
2963
local_tree = tree2.branch.create_checkout('local')
2964
local_tree.commit('local changes make me feel good.')
2965
branch2 = Branch.open(self.get_url('tree2'))
1804
2966
branch2.lock_read()
1805
2967
self.addCleanup(branch2.unlock)
2968
return tree1.branch, branch2
1808
2970
def test_stacked_get_parent_map(self):
1809
2971
# the public implementation of get_parent_map obeys stacking
1810
branch = self.prepare_stacked_remote_branch()
2972
_, branch = self.prepare_stacked_remote_branch()
1811
2973
repo = branch.repository
1812
2974
self.assertEqual(['rev1'], repo.get_parent_map(['rev1']).keys())
1814
2976
def test_unstacked_get_parent_map(self):
1815
2977
# _unstacked_provider.get_parent_map ignores stacking
1816
branch = self.prepare_stacked_remote_branch()
2978
_, branch = self.prepare_stacked_remote_branch()
1817
2979
provider = branch.repository._unstacked_provider
1818
2980
self.assertEqual([], provider.get_parent_map(['rev1']).keys())
2982
def fetch_stream_to_rev_order(self, stream):
2984
for kind, substream in stream:
2985
if not kind == 'revisions':
2988
for content in substream:
2989
result.append(content.key[-1])
2992
def get_ordered_revs(self, format, order, branch_factory=None):
2993
"""Get a list of the revisions in a stream to format format.
2995
:param format: The format of the target.
2996
:param order: the order that target should have requested.
2997
:param branch_factory: A callable to create a trunk and stacked branch
2998
to fetch from. If none, self.prepare_stacked_remote_branch is used.
2999
:result: The revision ids in the stream, in the order seen,
3000
the topological order of revisions in the source.
3002
unordered_format = bzrdir.format_registry.get(format)()
3003
target_repository_format = unordered_format.repository_format
3005
self.assertEqual(order, target_repository_format._fetch_order)
3006
if branch_factory is None:
3007
branch_factory = self.prepare_stacked_remote_branch
3008
_, stacked = branch_factory()
3009
source = stacked.repository._get_source(target_repository_format)
3010
tip = stacked.last_revision()
3011
revs = stacked.repository.get_ancestry(tip)
3012
search = graph.PendingAncestryResult([tip], stacked.repository)
3013
self.reset_smart_call_log()
3014
stream = source.get_stream(search)
3017
# We trust that if a revision is in the stream the rest of the new
3018
# content for it is too, as per our main fetch tests; here we are
3019
# checking that the revisions are actually included at all, and their
3021
return self.fetch_stream_to_rev_order(stream), revs
3023
def test_stacked_get_stream_unordered(self):
3024
# Repository._get_source.get_stream() from a stacked repository with
3025
# unordered yields the full data from both stacked and stacked upon
3027
rev_ord, expected_revs = self.get_ordered_revs('1.9', 'unordered')
3028
self.assertEqual(set(expected_revs), set(rev_ord))
3029
# Getting unordered results should have made a streaming data request
3030
# from the server, then one from the backing branch.
3031
self.assertLength(2, self.hpss_calls)
3033
def test_stacked_on_stacked_get_stream_unordered(self):
3034
# Repository._get_source.get_stream() from a stacked repository which
3035
# is itself stacked yields the full data from all three sources.
3036
def make_stacked_stacked():
3037
_, stacked = self.prepare_stacked_remote_branch()
3038
tree = stacked.bzrdir.sprout('tree3', stacked=True
3039
).open_workingtree()
3040
local_tree = tree.branch.create_checkout('local-tree3')
3041
local_tree.commit('more local changes are better')
3042
branch = Branch.open(self.get_url('tree3'))
3044
self.addCleanup(branch.unlock)
3046
rev_ord, expected_revs = self.get_ordered_revs('1.9', 'unordered',
3047
branch_factory=make_stacked_stacked)
3048
self.assertEqual(set(expected_revs), set(rev_ord))
3049
# Getting unordered results should have made a streaming data request
3050
# from the server, and one from each backing repo
3051
self.assertLength(3, self.hpss_calls)
3053
def test_stacked_get_stream_topological(self):
3054
# Repository._get_source.get_stream() from a stacked repository with
3055
# topological sorting yields the full data from both stacked and
3056
# stacked upon sources in topological order.
3057
rev_ord, expected_revs = self.get_ordered_revs('knit', 'topological')
3058
self.assertEqual(expected_revs, rev_ord)
3059
# Getting topological sort requires VFS calls still - one of which is
3060
# pushing up from the bound branch.
3061
self.assertLength(13, self.hpss_calls)
3063
def test_stacked_get_stream_groupcompress(self):
3064
# Repository._get_source.get_stream() from a stacked repository with
3065
# groupcompress sorting yields the full data from both stacked and
3066
# stacked upon sources in groupcompress order.
3067
raise tests.TestSkipped('No groupcompress ordered format available')
3068
rev_ord, expected_revs = self.get_ordered_revs('dev5', 'groupcompress')
3069
self.assertEqual(expected_revs, reversed(rev_ord))
3070
# Getting unordered results should have made a streaming data request
3071
# from the backing branch, and one from the stacked on branch.
3072
self.assertLength(2, self.hpss_calls)
3074
def test_stacked_pull_more_than_stacking_has_bug_360791(self):
3075
# When pulling some fixed amount of content that is more than the
3076
# source has (because some is coming from a fallback branch, no error
3077
# should be received. This was reported as bug 360791.
3078
# Need three branches: a trunk, a stacked branch, and a preexisting
3079
# branch pulling content from stacked and trunk.
3080
self.setup_smart_server_with_call_log()
3081
trunk = self.make_branch_and_tree('trunk', format="1.9-rich-root")
3082
r1 = trunk.commit('start')
3083
stacked_branch = trunk.branch.create_clone_on_transport(
3084
self.get_transport('stacked'), stacked_on=trunk.branch.base)
3085
local = self.make_branch('local', format='1.9-rich-root')
3086
local.repository.fetch(stacked_branch.repository,
3087
stacked_branch.last_revision())
1821
3090
class TestRemoteBranchEffort(tests.TestCaseWithTransport):