234
158
def add_unknown_method_response(self, verb):
235
159
self.responses.append(('unknown', verb))
237
def finished_test(self):
238
if self._expected_calls:
239
raise AssertionError("%r finished but was still expecting %r"
240
% (self, self._expected_calls[0]))
242
161
def _get_next_response(self):
244
response_tuple = self.responses.pop(0)
245
except IndexError, e:
246
raise AssertionError("%r didn't expect any more calls"
162
response_tuple = self.responses.pop(0)
248
163
if response_tuple[0] == 'unknown':
249
164
raise errors.UnknownSmartMethod(response_tuple[1])
250
165
elif response_tuple[0] == 'error':
251
166
raise errors.ErrorFromSmartServer(response_tuple[1])
252
167
return response_tuple
254
def _check_call(self, method, args):
255
if self._expected_calls is None:
256
# the test should be updated to say what it expects
259
next_call = self._expected_calls.pop(0)
261
raise AssertionError("%r didn't expect any more calls "
263
% (self, method, args,))
264
if next_call is None:
266
if method != next_call[0] or args != next_call[1]:
267
raise AssertionError("%r expected %r%r "
269
% (self, next_call[0], next_call[1], method, args,))
271
169
def call(self, method, *args):
272
self._check_call(method, args)
273
170
self._calls.append(('call', method, args))
274
171
return self._get_next_response()[1]
276
173
def call_expecting_body(self, method, *args):
277
self._check_call(method, args)
278
174
self._calls.append(('call_expecting_body', method, args))
279
175
result = self._get_next_response()
280
176
self.expecting_body = True
281
177
return result[1], FakeProtocol(result[2], self)
283
179
def call_with_body_bytes_expecting_body(self, method, args, body):
284
self._check_call(method, args)
285
180
self._calls.append(('call_with_body_bytes_expecting_body', method,
287
182
result = self._get_next_response()
288
183
self.expecting_body = True
289
184
return result[1], FakeProtocol(result[2], self)
291
def call_with_body_stream(self, args, stream):
292
# Explicitly consume the stream before checking for an error, because
293
# that's what happens a real medium.
294
stream = list(stream)
295
self._check_call(args[0], args[1:])
296
self._calls.append(('call_with_body_stream', args[0], args[1:], stream))
297
result = self._get_next_response()
298
# The second value returned from call_with_body_stream is supposed to
299
# be a response_handler object, but so far no tests depend on that.
300
response_handler = None
301
return result[1], response_handler
304
187
class FakeMedium(medium.SmartClientMedium):
413
281
AssertionError, client_medium._remember_remote_is_before, (1, 9))
416
class TestBzrDirCloningMetaDir(TestRemote):
418
def test_backwards_compat(self):
419
self.setup_smart_server_with_call_log()
420
a_dir = self.make_bzrdir('.')
421
self.reset_smart_call_log()
422
verb = 'BzrDir.cloning_metadir'
423
self.disable_verb(verb)
424
format = a_dir.cloning_metadir()
425
call_count = len([call for call in self.hpss_calls if
426
call.call.method == verb])
427
self.assertEqual(1, call_count)
429
def test_branch_reference(self):
430
transport = self.get_transport('quack')
431
referenced = self.make_branch('referenced')
432
expected = referenced.bzrdir.cloning_metadir()
433
client = FakeClient(transport.base)
434
client.add_expected_call(
435
'BzrDir.cloning_metadir', ('quack/', 'False'),
436
'error', ('BranchReference',)),
437
client.add_expected_call(
438
'BzrDir.open_branchV2', ('quack/',),
439
'success', ('ref', self.get_url('referenced'))),
440
a_bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
442
result = a_bzrdir.cloning_metadir()
443
# We should have got a control dir matching the referenced branch.
444
self.assertEqual(bzrdir.BzrDirMetaFormat1, type(result))
445
self.assertEqual(expected._repository_format, result._repository_format)
446
self.assertEqual(expected._branch_format, result._branch_format)
447
self.assertFinished(client)
449
def test_current_server(self):
450
transport = self.get_transport('.')
451
transport = transport.clone('quack')
452
self.make_bzrdir('quack')
453
client = FakeClient(transport.base)
454
reference_bzrdir_format = bzrdir.format_registry.get('default')()
455
control_name = reference_bzrdir_format.network_name()
456
client.add_expected_call(
457
'BzrDir.cloning_metadir', ('quack/', 'False'),
458
'success', (control_name, '', ('branch', ''))),
459
a_bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
461
result = a_bzrdir.cloning_metadir()
462
# We should have got a reference control dir with default branch and
463
# repository formats.
464
# This pokes a little, just to be sure.
465
self.assertEqual(bzrdir.BzrDirMetaFormat1, type(result))
466
self.assertEqual(None, result._repository_format)
467
self.assertEqual(None, result._branch_format)
468
self.assertFinished(client)
471
class TestBzrDirOpenBranch(TestRemote):
473
def test_backwards_compat(self):
474
self.setup_smart_server_with_call_log()
475
self.make_branch('.')
476
a_dir = BzrDir.open(self.get_url('.'))
477
self.reset_smart_call_log()
478
verb = 'BzrDir.open_branchV2'
479
self.disable_verb(verb)
480
format = a_dir.open_branch()
481
call_count = len([call for call in self.hpss_calls if
482
call.call.method == verb])
483
self.assertEqual(1, call_count)
284
class TestBzrDirOpenBranch(tests.TestCase):
485
286
def test_branch_present(self):
486
reference_format = self.get_repo_format()
487
network_name = reference_format.network_name()
488
branch_network_name = self.get_branch_format().network_name()
489
287
transport = MemoryTransport()
490
288
transport.mkdir('quack')
491
289
transport = transport.clone('quack')
492
290
client = FakeClient(transport.base)
493
client.add_expected_call(
494
'BzrDir.open_branchV2', ('quack/',),
495
'success', ('branch', branch_network_name))
496
client.add_expected_call(
497
'BzrDir.find_repositoryV3', ('quack/',),
498
'success', ('ok', '', 'no', 'no', 'no', network_name))
499
client.add_expected_call(
500
'Branch.get_stacked_on_url', ('quack/',),
501
'error', ('NotStacked',))
502
bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
291
client.add_success_response('ok', '')
292
client.add_success_response('ok', '', 'no', 'no', 'no')
293
bzrdir = RemoteBzrDir(transport, _client=client)
504
294
result = bzrdir.open_branch()
296
[('call', 'BzrDir.open_branch', ('quack/',)),
297
('call', 'BzrDir.find_repositoryV2', ('quack/',))],
505
299
self.assertIsInstance(result, RemoteBranch)
506
300
self.assertEqual(bzrdir, result.bzrdir)
507
self.assertFinished(client)
509
302
def test_branch_missing(self):
510
303
transport = MemoryTransport()
603
382
RemoteBzrDirFormat.probe_transport, OldServerTransport())
606
class TestBzrDirCreateBranch(TestRemote):
608
def test_backwards_compat(self):
609
self.setup_smart_server_with_call_log()
610
repo = self.make_repository('.')
611
self.reset_smart_call_log()
612
self.disable_verb('BzrDir.create_branch')
613
branch = repo.bzrdir.create_branch()
614
create_branch_call_count = len([call for call in self.hpss_calls if
615
call.call.method == 'BzrDir.create_branch'])
616
self.assertEqual(1, create_branch_call_count)
618
def test_current_server(self):
619
transport = self.get_transport('.')
620
transport = transport.clone('quack')
621
self.make_repository('quack')
622
client = FakeClient(transport.base)
623
reference_bzrdir_format = bzrdir.format_registry.get('default')()
624
reference_format = reference_bzrdir_format.get_branch_format()
625
network_name = reference_format.network_name()
626
reference_repo_fmt = reference_bzrdir_format.repository_format
627
reference_repo_name = reference_repo_fmt.network_name()
628
client.add_expected_call(
629
'BzrDir.create_branch', ('quack/', network_name),
630
'success', ('ok', network_name, '', 'no', 'no', 'yes',
631
reference_repo_name))
632
a_bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
634
branch = a_bzrdir.create_branch()
635
# We should have got a remote branch
636
self.assertIsInstance(branch, remote.RemoteBranch)
637
# its format should have the settings from the response
638
format = branch._format
639
self.assertEqual(network_name, format.network_name())
642
class TestBzrDirCreateRepository(TestRemote):
644
def test_backwards_compat(self):
645
self.setup_smart_server_with_call_log()
646
bzrdir = self.make_bzrdir('.')
647
self.reset_smart_call_log()
648
self.disable_verb('BzrDir.create_repository')
649
repo = bzrdir.create_repository()
650
create_repo_call_count = len([call for call in self.hpss_calls if
651
call.call.method == 'BzrDir.create_repository'])
652
self.assertEqual(1, create_repo_call_count)
654
def test_current_server(self):
655
transport = self.get_transport('.')
656
transport = transport.clone('quack')
657
self.make_bzrdir('quack')
658
client = FakeClient(transport.base)
659
reference_bzrdir_format = bzrdir.format_registry.get('default')()
660
reference_format = reference_bzrdir_format.repository_format
661
network_name = reference_format.network_name()
662
client.add_expected_call(
663
'BzrDir.create_repository', ('quack/',
664
'Bazaar repository format 2a (needs bzr 1.16 or later)\n',
666
'success', ('ok', 'yes', 'yes', 'yes', network_name))
667
a_bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
669
repo = a_bzrdir.create_repository()
670
# We should have got a remote repository
671
self.assertIsInstance(repo, remote.RemoteRepository)
672
# its format should have the settings from the response
673
format = repo._format
674
self.assertTrue(format.rich_root_data)
675
self.assertTrue(format.supports_tree_reference)
676
self.assertTrue(format.supports_external_lookups)
677
self.assertEqual(network_name, format.network_name())
680
class TestBzrDirOpenRepository(TestRemote):
682
def test_backwards_compat_1_2_3(self):
683
# fallback all the way to the first version.
684
reference_format = self.get_repo_format()
685
network_name = reference_format.network_name()
686
client = FakeClient('bzr://example.com/')
687
client.add_unknown_method_response('BzrDir.find_repositoryV3')
688
client.add_unknown_method_response('BzrDir.find_repositoryV2')
385
class TestBzrDirOpenRepository(tests.TestCase):
387
def test_backwards_compat_1_2(self):
388
transport = MemoryTransport()
389
transport.mkdir('quack')
390
transport = transport.clone('quack')
391
client = FakeClient(transport.base)
392
client.add_unknown_method_response('RemoteRepository.find_repositoryV2')
689
393
client.add_success_response('ok', '', 'no', 'no')
690
# A real repository instance will be created to determine the network
692
client.add_success_response_with_body(
693
"Bazaar-NG meta directory, format 1\n", 'ok')
694
client.add_success_response_with_body(
695
reference_format.get_format_string(), 'ok')
696
# PackRepository wants to do a stat
697
client.add_success_response('stat', '0', '65535')
698
remote_transport = RemoteTransport('bzr://example.com/quack/', medium=False,
700
bzrdir = RemoteBzrDir(remote_transport, remote.RemoteBzrDirFormat(),
702
repo = bzrdir.open_repository()
704
[('call', 'BzrDir.find_repositoryV3', ('quack/',)),
705
('call', 'BzrDir.find_repositoryV2', ('quack/',)),
706
('call', 'BzrDir.find_repository', ('quack/',)),
707
('call_expecting_body', 'get', ('/quack/.bzr/branch-format',)),
708
('call_expecting_body', 'get', ('/quack/.bzr/repository/format',)),
709
('call', 'stat', ('/quack/.bzr/repository',)),
712
self.assertEqual(network_name, repo._format.network_name())
714
def test_backwards_compat_2(self):
715
# fallback to find_repositoryV2
716
reference_format = self.get_repo_format()
717
network_name = reference_format.network_name()
718
client = FakeClient('bzr://example.com/')
719
client.add_unknown_method_response('BzrDir.find_repositoryV3')
720
client.add_success_response('ok', '', 'no', 'no', 'no')
721
# A real repository instance will be created to determine the network
723
client.add_success_response_with_body(
724
"Bazaar-NG meta directory, format 1\n", 'ok')
725
client.add_success_response_with_body(
726
reference_format.get_format_string(), 'ok')
727
# PackRepository wants to do a stat
728
client.add_success_response('stat', '0', '65535')
729
remote_transport = RemoteTransport('bzr://example.com/quack/', medium=False,
731
bzrdir = RemoteBzrDir(remote_transport, remote.RemoteBzrDirFormat(),
733
repo = bzrdir.open_repository()
735
[('call', 'BzrDir.find_repositoryV3', ('quack/',)),
736
('call', 'BzrDir.find_repositoryV2', ('quack/',)),
737
('call_expecting_body', 'get', ('/quack/.bzr/branch-format',)),
738
('call_expecting_body', 'get', ('/quack/.bzr/repository/format',)),
739
('call', 'stat', ('/quack/.bzr/repository',)),
742
self.assertEqual(network_name, repo._format.network_name())
744
def test_current_server(self):
745
reference_format = self.get_repo_format()
746
network_name = reference_format.network_name()
747
transport = MemoryTransport()
748
transport.mkdir('quack')
749
transport = transport.clone('quack')
750
client = FakeClient(transport.base)
751
client.add_success_response('ok', '', 'no', 'no', 'no', network_name)
752
bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
754
repo = bzrdir.open_repository()
756
[('call', 'BzrDir.find_repositoryV3', ('quack/',))],
758
self.assertEqual(network_name, repo._format.network_name())
761
class TestBzrDirFormatInitializeEx(TestRemote):
763
def test_success(self):
764
"""Simple test for typical successful call."""
765
fmt = bzrdir.RemoteBzrDirFormat()
766
default_format_name = BzrDirFormat.get_default_format().network_name()
767
transport = self.get_transport()
768
client = FakeClient(transport.base)
769
client.add_expected_call(
770
'BzrDirFormat.initialize_ex_1.16',
771
(default_format_name, 'path', 'False', 'False', 'False', '',
772
'', '', '', 'False'),
774
('.', 'no', 'no', 'yes', 'repo fmt', 'repo bzrdir fmt',
775
'bzrdir fmt', 'False', '', '', 'repo lock token'))
776
# XXX: It would be better to call fmt.initialize_on_transport_ex, but
777
# it's currently hard to test that without supplying a real remote
778
# transport connected to a real server.
779
result = fmt._initialize_on_transport_ex_rpc(client, 'path',
780
transport, False, False, False, None, None, None, None, False)
781
self.assertFinished(client)
783
def test_error(self):
784
"""Error responses are translated, e.g. 'PermissionDenied' raises the
785
corresponding error from the client.
787
fmt = bzrdir.RemoteBzrDirFormat()
788
default_format_name = BzrDirFormat.get_default_format().network_name()
789
transport = self.get_transport()
790
client = FakeClient(transport.base)
791
client.add_expected_call(
792
'BzrDirFormat.initialize_ex_1.16',
793
(default_format_name, 'path', 'False', 'False', 'False', '',
794
'', '', '', 'False'),
796
('PermissionDenied', 'path', 'extra info'))
797
# XXX: It would be better to call fmt.initialize_on_transport_ex, but
798
# it's currently hard to test that without supplying a real remote
799
# transport connected to a real server.
800
err = self.assertRaises(errors.PermissionDenied,
801
fmt._initialize_on_transport_ex_rpc, client, 'path', transport,
802
False, False, False, None, None, None, None, False)
803
self.assertEqual('path', err.path)
804
self.assertEqual(': extra info', err.extra)
805
self.assertFinished(client)
807
def test_error_from_real_server(self):
808
"""Integration test for error translation."""
809
transport = self.make_smart_server('foo')
810
transport = transport.clone('no-such-path')
811
fmt = bzrdir.RemoteBzrDirFormat()
812
err = self.assertRaises(errors.NoSuchFile,
813
fmt.initialize_on_transport_ex, transport, create_prefix=False)
394
bzrdir = RemoteBzrDir(transport, _client=client)
395
repo = bzrdir.open_repository()
397
[('call', 'BzrDir.find_repositoryV2', ('quack/',)),
398
('call', 'BzrDir.find_repository', ('quack/',))],
816
402
class OldSmartClient(object):
841
427
return OldSmartClient()
844
class RemoteBzrDirTestCase(TestRemote):
846
def make_remote_bzrdir(self, transport, client):
847
"""Make a RemotebzrDir using 'client' as the _client."""
848
return RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
852
class RemoteBranchTestCase(RemoteBzrDirTestCase):
854
def make_remote_branch(self, transport, client):
855
"""Make a RemoteBranch using 'client' as its _SmartClient.
857
A RemoteBzrDir and RemoteRepository will also be created to fill out
858
the RemoteBranch, albeit with stub values for some of their attributes.
860
# we do not want bzrdir to make any remote calls, so use False as its
861
# _client. If it tries to make a remote call, this will fail
863
bzrdir = self.make_remote_bzrdir(transport, False)
864
repo = RemoteRepository(bzrdir, None, _client=client)
865
branch_format = self.get_branch_format()
866
format = RemoteBranchFormat(network_name=branch_format.network_name())
867
return RemoteBranch(bzrdir, repo, _client=client, format=format)
870
class TestBranchGetParent(RemoteBranchTestCase):
872
def test_no_parent(self):
873
# in an empty branch we decode the response properly
874
transport = MemoryTransport()
875
client = FakeClient(transport.base)
876
client.add_expected_call(
877
'Branch.get_stacked_on_url', ('quack/',),
878
'error', ('NotStacked',))
879
client.add_expected_call(
880
'Branch.get_parent', ('quack/',),
882
transport.mkdir('quack')
883
transport = transport.clone('quack')
884
branch = self.make_remote_branch(transport, client)
885
result = branch.get_parent()
886
self.assertFinished(client)
887
self.assertEqual(None, result)
889
def test_parent_relative(self):
890
transport = MemoryTransport()
891
client = FakeClient(transport.base)
892
client.add_expected_call(
893
'Branch.get_stacked_on_url', ('kwaak/',),
894
'error', ('NotStacked',))
895
client.add_expected_call(
896
'Branch.get_parent', ('kwaak/',),
897
'success', ('../foo/',))
898
transport.mkdir('kwaak')
899
transport = transport.clone('kwaak')
900
branch = self.make_remote_branch(transport, client)
901
result = branch.get_parent()
902
self.assertEqual(transport.clone('../foo').base, result)
904
def test_parent_absolute(self):
905
transport = MemoryTransport()
906
client = FakeClient(transport.base)
907
client.add_expected_call(
908
'Branch.get_stacked_on_url', ('kwaak/',),
909
'error', ('NotStacked',))
910
client.add_expected_call(
911
'Branch.get_parent', ('kwaak/',),
912
'success', ('http://foo/',))
913
transport.mkdir('kwaak')
914
transport = transport.clone('kwaak')
915
branch = self.make_remote_branch(transport, client)
916
result = branch.get_parent()
917
self.assertEqual('http://foo/', result)
918
self.assertFinished(client)
921
class TestBranchSetParentLocation(RemoteBranchTestCase):
923
def test_no_parent(self):
924
# We call the verb when setting parent to None
925
transport = MemoryTransport()
926
client = FakeClient(transport.base)
927
client.add_expected_call(
928
'Branch.get_stacked_on_url', ('quack/',),
929
'error', ('NotStacked',))
930
client.add_expected_call(
931
'Branch.set_parent_location', ('quack/', 'b', 'r', ''),
933
transport.mkdir('quack')
934
transport = transport.clone('quack')
935
branch = self.make_remote_branch(transport, client)
936
branch._lock_token = 'b'
937
branch._repo_lock_token = 'r'
938
branch._set_parent_location(None)
939
self.assertFinished(client)
941
def test_parent(self):
942
transport = MemoryTransport()
943
client = FakeClient(transport.base)
944
client.add_expected_call(
945
'Branch.get_stacked_on_url', ('kwaak/',),
946
'error', ('NotStacked',))
947
client.add_expected_call(
948
'Branch.set_parent_location', ('kwaak/', 'b', 'r', 'foo'),
950
transport.mkdir('kwaak')
951
transport = transport.clone('kwaak')
952
branch = self.make_remote_branch(transport, client)
953
branch._lock_token = 'b'
954
branch._repo_lock_token = 'r'
955
branch._set_parent_location('foo')
956
self.assertFinished(client)
958
def test_backwards_compat(self):
959
self.setup_smart_server_with_call_log()
960
branch = self.make_branch('.')
961
self.reset_smart_call_log()
962
verb = 'Branch.set_parent_location'
963
self.disable_verb(verb)
964
branch.set_parent('http://foo/')
965
self.assertLength(12, self.hpss_calls)
968
class TestBranchGetTagsBytes(RemoteBranchTestCase):
970
def test_backwards_compat(self):
971
self.setup_smart_server_with_call_log()
972
branch = self.make_branch('.')
973
self.reset_smart_call_log()
974
verb = 'Branch.get_tags_bytes'
975
self.disable_verb(verb)
976
branch.tags.get_tag_dict()
977
call_count = len([call for call in self.hpss_calls if
978
call.call.method == verb])
979
self.assertEqual(1, call_count)
981
def test_trivial(self):
982
transport = MemoryTransport()
983
client = FakeClient(transport.base)
984
client.add_expected_call(
985
'Branch.get_stacked_on_url', ('quack/',),
986
'error', ('NotStacked',))
987
client.add_expected_call(
988
'Branch.get_tags_bytes', ('quack/',),
990
transport.mkdir('quack')
991
transport = transport.clone('quack')
992
branch = self.make_remote_branch(transport, client)
993
result = branch.tags.get_tag_dict()
994
self.assertFinished(client)
995
self.assertEqual({}, result)
998
class TestBranchLastRevisionInfo(RemoteBranchTestCase):
430
class TestBranchLastRevisionInfo(tests.TestCase):
1000
432
def test_empty_branch(self):
1001
433
# in an empty branch we decode the response properly
1002
434
transport = MemoryTransport()
1003
435
client = FakeClient(transport.base)
1004
client.add_expected_call(
1005
'Branch.get_stacked_on_url', ('quack/',),
1006
'error', ('NotStacked',))
1007
client.add_expected_call(
1008
'Branch.last_revision_info', ('quack/',),
1009
'success', ('ok', '0', 'null:'))
436
client.add_success_response('ok', '0', 'null:')
1010
437
transport.mkdir('quack')
1011
438
transport = transport.clone('quack')
1012
branch = self.make_remote_branch(transport, client)
439
# we do not want bzrdir to make any remote calls
440
bzrdir = RemoteBzrDir(transport, _client=False)
441
branch = RemoteBranch(bzrdir, None, _client=client)
1013
442
result = branch.last_revision_info()
1014
self.assertFinished(client)
445
[('call', 'Branch.last_revision_info', ('quack/',))],
1015
447
self.assertEqual((0, NULL_REVISION), result)
1017
449
def test_non_empty_branch(self):
1019
451
revid = u'\xc8'.encode('utf8')
1020
452
transport = MemoryTransport()
1021
453
client = FakeClient(transport.base)
1022
client.add_expected_call(
1023
'Branch.get_stacked_on_url', ('kwaak/',),
1024
'error', ('NotStacked',))
1025
client.add_expected_call(
1026
'Branch.last_revision_info', ('kwaak/',),
1027
'success', ('ok', '2', revid))
454
client.add_success_response('ok', '2', revid)
1028
455
transport.mkdir('kwaak')
1029
456
transport = transport.clone('kwaak')
1030
branch = self.make_remote_branch(transport, client)
457
# we do not want bzrdir to make any remote calls
458
bzrdir = RemoteBzrDir(transport, _client=False)
459
branch = RemoteBranch(bzrdir, None, _client=client)
1031
460
result = branch.last_revision_info()
463
[('call', 'Branch.last_revision_info', ('kwaak/',))],
1032
465
self.assertEqual((2, revid), result)
1035
class TestBranch_get_stacked_on_url(TestRemote):
1036
"""Test Branch._get_stacked_on_url rpc"""
1038
def test_get_stacked_on_invalid_url(self):
1039
# test that asking for a stacked on url the server can't access works.
1040
# This isn't perfect, but then as we're in the same process there
1041
# really isn't anything we can do to be 100% sure that the server
1042
# doesn't just open in - this test probably needs to be rewritten using
1043
# a spawn()ed server.
1044
stacked_branch = self.make_branch('stacked', format='1.9')
1045
memory_branch = self.make_branch('base', format='1.9')
1046
vfs_url = self.get_vfs_only_url('base')
1047
stacked_branch.set_stacked_on_url(vfs_url)
1048
transport = stacked_branch.bzrdir.root_transport
1049
client = FakeClient(transport.base)
1050
client.add_expected_call(
1051
'Branch.get_stacked_on_url', ('stacked/',),
1052
'success', ('ok', vfs_url))
1053
# XXX: Multiple calls are bad, this second call documents what is
1055
client.add_expected_call(
1056
'Branch.get_stacked_on_url', ('stacked/',),
1057
'success', ('ok', vfs_url))
1058
bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
1060
repo_fmt = remote.RemoteRepositoryFormat()
1061
repo_fmt._custom_format = stacked_branch.repository._format
1062
branch = RemoteBranch(bzrdir, RemoteRepository(bzrdir, repo_fmt),
1064
result = branch.get_stacked_on_url()
1065
self.assertEqual(vfs_url, result)
1067
def test_backwards_compatible(self):
1068
# like with bzr1.6 with no Branch.get_stacked_on_url rpc
1069
base_branch = self.make_branch('base', format='1.6')
1070
stacked_branch = self.make_branch('stacked', format='1.6')
1071
stacked_branch.set_stacked_on_url('../base')
1072
client = FakeClient(self.get_url())
1073
branch_network_name = self.get_branch_format().network_name()
1074
client.add_expected_call(
1075
'BzrDir.open_branchV2', ('stacked/',),
1076
'success', ('branch', branch_network_name))
1077
client.add_expected_call(
1078
'BzrDir.find_repositoryV3', ('stacked/',),
1079
'success', ('ok', '', 'no', 'no', 'yes',
1080
stacked_branch.repository._format.network_name()))
1081
# called twice, once from constructor and then again by us
1082
client.add_expected_call(
1083
'Branch.get_stacked_on_url', ('stacked/',),
1084
'unknown', ('Branch.get_stacked_on_url',))
1085
client.add_expected_call(
1086
'Branch.get_stacked_on_url', ('stacked/',),
1087
'unknown', ('Branch.get_stacked_on_url',))
1088
# this will also do vfs access, but that goes direct to the transport
1089
# and isn't seen by the FakeClient.
1090
bzrdir = RemoteBzrDir(self.get_transport('stacked'),
1091
remote.RemoteBzrDirFormat(), _client=client)
1092
branch = bzrdir.open_branch()
1093
result = branch.get_stacked_on_url()
1094
self.assertEqual('../base', result)
1095
self.assertFinished(client)
1096
# it's in the fallback list both for the RemoteRepository and its vfs
1098
self.assertEqual(1, len(branch.repository._fallback_repositories))
1100
len(branch.repository._real_repository._fallback_repositories))
1102
def test_get_stacked_on_real_branch(self):
1103
base_branch = self.make_branch('base', format='1.6')
1104
stacked_branch = self.make_branch('stacked', format='1.6')
1105
stacked_branch.set_stacked_on_url('../base')
1106
reference_format = self.get_repo_format()
1107
network_name = reference_format.network_name()
1108
client = FakeClient(self.get_url())
1109
branch_network_name = self.get_branch_format().network_name()
1110
client.add_expected_call(
1111
'BzrDir.open_branchV2', ('stacked/',),
1112
'success', ('branch', branch_network_name))
1113
client.add_expected_call(
1114
'BzrDir.find_repositoryV3', ('stacked/',),
1115
'success', ('ok', '', 'no', 'no', 'yes', network_name))
1116
# called twice, once from constructor and then again by us
1117
client.add_expected_call(
1118
'Branch.get_stacked_on_url', ('stacked/',),
1119
'success', ('ok', '../base'))
1120
client.add_expected_call(
1121
'Branch.get_stacked_on_url', ('stacked/',),
1122
'success', ('ok', '../base'))
1123
bzrdir = RemoteBzrDir(self.get_transport('stacked'),
1124
remote.RemoteBzrDirFormat(), _client=client)
1125
branch = bzrdir.open_branch()
1126
result = branch.get_stacked_on_url()
1127
self.assertEqual('../base', result)
1128
self.assertFinished(client)
1129
# it's in the fallback list both for the RemoteRepository.
1130
self.assertEqual(1, len(branch.repository._fallback_repositories))
1131
# And we haven't had to construct a real repository.
1132
self.assertEqual(None, branch.repository._real_repository)
1135
class TestBranchSetLastRevision(RemoteBranchTestCase):
468
class TestBranchSetLastRevision(tests.TestCase):
1137
470
def test_set_empty(self):
1138
471
# set_revision_history([]) is translated to calling
1142
475
transport = transport.clone('branch')
1144
477
client = FakeClient(transport.base)
1145
client.add_expected_call(
1146
'Branch.get_stacked_on_url', ('branch/',),
1147
'error', ('NotStacked',))
1148
client.add_expected_call(
1149
'Branch.lock_write', ('branch/', '', ''),
1150
'success', ('ok', 'branch token', 'repo token'))
1151
client.add_expected_call(
1152
'Branch.last_revision_info',
1154
'success', ('ok', '0', 'null:'))
1155
client.add_expected_call(
1156
'Branch.set_last_revision', ('branch/', 'branch token', 'repo token', 'null:',),
1158
client.add_expected_call(
1159
'Branch.unlock', ('branch/', 'branch token', 'repo token'),
1161
branch = self.make_remote_branch(transport, client)
479
client.add_success_response('ok', 'branch token', 'repo token')
481
client.add_success_response('ok')
483
client.add_success_response('ok')
484
bzrdir = RemoteBzrDir(transport, _client=False)
485
branch = RemoteBranch(bzrdir, None, _client=client)
1162
486
# This is a hack to work around the problem that RemoteBranch currently
1163
487
# unnecessarily invokes _ensure_real upon a call to lock_write.
1164
488
branch._ensure_real = lambda: None
1165
489
branch.lock_write()
1166
491
result = branch.set_revision_history([])
493
[('call', 'Branch.set_last_revision',
494
('branch/', 'branch token', 'repo token', 'null:'))],
1168
497
self.assertEqual(None, result)
1169
self.assertFinished(client)
1171
499
def test_set_nonempty(self):
1172
500
# set_revision_history([rev-id1, ..., rev-idN]) is translated to calling
1176
504
transport = transport.clone('branch')
1178
506
client = FakeClient(transport.base)
1179
client.add_expected_call(
1180
'Branch.get_stacked_on_url', ('branch/',),
1181
'error', ('NotStacked',))
1182
client.add_expected_call(
1183
'Branch.lock_write', ('branch/', '', ''),
1184
'success', ('ok', 'branch token', 'repo token'))
1185
client.add_expected_call(
1186
'Branch.last_revision_info',
1188
'success', ('ok', '0', 'null:'))
1190
encoded_body = bz2.compress('\n'.join(lines))
1191
client.add_success_response_with_body(encoded_body, 'ok')
1192
client.add_expected_call(
1193
'Branch.set_last_revision', ('branch/', 'branch token', 'repo token', 'rev-id2',),
1195
client.add_expected_call(
1196
'Branch.unlock', ('branch/', 'branch token', 'repo token'),
1198
branch = self.make_remote_branch(transport, client)
508
client.add_success_response('ok', 'branch token', 'repo token')
510
client.add_success_response('ok')
512
client.add_success_response('ok')
513
bzrdir = RemoteBzrDir(transport, _client=False)
514
branch = RemoteBranch(bzrdir, None, _client=client)
1199
515
# This is a hack to work around the problem that RemoteBranch currently
1200
516
# unnecessarily invokes _ensure_real upon a call to lock_write.
1201
517
branch._ensure_real = lambda: None
1202
518
# Lock the branch, reset the record of remote calls.
1203
519
branch.lock_write()
1204
522
result = branch.set_revision_history(['rev-id1', 'rev-id2'])
524
[('call', 'Branch.set_last_revision',
525
('branch/', 'branch token', 'repo token', 'rev-id2'))],
1206
528
self.assertEqual(None, result)
1207
self.assertFinished(client)
1209
530
def test_no_such_revision(self):
1210
531
transport = MemoryTransport()
1212
533
transport = transport.clone('branch')
1213
534
# A response of 'NoSuchRevision' is translated into an exception.
1214
535
client = FakeClient(transport.base)
1215
client.add_expected_call(
1216
'Branch.get_stacked_on_url', ('branch/',),
1217
'error', ('NotStacked',))
1218
client.add_expected_call(
1219
'Branch.lock_write', ('branch/', '', ''),
1220
'success', ('ok', 'branch token', 'repo token'))
1221
client.add_expected_call(
1222
'Branch.last_revision_info',
1224
'success', ('ok', '0', 'null:'))
1225
# get_graph calls to construct the revision history, for the set_rh
1228
encoded_body = bz2.compress('\n'.join(lines))
1229
client.add_success_response_with_body(encoded_body, 'ok')
1230
client.add_expected_call(
1231
'Branch.set_last_revision', ('branch/', 'branch token', 'repo token', 'rev-id',),
1232
'error', ('NoSuchRevision', 'rev-id'))
1233
client.add_expected_call(
1234
'Branch.unlock', ('branch/', 'branch token', 'repo token'),
537
client.add_success_response('ok', 'branch token', 'repo token')
539
client.add_error_response('NoSuchRevision', 'rev-id')
541
client.add_success_response('ok')
1237
branch = self.make_remote_branch(transport, client)
543
bzrdir = RemoteBzrDir(transport, _client=False)
544
repo = RemoteRepository(bzrdir, None, _client=client)
545
branch = RemoteBranch(bzrdir, repo, _client=client)
546
branch._ensure_real = lambda: None
1238
547
branch.lock_write()
1239
550
self.assertRaises(
1240
551
errors.NoSuchRevision, branch.set_revision_history, ['rev-id'])
1242
self.assertFinished(client)
1244
554
def test_tip_change_rejected(self):
1245
555
"""TipChangeRejected responses cause a TipChangeRejected exception to
1249
559
transport.mkdir('branch')
1250
560
transport = transport.clone('branch')
1251
561
client = FakeClient(transport.base)
563
client.add_success_response('ok', 'branch token', 'repo token')
1252
565
rejection_msg_unicode = u'rejection message\N{INTERROBANG}'
1253
566
rejection_msg_utf8 = rejection_msg_unicode.encode('utf8')
1254
client.add_expected_call(
1255
'Branch.get_stacked_on_url', ('branch/',),
1256
'error', ('NotStacked',))
1257
client.add_expected_call(
1258
'Branch.lock_write', ('branch/', '', ''),
1259
'success', ('ok', 'branch token', 'repo token'))
1260
client.add_expected_call(
1261
'Branch.last_revision_info',
1263
'success', ('ok', '0', 'null:'))
1265
encoded_body = bz2.compress('\n'.join(lines))
1266
client.add_success_response_with_body(encoded_body, 'ok')
1267
client.add_expected_call(
1268
'Branch.set_last_revision', ('branch/', 'branch token', 'repo token', 'rev-id',),
1269
'error', ('TipChangeRejected', rejection_msg_utf8))
1270
client.add_expected_call(
1271
'Branch.unlock', ('branch/', 'branch token', 'repo token'),
1273
branch = self.make_remote_branch(transport, client)
567
client.add_error_response('TipChangeRejected', rejection_msg_utf8)
569
client.add_success_response('ok')
571
bzrdir = RemoteBzrDir(transport, _client=False)
572
repo = RemoteRepository(bzrdir, None, _client=client)
573
branch = RemoteBranch(bzrdir, repo, _client=client)
1274
574
branch._ensure_real = lambda: None
1275
575
branch.lock_write()
576
self.addCleanup(branch.unlock)
1276
579
# The 'TipChangeRejected' error response triggered by calling
1277
580
# set_revision_history causes a TipChangeRejected exception.
1278
581
err = self.assertRaises(
1459
757
self.assertEqual('rejection message', err.msg)
1462
class TestBranchGetSetConfig(RemoteBranchTestCase):
760
class TestBranchControlGetBranchConf(tests.TestCaseWithMemoryTransport):
761
"""Getting the branch configuration should use an abstract method not vfs.
1464
764
def test_get_branch_conf(self):
1465
# in an empty branch we decode the response properly
1466
client = FakeClient()
1467
client.add_expected_call(
1468
'Branch.get_stacked_on_url', ('memory:///',),
1469
'error', ('NotStacked',),)
1470
client.add_success_response_with_body('# config file body', 'ok')
1471
transport = MemoryTransport()
1472
branch = self.make_remote_branch(transport, client)
1473
config = branch.get_config()
1474
config.has_explicit_nickname()
1476
[('call', 'Branch.get_stacked_on_url', ('memory:///',)),
1477
('call_expecting_body', 'Branch.get_config_file', ('memory:///',))],
1480
def test_get_multi_line_branch_conf(self):
1481
# Make sure that multiple-line branch.conf files are supported
1483
# https://bugs.edge.launchpad.net/bzr/+bug/354075
1484
client = FakeClient()
1485
client.add_expected_call(
1486
'Branch.get_stacked_on_url', ('memory:///',),
1487
'error', ('NotStacked',),)
1488
client.add_success_response_with_body('a = 1\nb = 2\nc = 3\n', 'ok')
1489
transport = MemoryTransport()
1490
branch = self.make_remote_branch(transport, client)
1491
config = branch.get_config()
1492
self.assertEqual(u'2', config.get_user_option('b'))
1494
def test_set_option(self):
1495
client = FakeClient()
1496
client.add_expected_call(
1497
'Branch.get_stacked_on_url', ('memory:///',),
1498
'error', ('NotStacked',),)
1499
client.add_expected_call(
1500
'Branch.lock_write', ('memory:///', '', ''),
1501
'success', ('ok', 'branch token', 'repo token'))
1502
client.add_expected_call(
1503
'Branch.set_config_option', ('memory:///', 'branch token',
1504
'repo token', 'foo', 'bar', ''),
1506
client.add_expected_call(
1507
'Branch.unlock', ('memory:///', 'branch token', 'repo token'),
1509
transport = MemoryTransport()
1510
branch = self.make_remote_branch(transport, client)
1512
config = branch._get_config()
1513
config.set_option('foo', 'bar')
1515
self.assertFinished(client)
1517
def test_backwards_compat_set_option(self):
1518
self.setup_smart_server_with_call_log()
1519
branch = self.make_branch('.')
1520
verb = 'Branch.set_config_option'
1521
self.disable_verb(verb)
1523
self.addCleanup(branch.unlock)
1524
self.reset_smart_call_log()
1525
branch._get_config().set_option('value', 'name')
1526
self.assertLength(10, self.hpss_calls)
1527
self.assertEqual('value', branch._get_config().get_option('name'))
1530
class TestBranchLockWrite(RemoteBranchTestCase):
765
raise tests.KnownFailure('branch.conf is not retrieved by get_config_file')
766
## # We should see that branch.get_config() does a single rpc to get the
767
## # remote configuration file, abstracting away where that is stored on
768
## # the server. However at the moment it always falls back to using the
769
## # vfs, and this would need some changes in config.py.
771
## # in an empty branch we decode the response properly
772
## client = FakeClient([(('ok', ), '# config file body')], self.get_url())
773
## # we need to make a real branch because the remote_branch.control_files
774
## # will trigger _ensure_real.
775
## branch = self.make_branch('quack')
776
## transport = branch.bzrdir.root_transport
777
## # we do not want bzrdir to make any remote calls
778
## bzrdir = RemoteBzrDir(transport, _client=False)
779
## branch = RemoteBranch(bzrdir, None, _client=client)
780
## config = branch.get_config()
782
## [('call_expecting_body', 'Branch.get_config_file', ('quack/',))],
786
class TestBranchLockWrite(tests.TestCase):
1532
788
def test_lock_write_unlockable(self):
1533
789
transport = MemoryTransport()
1534
790
client = FakeClient(transport.base)
1535
client.add_expected_call(
1536
'Branch.get_stacked_on_url', ('quack/',),
1537
'error', ('NotStacked',),)
1538
client.add_expected_call(
1539
'Branch.lock_write', ('quack/', '', ''),
1540
'error', ('UnlockableTransport',))
791
client.add_error_response('UnlockableTransport')
1541
792
transport.mkdir('quack')
1542
793
transport = transport.clone('quack')
1543
branch = self.make_remote_branch(transport, client)
794
# we do not want bzrdir to make any remote calls
795
bzrdir = RemoteBzrDir(transport, _client=False)
796
repo = RemoteRepository(bzrdir, None, _client=client)
797
branch = RemoteBranch(bzrdir, repo, _client=client)
1544
798
self.assertRaises(errors.UnlockableTransport, branch.lock_write)
1545
self.assertFinished(client)
1548
class TestBzrDirGetSetConfig(RemoteBzrDirTestCase):
1550
def test__get_config(self):
1551
client = FakeClient()
1552
client.add_success_response_with_body('default_stack_on = /\n', 'ok')
1553
transport = MemoryTransport()
1554
bzrdir = self.make_remote_bzrdir(transport, client)
1555
config = bzrdir.get_config()
1556
self.assertEqual('/', config.get_default_stack_on())
1557
799
self.assertEqual(
1558
[('call_expecting_body', 'BzrDir.get_config_file', ('memory:///',))],
800
[('call', 'Branch.lock_write', ('quack/', '', ''))],
1561
def test_set_option_uses_vfs(self):
1562
self.setup_smart_server_with_call_log()
1563
bzrdir = self.make_bzrdir('.')
1564
self.reset_smart_call_log()
1565
config = bzrdir.get_config()
1566
config.set_default_stack_on('/')
1567
self.assertLength(3, self.hpss_calls)
1569
def test_backwards_compat_get_option(self):
1570
self.setup_smart_server_with_call_log()
1571
bzrdir = self.make_bzrdir('.')
1572
verb = 'BzrDir.get_config_file'
1573
self.disable_verb(verb)
1574
self.reset_smart_call_log()
1575
self.assertEqual(None,
1576
bzrdir._get_config().get_option('default_stack_on'))
1577
self.assertLength(3, self.hpss_calls)
1580
804
class TestTransportIsReadonly(tests.TestCase):
1849
1034
errors.UnexpectedSmartServerResponse,
1850
1035
repo.get_parent_map, ['a-revision-id'])
1852
def test_get_parent_map_negative_caches_missing_keys(self):
1853
self.setup_smart_server_with_call_log()
1854
repo = self.make_repository('foo')
1855
self.assertIsInstance(repo, RemoteRepository)
1857
self.addCleanup(repo.unlock)
1858
self.reset_smart_call_log()
1859
graph = repo.get_graph()
1860
self.assertEqual({},
1861
graph.get_parent_map(['some-missing', 'other-missing']))
1862
self.assertLength(1, self.hpss_calls)
1863
# No call if we repeat this
1864
self.reset_smart_call_log()
1865
graph = repo.get_graph()
1866
self.assertEqual({},
1867
graph.get_parent_map(['some-missing', 'other-missing']))
1868
self.assertLength(0, self.hpss_calls)
1869
# Asking for more unknown keys makes a request.
1870
self.reset_smart_call_log()
1871
graph = repo.get_graph()
1872
self.assertEqual({},
1873
graph.get_parent_map(['some-missing', 'other-missing',
1875
self.assertLength(1, self.hpss_calls)
1877
def disableExtraResults(self):
1878
old_flag = SmartServerRepositoryGetParentMap.no_extra_results
1879
SmartServerRepositoryGetParentMap.no_extra_results = True
1881
SmartServerRepositoryGetParentMap.no_extra_results = old_flag
1882
self.addCleanup(reset_values)
1884
def test_null_cached_missing_and_stop_key(self):
1885
self.setup_smart_server_with_call_log()
1886
# Make a branch with a single revision.
1887
builder = self.make_branch_builder('foo')
1888
builder.start_series()
1889
builder.build_snapshot('first', None, [
1890
('add', ('', 'root-id', 'directory', ''))])
1891
builder.finish_series()
1892
branch = builder.get_branch()
1893
repo = branch.repository
1894
self.assertIsInstance(repo, RemoteRepository)
1895
# Stop the server from sending extra results.
1896
self.disableExtraResults()
1898
self.addCleanup(repo.unlock)
1899
self.reset_smart_call_log()
1900
graph = repo.get_graph()
1901
# Query for 'first' and 'null:'. Because 'null:' is a parent of
1902
# 'first' it will be a candidate for the stop_keys of subsequent
1903
# requests, and because 'null:' was queried but not returned it will be
1904
# cached as missing.
1905
self.assertEqual({'first': ('null:',)},
1906
graph.get_parent_map(['first', 'null:']))
1907
# Now query for another key. This request will pass along a recipe of
1908
# start and stop keys describing the already cached results, and this
1909
# recipe's revision count must be correct (or else it will trigger an
1910
# error from the server).
1911
self.assertEqual({}, graph.get_parent_map(['another-key']))
1912
# This assertion guards against disableExtraResults silently failing to
1913
# work, thus invalidating the test.
1914
self.assertLength(2, self.hpss_calls)
1916
def test_get_parent_map_gets_ghosts_from_result(self):
1917
# asking for a revision should negatively cache close ghosts in its
1919
self.setup_smart_server_with_call_log()
1920
tree = self.make_branch_and_memory_tree('foo')
1923
builder = treebuilder.TreeBuilder()
1924
builder.start_tree(tree)
1926
builder.finish_tree()
1927
tree.set_parent_ids(['non-existant'], allow_leftmost_as_ghost=True)
1928
rev_id = tree.commit('')
1932
self.addCleanup(tree.unlock)
1933
repo = tree.branch.repository
1934
self.assertIsInstance(repo, RemoteRepository)
1936
repo.get_parent_map([rev_id])
1937
self.reset_smart_call_log()
1938
# Now asking for rev_id's ghost parent should not make calls
1939
self.assertEqual({}, repo.get_parent_map(['non-existant']))
1940
self.assertLength(0, self.hpss_calls)
1943
class TestGetParentMapAllowsNew(tests.TestCaseWithTransport):
1945
def test_allows_new_revisions(self):
1946
"""get_parent_map's results can be updated by commit."""
1947
smart_server = server.SmartTCPServer_for_testing()
1948
smart_server.setUp()
1949
self.addCleanup(smart_server.tearDown)
1950
self.make_branch('branch')
1951
branch = Branch.open(smart_server.get_url() + '/branch')
1952
tree = branch.create_checkout('tree', lightweight=True)
1954
self.addCleanup(tree.unlock)
1955
graph = tree.branch.repository.get_graph()
1956
# This provides an opportunity for the missing rev-id to be cached.
1957
self.assertEqual({}, graph.get_parent_map(['rev1']))
1958
tree.commit('message', rev_id='rev1')
1959
graph = tree.branch.repository.get_graph()
1960
self.assertEqual({'rev1': ('null:',)}, graph.get_parent_map(['rev1']))
1963
1038
class TestRepositoryGetRevisionGraph(TestRemoteRepository):
1965
1040
def test_null_revision(self):
1966
1041
# a null revision has the predictable result {}, we should have no wire
1967
1042
# traffic when calling it with this argument
1968
1043
transport_path = 'empty'
1969
1044
repo, client = self.setup_fake_client_and_repository(transport_path)
1970
1045
client.add_success_response('notused')
1971
# actual RemoteRepository.get_revision_graph is gone, but there's an
1972
# equivalent private method for testing
1973
result = repo._get_revision_graph(NULL_REVISION)
1046
result = self.applyDeprecated(one_four, repo.get_revision_graph,
1974
1048
self.assertEqual([], client._calls)
1975
1049
self.assertEqual({}, result)
2030
1102
transport_path = 'sinhala'
2031
1103
repo, client = self.setup_fake_client_and_repository(transport_path)
2032
1104
client.add_error_response('AnUnexpectedError')
2033
e = self.assertRaises(errors.UnknownErrorFromSmartServer,
2034
repo._get_revision_graph, revid)
1105
e = self.assertRaises(errors.ErrorFromSmartServer,
1106
self.applyDeprecated, one_four, repo.get_revision_graph, revid)
2035
1107
self.assertEqual(('AnUnexpectedError',), e.error_tuple)
2038
class TestRepositoryGetRevIdForRevno(TestRemoteRepository):
2041
repo, client = self.setup_fake_client_and_repository('quack')
2042
client.add_expected_call(
2043
'Repository.get_rev_id_for_revno', ('quack/', 5, (42, 'rev-foo')),
2044
'success', ('ok', 'rev-five'))
2045
result = repo.get_rev_id_for_revno(5, (42, 'rev-foo'))
2046
self.assertEqual((True, 'rev-five'), result)
2047
self.assertFinished(client)
2049
def test_history_incomplete(self):
2050
repo, client = self.setup_fake_client_and_repository('quack')
2051
client.add_expected_call(
2052
'Repository.get_rev_id_for_revno', ('quack/', 5, (42, 'rev-foo')),
2053
'success', ('history-incomplete', 10, 'rev-ten'))
2054
result = repo.get_rev_id_for_revno(5, (42, 'rev-foo'))
2055
self.assertEqual((False, (10, 'rev-ten')), result)
2056
self.assertFinished(client)
2058
def test_history_incomplete_with_fallback(self):
2059
"""A 'history-incomplete' response causes the fallback repository to be
2060
queried too, if one is set.
2062
# Make a repo with a fallback repo, both using a FakeClient.
2063
format = remote.response_tuple_to_repo_format(
2064
('yes', 'no', 'yes', 'fake-network-name'))
2065
repo, client = self.setup_fake_client_and_repository('quack')
2066
repo._format = format
2067
fallback_repo, ignored = self.setup_fake_client_and_repository(
2069
fallback_repo._client = client
2070
repo.add_fallback_repository(fallback_repo)
2071
# First the client should ask the primary repo
2072
client.add_expected_call(
2073
'Repository.get_rev_id_for_revno', ('quack/', 1, (42, 'rev-foo')),
2074
'success', ('history-incomplete', 2, 'rev-two'))
2075
# Then it should ask the fallback, using revno/revid from the
2076
# history-incomplete response as the known revno/revid.
2077
client.add_expected_call(
2078
'Repository.get_rev_id_for_revno',('fallback/', 1, (2, 'rev-two')),
2079
'success', ('ok', 'rev-one'))
2080
result = repo.get_rev_id_for_revno(1, (42, 'rev-foo'))
2081
self.assertEqual((True, 'rev-one'), result)
2082
self.assertFinished(client)
2084
def test_nosuchrevision(self):
2085
# 'nosuchrevision' is returned when the known-revid is not found in the
2086
# remote repo. The client translates that response to NoSuchRevision.
2087
repo, client = self.setup_fake_client_and_repository('quack')
2088
client.add_expected_call(
2089
'Repository.get_rev_id_for_revno', ('quack/', 5, (42, 'rev-foo')),
2090
'error', ('nosuchrevision', 'rev-foo'))
2092
errors.NoSuchRevision,
2093
repo.get_rev_id_for_revno, 5, (42, 'rev-foo'))
2094
self.assertFinished(client)
2097
1110
class TestRepositoryIsShared(TestRemoteRepository):
2099
1112
def test_is_shared(self):
2214
1201
self.assertEqual([], client._calls)
2217
class TestRepositoryInsertStreamBase(TestRemoteRepository):
2218
"""Base class for Repository.insert_stream and .insert_stream_1.19
2222
def checkInsertEmptyStream(self, repo, client):
2223
"""Insert an empty stream, checking the result.
2225
This checks that there are no resume_tokens or missing_keys, and that
2226
the client is finished.
2228
sink = repo._get_sink()
2229
fmt = repository.RepositoryFormat.get_default_format()
2230
resume_tokens, missing_keys = sink.insert_stream([], fmt, [])
2231
self.assertEqual([], resume_tokens)
2232
self.assertEqual(set(), missing_keys)
2233
self.assertFinished(client)
2236
class TestRepositoryInsertStream(TestRepositoryInsertStreamBase):
2237
"""Tests for using Repository.insert_stream verb when the _1.19 variant is
2240
This test case is very similar to TestRepositoryInsertStream_1_19.
2244
TestRemoteRepository.setUp(self)
2245
self.disable_verb('Repository.insert_stream_1.19')
2247
def test_unlocked_repo(self):
2248
transport_path = 'quack'
2249
repo, client = self.setup_fake_client_and_repository(transport_path)
2250
client.add_expected_call(
2251
'Repository.insert_stream_1.19', ('quack/', ''),
2252
'unknown', ('Repository.insert_stream_1.19',))
2253
client.add_expected_call(
2254
'Repository.insert_stream', ('quack/', ''),
2256
client.add_expected_call(
2257
'Repository.insert_stream', ('quack/', ''),
2259
self.checkInsertEmptyStream(repo, client)
2261
def test_locked_repo_with_no_lock_token(self):
2262
transport_path = 'quack'
2263
repo, client = self.setup_fake_client_and_repository(transport_path)
2264
client.add_expected_call(
2265
'Repository.lock_write', ('quack/', ''),
2266
'success', ('ok', ''))
2267
client.add_expected_call(
2268
'Repository.insert_stream_1.19', ('quack/', ''),
2269
'unknown', ('Repository.insert_stream_1.19',))
2270
client.add_expected_call(
2271
'Repository.insert_stream', ('quack/', ''),
2273
client.add_expected_call(
2274
'Repository.insert_stream', ('quack/', ''),
2277
self.checkInsertEmptyStream(repo, client)
2279
def test_locked_repo_with_lock_token(self):
2280
transport_path = 'quack'
2281
repo, client = self.setup_fake_client_and_repository(transport_path)
2282
client.add_expected_call(
2283
'Repository.lock_write', ('quack/', ''),
2284
'success', ('ok', 'a token'))
2285
client.add_expected_call(
2286
'Repository.insert_stream_1.19', ('quack/', '', 'a token'),
2287
'unknown', ('Repository.insert_stream_1.19',))
2288
client.add_expected_call(
2289
'Repository.insert_stream_locked', ('quack/', '', 'a token'),
2291
client.add_expected_call(
2292
'Repository.insert_stream_locked', ('quack/', '', 'a token'),
2295
self.checkInsertEmptyStream(repo, client)
2297
def test_stream_with_inventory_deltas(self):
2298
"""'inventory-deltas' substreams cannot be sent to the
2299
Repository.insert_stream verb, because not all servers that implement
2300
that verb will accept them. So when one is encountered the RemoteSink
2301
immediately stops using that verb and falls back to VFS insert_stream.
2303
transport_path = 'quack'
2304
repo, client = self.setup_fake_client_and_repository(transport_path)
2305
client.add_expected_call(
2306
'Repository.insert_stream_1.19', ('quack/', ''),
2307
'unknown', ('Repository.insert_stream_1.19',))
2308
client.add_expected_call(
2309
'Repository.insert_stream', ('quack/', ''),
2311
client.add_expected_call(
2312
'Repository.insert_stream', ('quack/', ''),
2314
# Create a fake real repository for insert_stream to fall back on, so
2315
# that we can directly see the records the RemoteSink passes to the
2320
def insert_stream(self, stream, src_format, resume_tokens):
2321
for substream_kind, substream in stream:
2322
self.records.append(
2323
(substream_kind, [record.key for record in substream]))
2324
return ['fake tokens'], ['fake missing keys']
2325
fake_real_sink = FakeRealSink()
2326
class FakeRealRepository:
2327
def _get_sink(self):
2328
return fake_real_sink
2329
repo._real_repository = FakeRealRepository()
2330
sink = repo._get_sink()
2331
fmt = repository.RepositoryFormat.get_default_format()
2332
stream = self.make_stream_with_inv_deltas(fmt)
2333
resume_tokens, missing_keys = sink.insert_stream(stream, fmt, [])
2334
# Every record from the first inventory delta should have been sent to
2336
expected_records = [
2337
('inventory-deltas', [('rev2',), ('rev3',)]),
2338
('texts', [('some-rev', 'some-file')])]
2339
self.assertEqual(expected_records, fake_real_sink.records)
2340
# The return values from the real sink's insert_stream are propagated
2341
# back to the original caller.
2342
self.assertEqual(['fake tokens'], resume_tokens)
2343
self.assertEqual(['fake missing keys'], missing_keys)
2344
self.assertFinished(client)
2346
def make_stream_with_inv_deltas(self, fmt):
2347
"""Make a simple stream with an inventory delta followed by more
2348
records and more substreams to test that all records and substreams
2349
from that point on are used.
2351
This sends, in order:
2352
* inventories substream: rev1, rev2, rev3. rev2 and rev3 are
2354
* texts substream: (some-rev, some-file)
2356
# Define a stream using generators so that it isn't rewindable.
2357
inv = inventory.Inventory(revision_id='rev1')
2358
inv.root.revision = 'rev1'
2359
def stream_with_inv_delta():
2360
yield ('inventories', inventories_substream())
2361
yield ('inventory-deltas', inventory_delta_substream())
2363
versionedfile.FulltextContentFactory(
2364
('some-rev', 'some-file'), (), None, 'content')])
2365
def inventories_substream():
2366
# An empty inventory fulltext. This will be streamed normally.
2367
text = fmt._serializer.write_inventory_to_string(inv)
2368
yield versionedfile.FulltextContentFactory(
2369
('rev1',), (), None, text)
2370
def inventory_delta_substream():
2371
# An inventory delta. This can't be streamed via this verb, so it
2372
# will trigger a fallback to VFS insert_stream.
2373
entry = inv.make_entry(
2374
'directory', 'newdir', inv.root.file_id, 'newdir-id')
2375
entry.revision = 'ghost'
2376
delta = [(None, 'newdir', 'newdir-id', entry)]
2377
serializer = inventory_delta.InventoryDeltaSerializer(
2378
versioned_root=True, tree_references=False)
2379
lines = serializer.delta_to_lines('rev1', 'rev2', delta)
2380
yield versionedfile.ChunkedContentFactory(
2381
('rev2',), (('rev1',)), None, lines)
2383
lines = serializer.delta_to_lines('rev1', 'rev3', delta)
2384
yield versionedfile.ChunkedContentFactory(
2385
('rev3',), (('rev1',)), None, lines)
2386
return stream_with_inv_delta()
2389
class TestRepositoryInsertStream_1_19(TestRepositoryInsertStreamBase):
2391
def test_unlocked_repo(self):
2392
transport_path = 'quack'
2393
repo, client = self.setup_fake_client_and_repository(transport_path)
2394
client.add_expected_call(
2395
'Repository.insert_stream_1.19', ('quack/', ''),
2397
client.add_expected_call(
2398
'Repository.insert_stream_1.19', ('quack/', ''),
2400
self.checkInsertEmptyStream(repo, client)
2402
def test_locked_repo_with_no_lock_token(self):
2403
transport_path = 'quack'
2404
repo, client = self.setup_fake_client_and_repository(transport_path)
2405
client.add_expected_call(
2406
'Repository.lock_write', ('quack/', ''),
2407
'success', ('ok', ''))
2408
client.add_expected_call(
2409
'Repository.insert_stream_1.19', ('quack/', ''),
2411
client.add_expected_call(
2412
'Repository.insert_stream_1.19', ('quack/', ''),
2415
self.checkInsertEmptyStream(repo, client)
2417
def test_locked_repo_with_lock_token(self):
2418
transport_path = 'quack'
2419
repo, client = self.setup_fake_client_and_repository(transport_path)
2420
client.add_expected_call(
2421
'Repository.lock_write', ('quack/', ''),
2422
'success', ('ok', 'a token'))
2423
client.add_expected_call(
2424
'Repository.insert_stream_1.19', ('quack/', '', 'a token'),
2426
client.add_expected_call(
2427
'Repository.insert_stream_1.19', ('quack/', '', 'a token'),
2430
self.checkInsertEmptyStream(repo, client)
2433
1204
class TestRepositoryTarball(TestRemoteRepository):
2435
1206
# This is a canned tarball reponse we can validate against
2484
1255
src_repo.copy_content_into(dest_repo)
2487
class _StubRealPackRepository(object):
2489
def __init__(self, calls):
2491
self._pack_collection = _StubPackCollection(calls)
2493
def is_in_write_group(self):
2496
def refresh_data(self):
2497
self.calls.append(('pack collection reload_pack_names',))
2500
class _StubPackCollection(object):
2502
def __init__(self, calls):
2506
self.calls.append(('pack collection autopack',))
2509
class TestRemotePackRepositoryAutoPack(TestRemoteRepository):
2510
"""Tests for RemoteRepository.autopack implementation."""
2513
"""When the server returns 'ok' and there's no _real_repository, then
2514
nothing else happens: the autopack method is done.
2516
transport_path = 'quack'
2517
repo, client = self.setup_fake_client_and_repository(transport_path)
2518
client.add_expected_call(
2519
'PackRepository.autopack', ('quack/',), 'success', ('ok',))
2521
self.assertFinished(client)
2523
def test_ok_with_real_repo(self):
2524
"""When the server returns 'ok' and there is a _real_repository, then
2525
the _real_repository's reload_pack_name's method will be called.
2527
transport_path = 'quack'
2528
repo, client = self.setup_fake_client_and_repository(transport_path)
2529
client.add_expected_call(
2530
'PackRepository.autopack', ('quack/',),
2532
repo._real_repository = _StubRealPackRepository(client._calls)
2535
[('call', 'PackRepository.autopack', ('quack/',)),
2536
('pack collection reload_pack_names',)],
2539
def test_backwards_compatibility(self):
2540
"""If the server does not recognise the PackRepository.autopack verb,
2541
fallback to the real_repository's implementation.
2543
transport_path = 'quack'
2544
repo, client = self.setup_fake_client_and_repository(transport_path)
2545
client.add_unknown_method_response('PackRepository.autopack')
2546
def stub_ensure_real():
2547
client._calls.append(('_ensure_real',))
2548
repo._real_repository = _StubRealPackRepository(client._calls)
2549
repo._ensure_real = stub_ensure_real
2552
[('call', 'PackRepository.autopack', ('quack/',)),
2554
('pack collection autopack',)],
2558
1258
class TestErrorTranslationBase(tests.TestCaseWithMemoryTransport):
2559
1259
"""Base class for unit tests for bzrlib.remote._translate_error."""
2656
1356
expected_error = errors.DivergedBranches(branch, other_branch)
2657
1357
self.assertEqual(expected_error, translated_error)
2659
def test_ReadError_no_args(self):
2661
translated_error = self.translateTuple(('ReadError',), path=path)
2662
expected_error = errors.ReadError(path)
2663
self.assertEqual(expected_error, translated_error)
2665
def test_ReadError(self):
2667
translated_error = self.translateTuple(('ReadError', path))
2668
expected_error = errors.ReadError(path)
2669
self.assertEqual(expected_error, translated_error)
2671
def test_PermissionDenied_no_args(self):
2673
translated_error = self.translateTuple(('PermissionDenied',), path=path)
2674
expected_error = errors.PermissionDenied(path)
2675
self.assertEqual(expected_error, translated_error)
2677
def test_PermissionDenied_one_arg(self):
2679
translated_error = self.translateTuple(('PermissionDenied', path))
2680
expected_error = errors.PermissionDenied(path)
2681
self.assertEqual(expected_error, translated_error)
2683
def test_PermissionDenied_one_arg_and_context(self):
2684
"""Given a choice between a path from the local context and a path on
2685
the wire, _translate_error prefers the path from the local context.
2687
local_path = 'local path'
2688
remote_path = 'remote path'
2689
translated_error = self.translateTuple(
2690
('PermissionDenied', remote_path), path=local_path)
2691
expected_error = errors.PermissionDenied(local_path)
2692
self.assertEqual(expected_error, translated_error)
2694
def test_PermissionDenied_two_args(self):
2696
extra = 'a string with extra info'
2697
translated_error = self.translateTuple(
2698
('PermissionDenied', path, extra))
2699
expected_error = errors.PermissionDenied(path, extra)
2700
self.assertEqual(expected_error, translated_error)
2703
1360
class TestErrorTranslationRobustness(TestErrorTranslationBase):
2704
1361
"""Unit tests for bzrlib.remote._translate_error's robustness.
2706
1363
TestErrorTranslationSuccess is for cases where _translate_error can
2707
1364
translate successfully. This class about how _translate_err behaves when
2708
1365
it fails to translate: it re-raises the original error.
2736
1392
self.assertContainsRe(
2737
1393
self._get_log(keep_log_file=True),
2738
1394
"Missing key 'branch' in context")
2740
def test_path_missing(self):
2741
"""Some translations (PermissionDenied, ReadError) can determine the
2742
'path' variable from either the wire or the local context. If neither
2743
has it, then an error is raised.
2745
error_tuple = ('ReadError',)
2746
server_error = errors.ErrorFromSmartServer(error_tuple)
2747
translated_error = self.translateErrorFromSmartServer(server_error)
2748
self.assertEqual(server_error, translated_error)
2749
# In addition to re-raising ErrorFromSmartServer, some debug info has
2750
# been muttered to the log file for developer to look at.
2751
self.assertContainsRe(
2752
self._get_log(keep_log_file=True), "Missing key 'path' in context")
2755
class TestStacking(tests.TestCaseWithTransport):
2756
"""Tests for operations on stacked remote repositories.
2758
The underlying format type must support stacking.
2761
def test_access_stacked_remote(self):
2762
# based on <http://launchpad.net/bugs/261315>
2763
# make a branch stacked on another repository containing an empty
2764
# revision, then open it over hpss - we should be able to see that
2766
base_transport = self.get_transport()
2767
base_builder = self.make_branch_builder('base', format='1.9')
2768
base_builder.start_series()
2769
base_revid = base_builder.build_snapshot('rev-id', None,
2770
[('add', ('', None, 'directory', None))],
2772
base_builder.finish_series()
2773
stacked_branch = self.make_branch('stacked', format='1.9')
2774
stacked_branch.set_stacked_on_url('../base')
2775
# start a server looking at this
2776
smart_server = server.SmartTCPServer_for_testing()
2777
smart_server.setUp()
2778
self.addCleanup(smart_server.tearDown)
2779
remote_bzrdir = BzrDir.open(smart_server.get_url() + '/stacked')
2780
# can get its branch and repository
2781
remote_branch = remote_bzrdir.open_branch()
2782
remote_repo = remote_branch.repository
2783
remote_repo.lock_read()
2785
# it should have an appropriate fallback repository, which should also
2786
# be a RemoteRepository
2787
self.assertLength(1, remote_repo._fallback_repositories)
2788
self.assertIsInstance(remote_repo._fallback_repositories[0],
2790
# and it has the revision committed to the underlying repository;
2791
# these have varying implementations so we try several of them
2792
self.assertTrue(remote_repo.has_revisions([base_revid]))
2793
self.assertTrue(remote_repo.has_revision(base_revid))
2794
self.assertEqual(remote_repo.get_revision(base_revid).message,
2797
remote_repo.unlock()
2799
def prepare_stacked_remote_branch(self):
2800
"""Get stacked_upon and stacked branches with content in each."""
2801
self.setup_smart_server_with_call_log()
2802
tree1 = self.make_branch_and_tree('tree1', format='1.9')
2803
tree1.commit('rev1', rev_id='rev1')
2804
tree2 = tree1.branch.bzrdir.sprout('tree2', stacked=True
2805
).open_workingtree()
2806
local_tree = tree2.branch.create_checkout('local')
2807
local_tree.commit('local changes make me feel good.')
2808
branch2 = Branch.open(self.get_url('tree2'))
2810
self.addCleanup(branch2.unlock)
2811
return tree1.branch, branch2
2813
def test_stacked_get_parent_map(self):
2814
# the public implementation of get_parent_map obeys stacking
2815
_, branch = self.prepare_stacked_remote_branch()
2816
repo = branch.repository
2817
self.assertEqual(['rev1'], repo.get_parent_map(['rev1']).keys())
2819
def test_unstacked_get_parent_map(self):
2820
# _unstacked_provider.get_parent_map ignores stacking
2821
_, branch = self.prepare_stacked_remote_branch()
2822
provider = branch.repository._unstacked_provider
2823
self.assertEqual([], provider.get_parent_map(['rev1']).keys())
2825
def fetch_stream_to_rev_order(self, stream):
2827
for kind, substream in stream:
2828
if not kind == 'revisions':
2831
for content in substream:
2832
result.append(content.key[-1])
2835
def get_ordered_revs(self, format, order, branch_factory=None):
2836
"""Get a list of the revisions in a stream to format format.
2838
:param format: The format of the target.
2839
:param order: the order that target should have requested.
2840
:param branch_factory: A callable to create a trunk and stacked branch
2841
to fetch from. If none, self.prepare_stacked_remote_branch is used.
2842
:result: The revision ids in the stream, in the order seen,
2843
the topological order of revisions in the source.
2845
unordered_format = bzrdir.format_registry.get(format)()
2846
target_repository_format = unordered_format.repository_format
2848
self.assertEqual(order, target_repository_format._fetch_order)
2849
if branch_factory is None:
2850
branch_factory = self.prepare_stacked_remote_branch
2851
_, stacked = branch_factory()
2852
source = stacked.repository._get_source(target_repository_format)
2853
tip = stacked.last_revision()
2854
revs = stacked.repository.get_ancestry(tip)
2855
search = graph.PendingAncestryResult([tip], stacked.repository)
2856
self.reset_smart_call_log()
2857
stream = source.get_stream(search)
2860
# We trust that if a revision is in the stream the rest of the new
2861
# content for it is too, as per our main fetch tests; here we are
2862
# checking that the revisions are actually included at all, and their
2864
return self.fetch_stream_to_rev_order(stream), revs
2866
def test_stacked_get_stream_unordered(self):
2867
# Repository._get_source.get_stream() from a stacked repository with
2868
# unordered yields the full data from both stacked and stacked upon
2870
rev_ord, expected_revs = self.get_ordered_revs('1.9', 'unordered')
2871
self.assertEqual(set(expected_revs), set(rev_ord))
2872
# Getting unordered results should have made a streaming data request
2873
# from the server, then one from the backing branch.
2874
self.assertLength(2, self.hpss_calls)
2876
def test_stacked_on_stacked_get_stream_unordered(self):
2877
# Repository._get_source.get_stream() from a stacked repository which
2878
# is itself stacked yields the full data from all three sources.
2879
def make_stacked_stacked():
2880
_, stacked = self.prepare_stacked_remote_branch()
2881
tree = stacked.bzrdir.sprout('tree3', stacked=True
2882
).open_workingtree()
2883
local_tree = tree.branch.create_checkout('local-tree3')
2884
local_tree.commit('more local changes are better')
2885
branch = Branch.open(self.get_url('tree3'))
2888
rev_ord, expected_revs = self.get_ordered_revs('1.9', 'unordered',
2889
branch_factory=make_stacked_stacked)
2890
self.assertEqual(set(expected_revs), set(rev_ord))
2891
# Getting unordered results should have made a streaming data request
2892
# from the server, and one from each backing repo
2893
self.assertLength(3, self.hpss_calls)
2895
def test_stacked_get_stream_topological(self):
2896
# Repository._get_source.get_stream() from a stacked repository with
2897
# topological sorting yields the full data from both stacked and
2898
# stacked upon sources in topological order.
2899
rev_ord, expected_revs = self.get_ordered_revs('knit', 'topological')
2900
self.assertEqual(expected_revs, rev_ord)
2901
# Getting topological sort requires VFS calls still - one of which is
2902
# pushing up from the bound branch.
2903
self.assertLength(13, self.hpss_calls)
2905
def test_stacked_get_stream_groupcompress(self):
2906
# Repository._get_source.get_stream() from a stacked repository with
2907
# groupcompress sorting yields the full data from both stacked and
2908
# stacked upon sources in groupcompress order.
2909
raise tests.TestSkipped('No groupcompress ordered format available')
2910
rev_ord, expected_revs = self.get_ordered_revs('dev5', 'groupcompress')
2911
self.assertEqual(expected_revs, reversed(rev_ord))
2912
# Getting unordered results should have made a streaming data request
2913
# from the backing branch, and one from the stacked on branch.
2914
self.assertLength(2, self.hpss_calls)
2916
def test_stacked_pull_more_than_stacking_has_bug_360791(self):
2917
# When pulling some fixed amount of content that is more than the
2918
# source has (because some is coming from a fallback branch, no error
2919
# should be received. This was reported as bug 360791.
2920
# Need three branches: a trunk, a stacked branch, and a preexisting
2921
# branch pulling content from stacked and trunk.
2922
self.setup_smart_server_with_call_log()
2923
trunk = self.make_branch_and_tree('trunk', format="1.9-rich-root")
2924
r1 = trunk.commit('start')
2925
stacked_branch = trunk.branch.create_clone_on_transport(
2926
self.get_transport('stacked'), stacked_on=trunk.branch.base)
2927
local = self.make_branch('local', format='1.9-rich-root')
2928
local.repository.fetch(stacked_branch.repository,
2929
stacked_branch.last_revision())
2932
class TestRemoteBranchEffort(tests.TestCaseWithTransport):
2935
super(TestRemoteBranchEffort, self).setUp()
2936
# Create a smart server that publishes whatever the backing VFS server
2938
self.smart_server = server.SmartTCPServer_for_testing()
2939
self.smart_server.setUp(self.get_server())
2940
self.addCleanup(self.smart_server.tearDown)
2941
# Log all HPSS calls into self.hpss_calls.
2942
_SmartClient.hooks.install_named_hook(
2943
'call', self.capture_hpss_call, None)
2944
self.hpss_calls = []
2946
def capture_hpss_call(self, params):
2947
self.hpss_calls.append(params.method)
2949
def test_copy_content_into_avoids_revision_history(self):
2950
local = self.make_branch('local')
2951
remote_backing_tree = self.make_branch_and_tree('remote')
2952
remote_backing_tree.commit("Commit.")
2953
remote_branch_url = self.smart_server.get_url() + 'remote'
2954
remote_branch = bzrdir.BzrDir.open(remote_branch_url).open_branch()
2955
local.repository.fetch(remote_branch.repository)
2956
self.hpss_calls = []
2957
remote_branch.copy_content_into(local)
2958
self.assertFalse('Branch.revision_history' in self.hpss_calls)