238
158
def add_unknown_method_response(self, verb):
239
159
self.responses.append(('unknown', verb))
241
def finished_test(self):
242
if self._expected_calls:
243
raise AssertionError("%r finished but was still expecting %r"
244
% (self, self._expected_calls[0]))
246
161
def _get_next_response(self):
248
response_tuple = self.responses.pop(0)
249
except IndexError, e:
250
raise AssertionError("%r didn't expect any more calls"
162
response_tuple = self.responses.pop(0)
252
163
if response_tuple[0] == 'unknown':
253
164
raise errors.UnknownSmartMethod(response_tuple[1])
254
165
elif response_tuple[0] == 'error':
255
166
raise errors.ErrorFromSmartServer(response_tuple[1])
256
167
return response_tuple
258
def _check_call(self, method, args):
259
if self._expected_calls is None:
260
# the test should be updated to say what it expects
263
next_call = self._expected_calls.pop(0)
265
raise AssertionError("%r didn't expect any more calls "
267
% (self, method, args,))
268
if next_call is None:
270
if method != next_call[0] or args != next_call[1]:
271
raise AssertionError("%r expected %r%r "
273
% (self, next_call[0], next_call[1], method, args,))
275
169
def call(self, method, *args):
276
self._check_call(method, args)
277
170
self._calls.append(('call', method, args))
278
171
return self._get_next_response()[1]
280
173
def call_expecting_body(self, method, *args):
281
self._check_call(method, args)
282
174
self._calls.append(('call_expecting_body', method, args))
283
175
result = self._get_next_response()
284
176
self.expecting_body = True
285
177
return result[1], FakeProtocol(result[2], self)
287
def call_with_body_bytes(self, method, args, body):
288
self._check_call(method, args)
289
self._calls.append(('call_with_body_bytes', method, args, body))
290
result = self._get_next_response()
291
return result[1], FakeProtocol(result[2], self)
293
179
def call_with_body_bytes_expecting_body(self, method, args, body):
294
self._check_call(method, args)
295
180
self._calls.append(('call_with_body_bytes_expecting_body', method,
297
182
result = self._get_next_response()
298
183
self.expecting_body = True
299
184
return result[1], FakeProtocol(result[2], self)
301
def call_with_body_stream(self, args, stream):
302
# Explicitly consume the stream before checking for an error, because
303
# that's what happens a real medium.
304
stream = list(stream)
305
self._check_call(args[0], args[1:])
306
self._calls.append(('call_with_body_stream', args[0], args[1:], stream))
307
result = self._get_next_response()
308
# The second value returned from call_with_body_stream is supposed to
309
# be a response_handler object, but so far no tests depend on that.
310
response_handler = None
311
return result[1], response_handler
314
187
class FakeMedium(medium.SmartClientMedium):
418
276
# Calling _remember_remote_is_before again with a lower value works.
419
277
client_medium._remember_remote_is_before((1, 5))
420
278
self.assertTrue(client_medium._is_remote_before((1, 5)))
421
# If you call _remember_remote_is_before with a higher value it logs a
422
# warning, and continues to remember the lower value.
423
self.assertNotContainsRe(self.get_log(), '_remember_remote_is_before')
424
client_medium._remember_remote_is_before((1, 9))
425
self.assertContainsRe(self.get_log(), '_remember_remote_is_before')
426
self.assertTrue(client_medium._is_remote_before((1, 5)))
429
class TestBzrDirCloningMetaDir(TestRemote):
431
def test_backwards_compat(self):
432
self.setup_smart_server_with_call_log()
433
a_dir = self.make_bzrdir('.')
434
self.reset_smart_call_log()
435
verb = 'BzrDir.cloning_metadir'
436
self.disable_verb(verb)
437
format = a_dir.cloning_metadir()
438
call_count = len([call for call in self.hpss_calls if
439
call.call.method == verb])
440
self.assertEqual(1, call_count)
442
def test_branch_reference(self):
443
transport = self.get_transport('quack')
444
referenced = self.make_branch('referenced')
445
expected = referenced.bzrdir.cloning_metadir()
446
client = FakeClient(transport.base)
447
client.add_expected_call(
448
'BzrDir.cloning_metadir', ('quack/', 'False'),
449
'error', ('BranchReference',)),
450
client.add_expected_call(
451
'BzrDir.open_branchV3', ('quack/',),
452
'success', ('ref', self.get_url('referenced'))),
453
a_bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
455
result = a_bzrdir.cloning_metadir()
456
# We should have got a control dir matching the referenced branch.
457
self.assertEqual(bzrdir.BzrDirMetaFormat1, type(result))
458
self.assertEqual(expected._repository_format, result._repository_format)
459
self.assertEqual(expected._branch_format, result._branch_format)
460
self.assertFinished(client)
462
def test_current_server(self):
463
transport = self.get_transport('.')
464
transport = transport.clone('quack')
465
self.make_bzrdir('quack')
466
client = FakeClient(transport.base)
467
reference_bzrdir_format = bzrdir.format_registry.get('default')()
468
control_name = reference_bzrdir_format.network_name()
469
client.add_expected_call(
470
'BzrDir.cloning_metadir', ('quack/', 'False'),
471
'success', (control_name, '', ('branch', ''))),
472
a_bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
474
result = a_bzrdir.cloning_metadir()
475
# We should have got a reference control dir with default branch and
476
# repository formats.
477
# This pokes a little, just to be sure.
478
self.assertEqual(bzrdir.BzrDirMetaFormat1, type(result))
479
self.assertEqual(None, result._repository_format)
480
self.assertEqual(None, result._branch_format)
481
self.assertFinished(client)
484
class TestBzrDirOpen(TestRemote):
486
def make_fake_client_and_transport(self, path='quack'):
487
transport = MemoryTransport()
488
transport.mkdir(path)
489
transport = transport.clone(path)
490
client = FakeClient(transport.base)
491
return client, transport
493
def test_absent(self):
494
client, transport = self.make_fake_client_and_transport()
495
client.add_expected_call(
496
'BzrDir.open_2.1', ('quack/',), 'success', ('no',))
497
self.assertRaises(errors.NotBranchError, RemoteBzrDir, transport,
498
remote.RemoteBzrDirFormat(), _client=client, _force_probe=True)
499
self.assertFinished(client)
501
def test_present_without_workingtree(self):
502
client, transport = self.make_fake_client_and_transport()
503
client.add_expected_call(
504
'BzrDir.open_2.1', ('quack/',), 'success', ('yes', 'no'))
505
bd = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
506
_client=client, _force_probe=True)
507
self.assertIsInstance(bd, RemoteBzrDir)
508
self.assertFalse(bd.has_workingtree())
509
self.assertRaises(errors.NoWorkingTree, bd.open_workingtree)
510
self.assertFinished(client)
512
def test_present_with_workingtree(self):
513
client, transport = self.make_fake_client_and_transport()
514
client.add_expected_call(
515
'BzrDir.open_2.1', ('quack/',), 'success', ('yes', 'yes'))
516
bd = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
517
_client=client, _force_probe=True)
518
self.assertIsInstance(bd, RemoteBzrDir)
519
self.assertTrue(bd.has_workingtree())
520
self.assertRaises(errors.NotLocalUrl, bd.open_workingtree)
521
self.assertFinished(client)
523
def test_backwards_compat(self):
524
client, transport = self.make_fake_client_and_transport()
525
client.add_expected_call(
526
'BzrDir.open_2.1', ('quack/',), 'unknown', ('BzrDir.open_2.1',))
527
client.add_expected_call(
528
'BzrDir.open', ('quack/',), 'success', ('yes',))
529
bd = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
530
_client=client, _force_probe=True)
531
self.assertIsInstance(bd, RemoteBzrDir)
532
self.assertFinished(client)
534
def test_backwards_compat_hpss_v2(self):
535
client, transport = self.make_fake_client_and_transport()
536
# Monkey-patch fake client to simulate real-world behaviour with v2
537
# server: upon first RPC call detect the protocol version, and because
538
# the version is 2 also do _remember_remote_is_before((1, 6)) before
539
# continuing with the RPC.
540
orig_check_call = client._check_call
541
def check_call(method, args):
542
client._medium._protocol_version = 2
543
client._medium._remember_remote_is_before((1, 6))
544
client._check_call = orig_check_call
545
client._check_call(method, args)
546
client._check_call = check_call
547
client.add_expected_call(
548
'BzrDir.open_2.1', ('quack/',), 'unknown', ('BzrDir.open_2.1',))
549
client.add_expected_call(
550
'BzrDir.open', ('quack/',), 'success', ('yes',))
551
bd = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
552
_client=client, _force_probe=True)
553
self.assertIsInstance(bd, RemoteBzrDir)
554
self.assertFinished(client)
557
class TestBzrDirOpenBranch(TestRemote):
559
def test_backwards_compat(self):
560
self.setup_smart_server_with_call_log()
561
self.make_branch('.')
562
a_dir = BzrDir.open(self.get_url('.'))
563
self.reset_smart_call_log()
564
verb = 'BzrDir.open_branchV3'
565
self.disable_verb(verb)
566
format = a_dir.open_branch()
567
call_count = len([call for call in self.hpss_calls if
568
call.call.method == verb])
569
self.assertEqual(1, call_count)
279
# You cannot call _remember_remote_is_before with a larger value.
281
AssertionError, client_medium._remember_remote_is_before, (1, 9))
284
class TestBzrDirOpenBranch(tests.TestCase):
571
286
def test_branch_present(self):
572
reference_format = self.get_repo_format()
573
network_name = reference_format.network_name()
574
branch_network_name = self.get_branch_format().network_name()
575
287
transport = MemoryTransport()
576
288
transport.mkdir('quack')
577
289
transport = transport.clone('quack')
578
290
client = FakeClient(transport.base)
579
client.add_expected_call(
580
'BzrDir.open_branchV3', ('quack/',),
581
'success', ('branch', branch_network_name))
582
client.add_expected_call(
583
'BzrDir.find_repositoryV3', ('quack/',),
584
'success', ('ok', '', 'no', 'no', 'no', network_name))
585
client.add_expected_call(
586
'Branch.get_stacked_on_url', ('quack/',),
587
'error', ('NotStacked',))
588
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)
590
294
result = bzrdir.open_branch()
296
[('call', 'BzrDir.open_branch', ('quack/',)),
297
('call', 'BzrDir.find_repositoryV2', ('quack/',))],
591
299
self.assertIsInstance(result, RemoteBranch)
592
300
self.assertEqual(bzrdir, result.bzrdir)
593
self.assertFinished(client)
595
302
def test_branch_missing(self):
596
303
transport = MemoryTransport()
689
382
RemoteBzrDirFormat.probe_transport, OldServerTransport())
692
class TestBzrDirCreateBranch(TestRemote):
694
def test_backwards_compat(self):
695
self.setup_smart_server_with_call_log()
696
repo = self.make_repository('.')
697
self.reset_smart_call_log()
698
self.disable_verb('BzrDir.create_branch')
699
branch = repo.bzrdir.create_branch()
700
create_branch_call_count = len([call for call in self.hpss_calls if
701
call.call.method == 'BzrDir.create_branch'])
702
self.assertEqual(1, create_branch_call_count)
704
def test_current_server(self):
705
transport = self.get_transport('.')
706
transport = transport.clone('quack')
707
self.make_repository('quack')
708
client = FakeClient(transport.base)
709
reference_bzrdir_format = bzrdir.format_registry.get('default')()
710
reference_format = reference_bzrdir_format.get_branch_format()
711
network_name = reference_format.network_name()
712
reference_repo_fmt = reference_bzrdir_format.repository_format
713
reference_repo_name = reference_repo_fmt.network_name()
714
client.add_expected_call(
715
'BzrDir.create_branch', ('quack/', network_name),
716
'success', ('ok', network_name, '', 'no', 'no', 'yes',
717
reference_repo_name))
718
a_bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
720
branch = a_bzrdir.create_branch()
721
# We should have got a remote branch
722
self.assertIsInstance(branch, remote.RemoteBranch)
723
# its format should have the settings from the response
724
format = branch._format
725
self.assertEqual(network_name, format.network_name())
728
class TestBzrDirCreateRepository(TestRemote):
730
def test_backwards_compat(self):
731
self.setup_smart_server_with_call_log()
732
bzrdir = self.make_bzrdir('.')
733
self.reset_smart_call_log()
734
self.disable_verb('BzrDir.create_repository')
735
repo = bzrdir.create_repository()
736
create_repo_call_count = len([call for call in self.hpss_calls if
737
call.call.method == 'BzrDir.create_repository'])
738
self.assertEqual(1, create_repo_call_count)
740
def test_current_server(self):
741
transport = self.get_transport('.')
742
transport = transport.clone('quack')
743
self.make_bzrdir('quack')
744
client = FakeClient(transport.base)
745
reference_bzrdir_format = bzrdir.format_registry.get('default')()
746
reference_format = reference_bzrdir_format.repository_format
747
network_name = reference_format.network_name()
748
client.add_expected_call(
749
'BzrDir.create_repository', ('quack/',
750
'Bazaar repository format 2a (needs bzr 1.16 or later)\n',
752
'success', ('ok', 'yes', 'yes', 'yes', network_name))
753
a_bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
755
repo = a_bzrdir.create_repository()
756
# We should have got a remote repository
757
self.assertIsInstance(repo, remote.RemoteRepository)
758
# its format should have the settings from the response
759
format = repo._format
760
self.assertTrue(format.rich_root_data)
761
self.assertTrue(format.supports_tree_reference)
762
self.assertTrue(format.supports_external_lookups)
763
self.assertEqual(network_name, format.network_name())
766
class TestBzrDirOpenRepository(TestRemote):
768
def test_backwards_compat_1_2_3(self):
769
# fallback all the way to the first version.
770
reference_format = self.get_repo_format()
771
network_name = reference_format.network_name()
772
server_url = 'bzr://example.com/'
773
self.permit_url(server_url)
774
client = FakeClient(server_url)
775
client.add_unknown_method_response('BzrDir.find_repositoryV3')
776
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')
777
393
client.add_success_response('ok', '', 'no', 'no')
778
# A real repository instance will be created to determine the network
780
client.add_success_response_with_body(
781
"Bazaar-NG meta directory, format 1\n", 'ok')
782
client.add_success_response_with_body(
783
reference_format.get_format_string(), 'ok')
784
# PackRepository wants to do a stat
785
client.add_success_response('stat', '0', '65535')
786
remote_transport = RemoteTransport(server_url + 'quack/', medium=False,
788
bzrdir = RemoteBzrDir(remote_transport, remote.RemoteBzrDirFormat(),
790
repo = bzrdir.open_repository()
792
[('call', 'BzrDir.find_repositoryV3', ('quack/',)),
793
('call', 'BzrDir.find_repositoryV2', ('quack/',)),
794
('call', 'BzrDir.find_repository', ('quack/',)),
795
('call_expecting_body', 'get', ('/quack/.bzr/branch-format',)),
796
('call_expecting_body', 'get', ('/quack/.bzr/repository/format',)),
797
('call', 'stat', ('/quack/.bzr/repository',)),
800
self.assertEqual(network_name, repo._format.network_name())
802
def test_backwards_compat_2(self):
803
# fallback to find_repositoryV2
804
reference_format = self.get_repo_format()
805
network_name = reference_format.network_name()
806
server_url = 'bzr://example.com/'
807
self.permit_url(server_url)
808
client = FakeClient(server_url)
809
client.add_unknown_method_response('BzrDir.find_repositoryV3')
810
client.add_success_response('ok', '', 'no', 'no', 'no')
811
# A real repository instance will be created to determine the network
813
client.add_success_response_with_body(
814
"Bazaar-NG meta directory, format 1\n", 'ok')
815
client.add_success_response_with_body(
816
reference_format.get_format_string(), 'ok')
817
# PackRepository wants to do a stat
818
client.add_success_response('stat', '0', '65535')
819
remote_transport = RemoteTransport(server_url + 'quack/', medium=False,
821
bzrdir = RemoteBzrDir(remote_transport, remote.RemoteBzrDirFormat(),
823
repo = bzrdir.open_repository()
825
[('call', 'BzrDir.find_repositoryV3', ('quack/',)),
826
('call', 'BzrDir.find_repositoryV2', ('quack/',)),
827
('call_expecting_body', 'get', ('/quack/.bzr/branch-format',)),
828
('call_expecting_body', 'get', ('/quack/.bzr/repository/format',)),
829
('call', 'stat', ('/quack/.bzr/repository',)),
832
self.assertEqual(network_name, repo._format.network_name())
834
def test_current_server(self):
835
reference_format = self.get_repo_format()
836
network_name = reference_format.network_name()
837
transport = MemoryTransport()
838
transport.mkdir('quack')
839
transport = transport.clone('quack')
840
client = FakeClient(transport.base)
841
client.add_success_response('ok', '', 'no', 'no', 'no', network_name)
842
bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
844
repo = bzrdir.open_repository()
846
[('call', 'BzrDir.find_repositoryV3', ('quack/',))],
848
self.assertEqual(network_name, repo._format.network_name())
851
class TestBzrDirFormatInitializeEx(TestRemote):
853
def test_success(self):
854
"""Simple test for typical successful call."""
855
fmt = bzrdir.RemoteBzrDirFormat()
856
default_format_name = BzrDirFormat.get_default_format().network_name()
857
transport = self.get_transport()
858
client = FakeClient(transport.base)
859
client.add_expected_call(
860
'BzrDirFormat.initialize_ex_1.16',
861
(default_format_name, 'path', 'False', 'False', 'False', '',
862
'', '', '', 'False'),
864
('.', 'no', 'no', 'yes', 'repo fmt', 'repo bzrdir fmt',
865
'bzrdir fmt', 'False', '', '', 'repo lock token'))
866
# XXX: It would be better to call fmt.initialize_on_transport_ex, but
867
# it's currently hard to test that without supplying a real remote
868
# transport connected to a real server.
869
result = fmt._initialize_on_transport_ex_rpc(client, 'path',
870
transport, False, False, False, None, None, None, None, False)
871
self.assertFinished(client)
873
def test_error(self):
874
"""Error responses are translated, e.g. 'PermissionDenied' raises the
875
corresponding error from the client.
877
fmt = bzrdir.RemoteBzrDirFormat()
878
default_format_name = BzrDirFormat.get_default_format().network_name()
879
transport = self.get_transport()
880
client = FakeClient(transport.base)
881
client.add_expected_call(
882
'BzrDirFormat.initialize_ex_1.16',
883
(default_format_name, 'path', 'False', 'False', 'False', '',
884
'', '', '', 'False'),
886
('PermissionDenied', 'path', 'extra info'))
887
# XXX: It would be better to call fmt.initialize_on_transport_ex, but
888
# it's currently hard to test that without supplying a real remote
889
# transport connected to a real server.
890
err = self.assertRaises(errors.PermissionDenied,
891
fmt._initialize_on_transport_ex_rpc, client, 'path', transport,
892
False, False, False, None, None, None, None, False)
893
self.assertEqual('path', err.path)
894
self.assertEqual(': extra info', err.extra)
895
self.assertFinished(client)
897
def test_error_from_real_server(self):
898
"""Integration test for error translation."""
899
transport = self.make_smart_server('foo')
900
transport = transport.clone('no-such-path')
901
fmt = bzrdir.RemoteBzrDirFormat()
902
err = self.assertRaises(errors.NoSuchFile,
903
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/',))],
906
402
class OldSmartClient(object):
931
427
return OldSmartClient()
934
class RemoteBzrDirTestCase(TestRemote):
936
def make_remote_bzrdir(self, transport, client):
937
"""Make a RemotebzrDir using 'client' as the _client."""
938
return RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
942
class RemoteBranchTestCase(RemoteBzrDirTestCase):
944
def lock_remote_branch(self, branch):
945
"""Trick a RemoteBranch into thinking it is locked."""
946
branch._lock_mode = 'w'
947
branch._lock_count = 2
948
branch._lock_token = 'branch token'
949
branch._repo_lock_token = 'repo token'
950
branch.repository._lock_mode = 'w'
951
branch.repository._lock_count = 2
952
branch.repository._lock_token = 'repo token'
430
class RemoteBranchTestCase(tests.TestCase):
954
432
def make_remote_branch(self, transport, client):
955
433
"""Make a RemoteBranch using 'client' as its _SmartClient.
957
435
A RemoteBzrDir and RemoteRepository will also be created to fill out
958
436
the RemoteBranch, albeit with stub values for some of their attributes.
960
438
# we do not want bzrdir to make any remote calls, so use False as its
961
439
# _client. If it tries to make a remote call, this will fail
963
bzrdir = self.make_remote_bzrdir(transport, False)
441
bzrdir = RemoteBzrDir(transport, _client=False)
964
442
repo = RemoteRepository(bzrdir, None, _client=client)
965
branch_format = self.get_branch_format()
966
format = RemoteBranchFormat(network_name=branch_format.network_name())
967
return RemoteBranch(bzrdir, repo, _client=client, format=format)
970
class TestBranchGetParent(RemoteBranchTestCase):
972
def test_no_parent(self):
973
# in an empty branch we decode the response properly
974
transport = MemoryTransport()
975
client = FakeClient(transport.base)
976
client.add_expected_call(
977
'Branch.get_stacked_on_url', ('quack/',),
978
'error', ('NotStacked',))
979
client.add_expected_call(
980
'Branch.get_parent', ('quack/',),
982
transport.mkdir('quack')
983
transport = transport.clone('quack')
984
branch = self.make_remote_branch(transport, client)
985
result = branch.get_parent()
986
self.assertFinished(client)
987
self.assertEqual(None, result)
989
def test_parent_relative(self):
990
transport = MemoryTransport()
991
client = FakeClient(transport.base)
992
client.add_expected_call(
993
'Branch.get_stacked_on_url', ('kwaak/',),
994
'error', ('NotStacked',))
995
client.add_expected_call(
996
'Branch.get_parent', ('kwaak/',),
997
'success', ('../foo/',))
998
transport.mkdir('kwaak')
999
transport = transport.clone('kwaak')
1000
branch = self.make_remote_branch(transport, client)
1001
result = branch.get_parent()
1002
self.assertEqual(transport.clone('../foo').base, result)
1004
def test_parent_absolute(self):
1005
transport = MemoryTransport()
1006
client = FakeClient(transport.base)
1007
client.add_expected_call(
1008
'Branch.get_stacked_on_url', ('kwaak/',),
1009
'error', ('NotStacked',))
1010
client.add_expected_call(
1011
'Branch.get_parent', ('kwaak/',),
1012
'success', ('http://foo/',))
1013
transport.mkdir('kwaak')
1014
transport = transport.clone('kwaak')
1015
branch = self.make_remote_branch(transport, client)
1016
result = branch.get_parent()
1017
self.assertEqual('http://foo/', result)
1018
self.assertFinished(client)
1021
class TestBranchSetParentLocation(RemoteBranchTestCase):
1023
def test_no_parent(self):
1024
# We call the verb when setting parent to None
1025
transport = MemoryTransport()
1026
client = FakeClient(transport.base)
1027
client.add_expected_call(
1028
'Branch.get_stacked_on_url', ('quack/',),
1029
'error', ('NotStacked',))
1030
client.add_expected_call(
1031
'Branch.set_parent_location', ('quack/', 'b', 'r', ''),
1033
transport.mkdir('quack')
1034
transport = transport.clone('quack')
1035
branch = self.make_remote_branch(transport, client)
1036
branch._lock_token = 'b'
1037
branch._repo_lock_token = 'r'
1038
branch._set_parent_location(None)
1039
self.assertFinished(client)
1041
def test_parent(self):
1042
transport = MemoryTransport()
1043
client = FakeClient(transport.base)
1044
client.add_expected_call(
1045
'Branch.get_stacked_on_url', ('kwaak/',),
1046
'error', ('NotStacked',))
1047
client.add_expected_call(
1048
'Branch.set_parent_location', ('kwaak/', 'b', 'r', 'foo'),
1050
transport.mkdir('kwaak')
1051
transport = transport.clone('kwaak')
1052
branch = self.make_remote_branch(transport, client)
1053
branch._lock_token = 'b'
1054
branch._repo_lock_token = 'r'
1055
branch._set_parent_location('foo')
1056
self.assertFinished(client)
1058
def test_backwards_compat(self):
1059
self.setup_smart_server_with_call_log()
1060
branch = self.make_branch('.')
1061
self.reset_smart_call_log()
1062
verb = 'Branch.set_parent_location'
1063
self.disable_verb(verb)
1064
branch.set_parent('http://foo/')
1065
self.assertLength(12, self.hpss_calls)
1068
class TestBranchGetTagsBytes(RemoteBranchTestCase):
1070
def test_backwards_compat(self):
1071
self.setup_smart_server_with_call_log()
1072
branch = self.make_branch('.')
1073
self.reset_smart_call_log()
1074
verb = 'Branch.get_tags_bytes'
1075
self.disable_verb(verb)
1076
branch.tags.get_tag_dict()
1077
call_count = len([call for call in self.hpss_calls if
1078
call.call.method == verb])
1079
self.assertEqual(1, call_count)
1081
def test_trivial(self):
1082
transport = MemoryTransport()
1083
client = FakeClient(transport.base)
1084
client.add_expected_call(
1085
'Branch.get_stacked_on_url', ('quack/',),
1086
'error', ('NotStacked',))
1087
client.add_expected_call(
1088
'Branch.get_tags_bytes', ('quack/',),
1090
transport.mkdir('quack')
1091
transport = transport.clone('quack')
1092
branch = self.make_remote_branch(transport, client)
1093
result = branch.tags.get_tag_dict()
1094
self.assertFinished(client)
1095
self.assertEqual({}, result)
1098
class TestBranchSetTagsBytes(RemoteBranchTestCase):
1100
def test_trivial(self):
1101
transport = MemoryTransport()
1102
client = FakeClient(transport.base)
1103
client.add_expected_call(
1104
'Branch.get_stacked_on_url', ('quack/',),
1105
'error', ('NotStacked',))
1106
client.add_expected_call(
1107
'Branch.set_tags_bytes', ('quack/', 'branch token', 'repo token'),
1109
transport.mkdir('quack')
1110
transport = transport.clone('quack')
1111
branch = self.make_remote_branch(transport, client)
1112
self.lock_remote_branch(branch)
1113
branch._set_tags_bytes('tags bytes')
1114
self.assertFinished(client)
1115
self.assertEqual('tags bytes', client._calls[-1][-1])
1117
def test_backwards_compatible(self):
1118
transport = MemoryTransport()
1119
client = FakeClient(transport.base)
1120
client.add_expected_call(
1121
'Branch.get_stacked_on_url', ('quack/',),
1122
'error', ('NotStacked',))
1123
client.add_expected_call(
1124
'Branch.set_tags_bytes', ('quack/', 'branch token', 'repo token'),
1125
'unknown', ('Branch.set_tags_bytes',))
1126
transport.mkdir('quack')
1127
transport = transport.clone('quack')
1128
branch = self.make_remote_branch(transport, client)
1129
self.lock_remote_branch(branch)
1130
class StubRealBranch(object):
1133
def _set_tags_bytes(self, bytes):
1134
self.calls.append(('set_tags_bytes', bytes))
1135
real_branch = StubRealBranch()
1136
branch._real_branch = real_branch
1137
branch._set_tags_bytes('tags bytes')
1138
# Call a second time, to exercise the 'remote version already inferred'
1140
branch._set_tags_bytes('tags bytes')
1141
self.assertFinished(client)
1143
[('set_tags_bytes', 'tags bytes')] * 2, real_branch.calls)
443
return RemoteBranch(bzrdir, repo, _client=client)
1146
446
class TestBranchLastRevisionInfo(RemoteBranchTestCase):
1167
465
revid = u'\xc8'.encode('utf8')
1168
466
transport = MemoryTransport()
1169
467
client = FakeClient(transport.base)
1170
client.add_expected_call(
1171
'Branch.get_stacked_on_url', ('kwaak/',),
1172
'error', ('NotStacked',))
1173
client.add_expected_call(
1174
'Branch.last_revision_info', ('kwaak/',),
1175
'success', ('ok', '2', revid))
468
client.add_success_response('ok', '2', revid)
1176
469
transport.mkdir('kwaak')
1177
470
transport = transport.clone('kwaak')
1178
471
branch = self.make_remote_branch(transport, client)
1179
472
result = branch.last_revision_info()
475
[('call', 'Branch.last_revision_info', ('kwaak/',))],
1180
477
self.assertEqual((2, revid), result)
1183
class TestBranch_get_stacked_on_url(TestRemote):
1184
"""Test Branch._get_stacked_on_url rpc"""
1186
def test_get_stacked_on_invalid_url(self):
1187
# test that asking for a stacked on url the server can't access works.
1188
# This isn't perfect, but then as we're in the same process there
1189
# really isn't anything we can do to be 100% sure that the server
1190
# doesn't just open in - this test probably needs to be rewritten using
1191
# a spawn()ed server.
1192
stacked_branch = self.make_branch('stacked', format='1.9')
1193
memory_branch = self.make_branch('base', format='1.9')
1194
vfs_url = self.get_vfs_only_url('base')
1195
stacked_branch.set_stacked_on_url(vfs_url)
1196
transport = stacked_branch.bzrdir.root_transport
1197
client = FakeClient(transport.base)
1198
client.add_expected_call(
1199
'Branch.get_stacked_on_url', ('stacked/',),
1200
'success', ('ok', vfs_url))
1201
# XXX: Multiple calls are bad, this second call documents what is
1203
client.add_expected_call(
1204
'Branch.get_stacked_on_url', ('stacked/',),
1205
'success', ('ok', vfs_url))
1206
bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
1208
repo_fmt = remote.RemoteRepositoryFormat()
1209
repo_fmt._custom_format = stacked_branch.repository._format
1210
branch = RemoteBranch(bzrdir, RemoteRepository(bzrdir, repo_fmt),
1212
result = branch.get_stacked_on_url()
1213
self.assertEqual(vfs_url, result)
1215
def test_backwards_compatible(self):
1216
# like with bzr1.6 with no Branch.get_stacked_on_url rpc
1217
base_branch = self.make_branch('base', format='1.6')
1218
stacked_branch = self.make_branch('stacked', format='1.6')
1219
stacked_branch.set_stacked_on_url('../base')
1220
client = FakeClient(self.get_url())
1221
branch_network_name = self.get_branch_format().network_name()
1222
client.add_expected_call(
1223
'BzrDir.open_branchV3', ('stacked/',),
1224
'success', ('branch', branch_network_name))
1225
client.add_expected_call(
1226
'BzrDir.find_repositoryV3', ('stacked/',),
1227
'success', ('ok', '', 'no', 'no', 'yes',
1228
stacked_branch.repository._format.network_name()))
1229
# called twice, once from constructor and then again by us
1230
client.add_expected_call(
1231
'Branch.get_stacked_on_url', ('stacked/',),
1232
'unknown', ('Branch.get_stacked_on_url',))
1233
client.add_expected_call(
1234
'Branch.get_stacked_on_url', ('stacked/',),
1235
'unknown', ('Branch.get_stacked_on_url',))
1236
# this will also do vfs access, but that goes direct to the transport
1237
# and isn't seen by the FakeClient.
1238
bzrdir = RemoteBzrDir(self.get_transport('stacked'),
1239
remote.RemoteBzrDirFormat(), _client=client)
1240
branch = bzrdir.open_branch()
1241
result = branch.get_stacked_on_url()
1242
self.assertEqual('../base', result)
1243
self.assertFinished(client)
1244
# it's in the fallback list both for the RemoteRepository and its vfs
1246
self.assertEqual(1, len(branch.repository._fallback_repositories))
1248
len(branch.repository._real_repository._fallback_repositories))
1250
def test_get_stacked_on_real_branch(self):
1251
base_branch = self.make_branch('base')
1252
stacked_branch = self.make_branch('stacked')
1253
stacked_branch.set_stacked_on_url('../base')
1254
reference_format = self.get_repo_format()
1255
network_name = reference_format.network_name()
1256
client = FakeClient(self.get_url())
1257
branch_network_name = self.get_branch_format().network_name()
1258
client.add_expected_call(
1259
'BzrDir.open_branchV3', ('stacked/',),
1260
'success', ('branch', branch_network_name))
1261
client.add_expected_call(
1262
'BzrDir.find_repositoryV3', ('stacked/',),
1263
'success', ('ok', '', 'yes', 'no', 'yes', network_name))
1264
# called twice, once from constructor and then again by us
1265
client.add_expected_call(
1266
'Branch.get_stacked_on_url', ('stacked/',),
1267
'success', ('ok', '../base'))
1268
client.add_expected_call(
1269
'Branch.get_stacked_on_url', ('stacked/',),
1270
'success', ('ok', '../base'))
1271
bzrdir = RemoteBzrDir(self.get_transport('stacked'),
1272
remote.RemoteBzrDirFormat(), _client=client)
1273
branch = bzrdir.open_branch()
1274
result = branch.get_stacked_on_url()
1275
self.assertEqual('../base', result)
1276
self.assertFinished(client)
1277
# it's in the fallback list both for the RemoteRepository.
1278
self.assertEqual(1, len(branch.repository._fallback_repositories))
1279
# And we haven't had to construct a real repository.
1280
self.assertEqual(None, branch.repository._real_repository)
1283
480
class TestBranchSetLastRevision(RemoteBranchTestCase):
1285
482
def test_set_empty(self):
1324
512
transport = transport.clone('branch')
1326
514
client = FakeClient(transport.base)
1327
client.add_expected_call(
1328
'Branch.get_stacked_on_url', ('branch/',),
1329
'error', ('NotStacked',))
1330
client.add_expected_call(
1331
'Branch.lock_write', ('branch/', '', ''),
1332
'success', ('ok', 'branch token', 'repo token'))
1333
client.add_expected_call(
1334
'Branch.last_revision_info',
1336
'success', ('ok', '0', 'null:'))
1338
encoded_body = bz2.compress('\n'.join(lines))
1339
client.add_success_response_with_body(encoded_body, 'ok')
1340
client.add_expected_call(
1341
'Branch.set_last_revision', ('branch/', 'branch token', 'repo token', 'rev-id2',),
1343
client.add_expected_call(
1344
'Branch.unlock', ('branch/', 'branch token', 'repo token'),
516
client.add_success_response('ok', 'branch token', 'repo token')
518
client.add_success_response('ok')
520
client.add_success_response('ok')
1346
521
branch = self.make_remote_branch(transport, client)
1347
# This is a hack to work around the problem that RemoteBranch currently
1348
# unnecessarily invokes _ensure_real upon a call to lock_write.
1349
branch._ensure_real = lambda: None
1350
522
# Lock the branch, reset the record of remote calls.
1351
523
branch.lock_write()
1352
526
result = branch.set_revision_history(['rev-id1', 'rev-id2'])
528
[('call', 'Branch.set_last_revision',
529
('branch/', 'branch token', 'repo token', 'rev-id2'))],
1354
532
self.assertEqual(None, result)
1355
self.assertFinished(client)
1357
534
def test_no_such_revision(self):
1358
535
transport = MemoryTransport()
1597
738
self.assertEqual('rejection message', err.msg)
1600
class TestBranchGetSetConfig(RemoteBranchTestCase):
741
class TestBranchControlGetBranchConf(tests.TestCaseWithMemoryTransport):
742
"""Getting the branch configuration should use an abstract method not vfs.
1602
745
def test_get_branch_conf(self):
1603
# in an empty branch we decode the response properly
1604
client = FakeClient()
1605
client.add_expected_call(
1606
'Branch.get_stacked_on_url', ('memory:///',),
1607
'error', ('NotStacked',),)
1608
client.add_success_response_with_body('# config file body', 'ok')
1609
transport = MemoryTransport()
1610
branch = self.make_remote_branch(transport, client)
1611
config = branch.get_config()
1612
config.has_explicit_nickname()
1614
[('call', 'Branch.get_stacked_on_url', ('memory:///',)),
1615
('call_expecting_body', 'Branch.get_config_file', ('memory:///',))],
1618
def test_get_multi_line_branch_conf(self):
1619
# Make sure that multiple-line branch.conf files are supported
1621
# https://bugs.edge.launchpad.net/bzr/+bug/354075
1622
client = FakeClient()
1623
client.add_expected_call(
1624
'Branch.get_stacked_on_url', ('memory:///',),
1625
'error', ('NotStacked',),)
1626
client.add_success_response_with_body('a = 1\nb = 2\nc = 3\n', 'ok')
1627
transport = MemoryTransport()
1628
branch = self.make_remote_branch(transport, client)
1629
config = branch.get_config()
1630
self.assertEqual(u'2', config.get_user_option('b'))
1632
def test_set_option(self):
1633
client = FakeClient()
1634
client.add_expected_call(
1635
'Branch.get_stacked_on_url', ('memory:///',),
1636
'error', ('NotStacked',),)
1637
client.add_expected_call(
1638
'Branch.lock_write', ('memory:///', '', ''),
1639
'success', ('ok', 'branch token', 'repo token'))
1640
client.add_expected_call(
1641
'Branch.set_config_option', ('memory:///', 'branch token',
1642
'repo token', 'foo', 'bar', ''),
1644
client.add_expected_call(
1645
'Branch.unlock', ('memory:///', 'branch token', 'repo token'),
1647
transport = MemoryTransport()
1648
branch = self.make_remote_branch(transport, client)
1650
config = branch._get_config()
1651
config.set_option('foo', 'bar')
1653
self.assertFinished(client)
1655
def test_backwards_compat_set_option(self):
1656
self.setup_smart_server_with_call_log()
1657
branch = self.make_branch('.')
1658
verb = 'Branch.set_config_option'
1659
self.disable_verb(verb)
1661
self.addCleanup(branch.unlock)
1662
self.reset_smart_call_log()
1663
branch._get_config().set_option('value', 'name')
1664
self.assertLength(10, self.hpss_calls)
1665
self.assertEqual('value', branch._get_config().get_option('name'))
746
raise tests.KnownFailure('branch.conf is not retrieved by get_config_file')
747
## # We should see that branch.get_config() does a single rpc to get the
748
## # remote configuration file, abstracting away where that is stored on
749
## # the server. However at the moment it always falls back to using the
750
## # vfs, and this would need some changes in config.py.
752
## # in an empty branch we decode the response properly
753
## client = FakeClient([(('ok', ), '# config file body')], self.get_url())
754
## # we need to make a real branch because the remote_branch.control_files
755
## # will trigger _ensure_real.
756
## branch = self.make_branch('quack')
757
## transport = branch.bzrdir.root_transport
758
## # we do not want bzrdir to make any remote calls
759
## bzrdir = RemoteBzrDir(transport, _client=False)
760
## branch = RemoteBranch(bzrdir, None, _client=client)
761
## config = branch.get_config()
763
## [('call_expecting_body', 'Branch.get_config_file', ('quack/',))],
1668
767
class TestBranchLockWrite(RemoteBranchTestCase):
1670
769
def test_lock_write_unlockable(self):
1671
770
transport = MemoryTransport()
1672
771
client = FakeClient(transport.base)
1673
client.add_expected_call(
1674
'Branch.get_stacked_on_url', ('quack/',),
1675
'error', ('NotStacked',),)
1676
client.add_expected_call(
1677
'Branch.lock_write', ('quack/', '', ''),
1678
'error', ('UnlockableTransport',))
772
client.add_error_response('UnlockableTransport')
1679
773
transport.mkdir('quack')
1680
774
transport = transport.clone('quack')
1681
775
branch = self.make_remote_branch(transport, client)
1682
776
self.assertRaises(errors.UnlockableTransport, branch.lock_write)
1683
self.assertFinished(client)
1686
class TestBzrDirGetSetConfig(RemoteBzrDirTestCase):
1688
def test__get_config(self):
1689
client = FakeClient()
1690
client.add_success_response_with_body('default_stack_on = /\n', 'ok')
1691
transport = MemoryTransport()
1692
bzrdir = self.make_remote_bzrdir(transport, client)
1693
config = bzrdir.get_config()
1694
self.assertEqual('/', config.get_default_stack_on())
1695
777
self.assertEqual(
1696
[('call_expecting_body', 'BzrDir.get_config_file', ('memory:///',))],
778
[('call', 'Branch.lock_write', ('quack/', '', ''))],
1699
def test_set_option_uses_vfs(self):
1700
self.setup_smart_server_with_call_log()
1701
bzrdir = self.make_bzrdir('.')
1702
self.reset_smart_call_log()
1703
config = bzrdir.get_config()
1704
config.set_default_stack_on('/')
1705
self.assertLength(3, self.hpss_calls)
1707
def test_backwards_compat_get_option(self):
1708
self.setup_smart_server_with_call_log()
1709
bzrdir = self.make_bzrdir('.')
1710
verb = 'BzrDir.get_config_file'
1711
self.disable_verb(verb)
1712
self.reset_smart_call_log()
1713
self.assertEqual(None,
1714
bzrdir._get_config().get_option('default_stack_on'))
1715
self.assertLength(3, self.hpss_calls)
1718
782
class TestTransportIsReadonly(tests.TestCase):
2008
1012
errors.UnexpectedSmartServerResponse,
2009
1013
repo.get_parent_map, ['a-revision-id'])
2011
def test_get_parent_map_negative_caches_missing_keys(self):
2012
self.setup_smart_server_with_call_log()
2013
repo = self.make_repository('foo')
2014
self.assertIsInstance(repo, RemoteRepository)
2016
self.addCleanup(repo.unlock)
2017
self.reset_smart_call_log()
2018
graph = repo.get_graph()
2019
self.assertEqual({},
2020
graph.get_parent_map(['some-missing', 'other-missing']))
2021
self.assertLength(1, self.hpss_calls)
2022
# No call if we repeat this
2023
self.reset_smart_call_log()
2024
graph = repo.get_graph()
2025
self.assertEqual({},
2026
graph.get_parent_map(['some-missing', 'other-missing']))
2027
self.assertLength(0, self.hpss_calls)
2028
# Asking for more unknown keys makes a request.
2029
self.reset_smart_call_log()
2030
graph = repo.get_graph()
2031
self.assertEqual({},
2032
graph.get_parent_map(['some-missing', 'other-missing',
2034
self.assertLength(1, self.hpss_calls)
2036
def disableExtraResults(self):
2037
self.overrideAttr(SmartServerRepositoryGetParentMap,
2038
'no_extra_results', True)
2040
def test_null_cached_missing_and_stop_key(self):
2041
self.setup_smart_server_with_call_log()
2042
# Make a branch with a single revision.
2043
builder = self.make_branch_builder('foo')
2044
builder.start_series()
2045
builder.build_snapshot('first', None, [
2046
('add', ('', 'root-id', 'directory', ''))])
2047
builder.finish_series()
2048
branch = builder.get_branch()
2049
repo = branch.repository
2050
self.assertIsInstance(repo, RemoteRepository)
2051
# Stop the server from sending extra results.
2052
self.disableExtraResults()
2054
self.addCleanup(repo.unlock)
2055
self.reset_smart_call_log()
2056
graph = repo.get_graph()
2057
# Query for 'first' and 'null:'. Because 'null:' is a parent of
2058
# 'first' it will be a candidate for the stop_keys of subsequent
2059
# requests, and because 'null:' was queried but not returned it will be
2060
# cached as missing.
2061
self.assertEqual({'first': ('null:',)},
2062
graph.get_parent_map(['first', 'null:']))
2063
# Now query for another key. This request will pass along a recipe of
2064
# start and stop keys describing the already cached results, and this
2065
# recipe's revision count must be correct (or else it will trigger an
2066
# error from the server).
2067
self.assertEqual({}, graph.get_parent_map(['another-key']))
2068
# This assertion guards against disableExtraResults silently failing to
2069
# work, thus invalidating the test.
2070
self.assertLength(2, self.hpss_calls)
2072
def test_get_parent_map_gets_ghosts_from_result(self):
2073
# asking for a revision should negatively cache close ghosts in its
2075
self.setup_smart_server_with_call_log()
2076
tree = self.make_branch_and_memory_tree('foo')
2079
builder = treebuilder.TreeBuilder()
2080
builder.start_tree(tree)
2082
builder.finish_tree()
2083
tree.set_parent_ids(['non-existant'], allow_leftmost_as_ghost=True)
2084
rev_id = tree.commit('')
2088
self.addCleanup(tree.unlock)
2089
repo = tree.branch.repository
2090
self.assertIsInstance(repo, RemoteRepository)
2092
repo.get_parent_map([rev_id])
2093
self.reset_smart_call_log()
2094
# Now asking for rev_id's ghost parent should not make calls
2095
self.assertEqual({}, repo.get_parent_map(['non-existant']))
2096
self.assertLength(0, self.hpss_calls)
2099
class TestGetParentMapAllowsNew(tests.TestCaseWithTransport):
2101
def test_allows_new_revisions(self):
2102
"""get_parent_map's results can be updated by commit."""
2103
smart_server = test_server.SmartTCPServer_for_testing()
2104
self.start_server(smart_server)
2105
self.make_branch('branch')
2106
branch = Branch.open(smart_server.get_url() + '/branch')
2107
tree = branch.create_checkout('tree', lightweight=True)
2109
self.addCleanup(tree.unlock)
2110
graph = tree.branch.repository.get_graph()
2111
# This provides an opportunity for the missing rev-id to be cached.
2112
self.assertEqual({}, graph.get_parent_map(['rev1']))
2113
tree.commit('message', rev_id='rev1')
2114
graph = tree.branch.repository.get_graph()
2115
self.assertEqual({'rev1': ('null:',)}, graph.get_parent_map(['rev1']))
2118
1016
class TestRepositoryGetRevisionGraph(TestRemoteRepository):
2120
1018
def test_null_revision(self):
2121
1019
# a null revision has the predictable result {}, we should have no wire
2122
1020
# traffic when calling it with this argument
2123
1021
transport_path = 'empty'
2124
1022
repo, client = self.setup_fake_client_and_repository(transport_path)
2125
1023
client.add_success_response('notused')
2126
# actual RemoteRepository.get_revision_graph is gone, but there's an
2127
# equivalent private method for testing
2128
result = repo._get_revision_graph(NULL_REVISION)
1024
result = self.applyDeprecated(one_four, repo.get_revision_graph,
2129
1026
self.assertEqual([], client._calls)
2130
1027
self.assertEqual({}, result)
2186
1081
repo, client = self.setup_fake_client_and_repository(transport_path)
2187
1082
client.add_error_response('AnUnexpectedError')
2188
1083
e = self.assertRaises(errors.UnknownErrorFromSmartServer,
2189
repo._get_revision_graph, revid)
1084
self.applyDeprecated, one_four, repo.get_revision_graph, revid)
2190
1085
self.assertEqual(('AnUnexpectedError',), e.error_tuple)
2193
class TestRepositoryGetRevIdForRevno(TestRemoteRepository):
2196
repo, client = self.setup_fake_client_and_repository('quack')
2197
client.add_expected_call(
2198
'Repository.get_rev_id_for_revno', ('quack/', 5, (42, 'rev-foo')),
2199
'success', ('ok', 'rev-five'))
2200
result = repo.get_rev_id_for_revno(5, (42, 'rev-foo'))
2201
self.assertEqual((True, 'rev-five'), result)
2202
self.assertFinished(client)
2204
def test_history_incomplete(self):
2205
repo, client = self.setup_fake_client_and_repository('quack')
2206
client.add_expected_call(
2207
'Repository.get_rev_id_for_revno', ('quack/', 5, (42, 'rev-foo')),
2208
'success', ('history-incomplete', 10, 'rev-ten'))
2209
result = repo.get_rev_id_for_revno(5, (42, 'rev-foo'))
2210
self.assertEqual((False, (10, 'rev-ten')), result)
2211
self.assertFinished(client)
2213
def test_history_incomplete_with_fallback(self):
2214
"""A 'history-incomplete' response causes the fallback repository to be
2215
queried too, if one is set.
2217
# Make a repo with a fallback repo, both using a FakeClient.
2218
format = remote.response_tuple_to_repo_format(
2219
('yes', 'no', 'yes', self.get_repo_format().network_name()))
2220
repo, client = self.setup_fake_client_and_repository('quack')
2221
repo._format = format
2222
fallback_repo, ignored = self.setup_fake_client_and_repository(
2224
fallback_repo._client = client
2225
fallback_repo._format = format
2226
repo.add_fallback_repository(fallback_repo)
2227
# First the client should ask the primary repo
2228
client.add_expected_call(
2229
'Repository.get_rev_id_for_revno', ('quack/', 1, (42, 'rev-foo')),
2230
'success', ('history-incomplete', 2, 'rev-two'))
2231
# Then it should ask the fallback, using revno/revid from the
2232
# history-incomplete response as the known revno/revid.
2233
client.add_expected_call(
2234
'Repository.get_rev_id_for_revno',('fallback/', 1, (2, 'rev-two')),
2235
'success', ('ok', 'rev-one'))
2236
result = repo.get_rev_id_for_revno(1, (42, 'rev-foo'))
2237
self.assertEqual((True, 'rev-one'), result)
2238
self.assertFinished(client)
2240
def test_nosuchrevision(self):
2241
# 'nosuchrevision' is returned when the known-revid is not found in the
2242
# remote repo. The client translates that response to NoSuchRevision.
2243
repo, client = self.setup_fake_client_and_repository('quack')
2244
client.add_expected_call(
2245
'Repository.get_rev_id_for_revno', ('quack/', 5, (42, 'rev-foo')),
2246
'error', ('nosuchrevision', 'rev-foo'))
2248
errors.NoSuchRevision,
2249
repo.get_rev_id_for_revno, 5, (42, 'rev-foo'))
2250
self.assertFinished(client)
2252
def test_branch_fallback_locking(self):
2253
"""RemoteBranch.get_rev_id takes a read lock, and tries to call the
2254
get_rev_id_for_revno verb. If the verb is unknown the VFS fallback
2255
will be invoked, which will fail if the repo is unlocked.
2257
self.setup_smart_server_with_call_log()
2258
tree = self.make_branch_and_memory_tree('.')
2260
rev1 = tree.commit('First')
2261
rev2 = tree.commit('Second')
2263
branch = tree.branch
2264
self.assertFalse(branch.is_locked())
2265
self.reset_smart_call_log()
2266
verb = 'Repository.get_rev_id_for_revno'
2267
self.disable_verb(verb)
2268
self.assertEqual(rev1, branch.get_rev_id(1))
2269
self.assertLength(1, [call for call in self.hpss_calls if
2270
call.call.method == verb])
2273
1088
class TestRepositoryIsShared(TestRemoteRepository):
2275
1090
def test_is_shared(self):
2390
1179
self.assertEqual([], client._calls)
2393
class TestRepositoryInsertStreamBase(TestRemoteRepository):
2394
"""Base class for Repository.insert_stream and .insert_stream_1.19
2398
def checkInsertEmptyStream(self, repo, client):
2399
"""Insert an empty stream, checking the result.
2401
This checks that there are no resume_tokens or missing_keys, and that
2402
the client is finished.
2404
sink = repo._get_sink()
2405
fmt = repository.RepositoryFormat.get_default_format()
2406
resume_tokens, missing_keys = sink.insert_stream([], fmt, [])
2407
self.assertEqual([], resume_tokens)
2408
self.assertEqual(set(), missing_keys)
2409
self.assertFinished(client)
2412
class TestRepositoryInsertStream(TestRepositoryInsertStreamBase):
2413
"""Tests for using Repository.insert_stream verb when the _1.19 variant is
2416
This test case is very similar to TestRepositoryInsertStream_1_19.
2420
TestRemoteRepository.setUp(self)
2421
self.disable_verb('Repository.insert_stream_1.19')
2423
def test_unlocked_repo(self):
2424
transport_path = 'quack'
2425
repo, client = self.setup_fake_client_and_repository(transport_path)
2426
client.add_expected_call(
2427
'Repository.insert_stream_1.19', ('quack/', ''),
2428
'unknown', ('Repository.insert_stream_1.19',))
2429
client.add_expected_call(
2430
'Repository.insert_stream', ('quack/', ''),
2432
client.add_expected_call(
2433
'Repository.insert_stream', ('quack/', ''),
2435
self.checkInsertEmptyStream(repo, client)
2437
def test_locked_repo_with_no_lock_token(self):
2438
transport_path = 'quack'
2439
repo, client = self.setup_fake_client_and_repository(transport_path)
2440
client.add_expected_call(
2441
'Repository.lock_write', ('quack/', ''),
2442
'success', ('ok', ''))
2443
client.add_expected_call(
2444
'Repository.insert_stream_1.19', ('quack/', ''),
2445
'unknown', ('Repository.insert_stream_1.19',))
2446
client.add_expected_call(
2447
'Repository.insert_stream', ('quack/', ''),
2449
client.add_expected_call(
2450
'Repository.insert_stream', ('quack/', ''),
2453
self.checkInsertEmptyStream(repo, client)
2455
def test_locked_repo_with_lock_token(self):
2456
transport_path = 'quack'
2457
repo, client = self.setup_fake_client_and_repository(transport_path)
2458
client.add_expected_call(
2459
'Repository.lock_write', ('quack/', ''),
2460
'success', ('ok', 'a token'))
2461
client.add_expected_call(
2462
'Repository.insert_stream_1.19', ('quack/', '', 'a token'),
2463
'unknown', ('Repository.insert_stream_1.19',))
2464
client.add_expected_call(
2465
'Repository.insert_stream_locked', ('quack/', '', 'a token'),
2467
client.add_expected_call(
2468
'Repository.insert_stream_locked', ('quack/', '', 'a token'),
2471
self.checkInsertEmptyStream(repo, client)
2473
def test_stream_with_inventory_deltas(self):
2474
"""'inventory-deltas' substreams cannot be sent to the
2475
Repository.insert_stream verb, because not all servers that implement
2476
that verb will accept them. So when one is encountered the RemoteSink
2477
immediately stops using that verb and falls back to VFS insert_stream.
2479
transport_path = 'quack'
2480
repo, client = self.setup_fake_client_and_repository(transport_path)
2481
client.add_expected_call(
2482
'Repository.insert_stream_1.19', ('quack/', ''),
2483
'unknown', ('Repository.insert_stream_1.19',))
2484
client.add_expected_call(
2485
'Repository.insert_stream', ('quack/', ''),
2487
client.add_expected_call(
2488
'Repository.insert_stream', ('quack/', ''),
2490
# Create a fake real repository for insert_stream to fall back on, so
2491
# that we can directly see the records the RemoteSink passes to the
2496
def insert_stream(self, stream, src_format, resume_tokens):
2497
for substream_kind, substream in stream:
2498
self.records.append(
2499
(substream_kind, [record.key for record in substream]))
2500
return ['fake tokens'], ['fake missing keys']
2501
fake_real_sink = FakeRealSink()
2502
class FakeRealRepository:
2503
def _get_sink(self):
2504
return fake_real_sink
2505
def is_in_write_group(self):
2507
def refresh_data(self):
2509
repo._real_repository = FakeRealRepository()
2510
sink = repo._get_sink()
2511
fmt = repository.RepositoryFormat.get_default_format()
2512
stream = self.make_stream_with_inv_deltas(fmt)
2513
resume_tokens, missing_keys = sink.insert_stream(stream, fmt, [])
2514
# Every record from the first inventory delta should have been sent to
2516
expected_records = [
2517
('inventory-deltas', [('rev2',), ('rev3',)]),
2518
('texts', [('some-rev', 'some-file')])]
2519
self.assertEqual(expected_records, fake_real_sink.records)
2520
# The return values from the real sink's insert_stream are propagated
2521
# back to the original caller.
2522
self.assertEqual(['fake tokens'], resume_tokens)
2523
self.assertEqual(['fake missing keys'], missing_keys)
2524
self.assertFinished(client)
2526
def make_stream_with_inv_deltas(self, fmt):
2527
"""Make a simple stream with an inventory delta followed by more
2528
records and more substreams to test that all records and substreams
2529
from that point on are used.
2531
This sends, in order:
2532
* inventories substream: rev1, rev2, rev3. rev2 and rev3 are
2534
* texts substream: (some-rev, some-file)
2536
# Define a stream using generators so that it isn't rewindable.
2537
inv = inventory.Inventory(revision_id='rev1')
2538
inv.root.revision = 'rev1'
2539
def stream_with_inv_delta():
2540
yield ('inventories', inventories_substream())
2541
yield ('inventory-deltas', inventory_delta_substream())
2543
versionedfile.FulltextContentFactory(
2544
('some-rev', 'some-file'), (), None, 'content')])
2545
def inventories_substream():
2546
# An empty inventory fulltext. This will be streamed normally.
2547
text = fmt._serializer.write_inventory_to_string(inv)
2548
yield versionedfile.FulltextContentFactory(
2549
('rev1',), (), None, text)
2550
def inventory_delta_substream():
2551
# An inventory delta. This can't be streamed via this verb, so it
2552
# will trigger a fallback to VFS insert_stream.
2553
entry = inv.make_entry(
2554
'directory', 'newdir', inv.root.file_id, 'newdir-id')
2555
entry.revision = 'ghost'
2556
delta = [(None, 'newdir', 'newdir-id', entry)]
2557
serializer = inventory_delta.InventoryDeltaSerializer(
2558
versioned_root=True, tree_references=False)
2559
lines = serializer.delta_to_lines('rev1', 'rev2', delta)
2560
yield versionedfile.ChunkedContentFactory(
2561
('rev2',), (('rev1',)), None, lines)
2563
lines = serializer.delta_to_lines('rev1', 'rev3', delta)
2564
yield versionedfile.ChunkedContentFactory(
2565
('rev3',), (('rev1',)), None, lines)
2566
return stream_with_inv_delta()
2569
class TestRepositoryInsertStream_1_19(TestRepositoryInsertStreamBase):
2571
def test_unlocked_repo(self):
2572
transport_path = 'quack'
2573
repo, client = self.setup_fake_client_and_repository(transport_path)
2574
client.add_expected_call(
2575
'Repository.insert_stream_1.19', ('quack/', ''),
2577
client.add_expected_call(
2578
'Repository.insert_stream_1.19', ('quack/', ''),
2580
self.checkInsertEmptyStream(repo, client)
2582
def test_locked_repo_with_no_lock_token(self):
2583
transport_path = 'quack'
2584
repo, client = self.setup_fake_client_and_repository(transport_path)
2585
client.add_expected_call(
2586
'Repository.lock_write', ('quack/', ''),
2587
'success', ('ok', ''))
2588
client.add_expected_call(
2589
'Repository.insert_stream_1.19', ('quack/', ''),
2591
client.add_expected_call(
2592
'Repository.insert_stream_1.19', ('quack/', ''),
2595
self.checkInsertEmptyStream(repo, client)
2597
def test_locked_repo_with_lock_token(self):
2598
transport_path = 'quack'
2599
repo, client = self.setup_fake_client_and_repository(transport_path)
2600
client.add_expected_call(
2601
'Repository.lock_write', ('quack/', ''),
2602
'success', ('ok', 'a token'))
2603
client.add_expected_call(
2604
'Repository.insert_stream_1.19', ('quack/', '', 'a token'),
2606
client.add_expected_call(
2607
'Repository.insert_stream_1.19', ('quack/', '', 'a token'),
2610
self.checkInsertEmptyStream(repo, client)
2613
1182
class TestRepositoryTarball(TestRemoteRepository):
2615
1184
# This is a canned tarball reponse we can validate against
2664
1233
src_repo.copy_content_into(dest_repo)
2667
class _StubRealPackRepository(object):
2669
def __init__(self, calls):
2671
self._pack_collection = _StubPackCollection(calls)
2673
def is_in_write_group(self):
2676
def refresh_data(self):
2677
self.calls.append(('pack collection reload_pack_names',))
2680
class _StubPackCollection(object):
2682
def __init__(self, calls):
2686
self.calls.append(('pack collection autopack',))
2689
class TestRemotePackRepositoryAutoPack(TestRemoteRepository):
2690
"""Tests for RemoteRepository.autopack implementation."""
2693
"""When the server returns 'ok' and there's no _real_repository, then
2694
nothing else happens: the autopack method is done.
2696
transport_path = 'quack'
2697
repo, client = self.setup_fake_client_and_repository(transport_path)
2698
client.add_expected_call(
2699
'PackRepository.autopack', ('quack/',), 'success', ('ok',))
2701
self.assertFinished(client)
2703
def test_ok_with_real_repo(self):
2704
"""When the server returns 'ok' and there is a _real_repository, then
2705
the _real_repository's reload_pack_name's method will be called.
2707
transport_path = 'quack'
2708
repo, client = self.setup_fake_client_and_repository(transport_path)
2709
client.add_expected_call(
2710
'PackRepository.autopack', ('quack/',),
2712
repo._real_repository = _StubRealPackRepository(client._calls)
2715
[('call', 'PackRepository.autopack', ('quack/',)),
2716
('pack collection reload_pack_names',)],
2719
def test_backwards_compatibility(self):
2720
"""If the server does not recognise the PackRepository.autopack verb,
2721
fallback to the real_repository's implementation.
2723
transport_path = 'quack'
2724
repo, client = self.setup_fake_client_and_repository(transport_path)
2725
client.add_unknown_method_response('PackRepository.autopack')
2726
def stub_ensure_real():
2727
client._calls.append(('_ensure_real',))
2728
repo._real_repository = _StubRealPackRepository(client._calls)
2729
repo._ensure_real = stub_ensure_real
2732
[('call', 'PackRepository.autopack', ('quack/',)),
2734
('pack collection autopack',)],
2738
1236
class TestErrorTranslationBase(tests.TestCaseWithMemoryTransport):
2739
1237
"""Base class for unit tests for bzrlib.remote._translate_error."""
2845
1334
expected_error = errors.DivergedBranches(branch, other_branch)
2846
1335
self.assertEqual(expected_error, translated_error)
2848
def test_ReadError_no_args(self):
2850
translated_error = self.translateTuple(('ReadError',), path=path)
2851
expected_error = errors.ReadError(path)
2852
self.assertEqual(expected_error, translated_error)
2854
def test_ReadError(self):
2856
translated_error = self.translateTuple(('ReadError', path))
2857
expected_error = errors.ReadError(path)
2858
self.assertEqual(expected_error, translated_error)
2860
def test_IncompatibleRepositories(self):
2861
translated_error = self.translateTuple(('IncompatibleRepositories',
2862
"repo1", "repo2", "details here"))
2863
expected_error = errors.IncompatibleRepositories("repo1", "repo2",
2865
self.assertEqual(expected_error, translated_error)
2867
def test_PermissionDenied_no_args(self):
2869
translated_error = self.translateTuple(('PermissionDenied',), path=path)
2870
expected_error = errors.PermissionDenied(path)
2871
self.assertEqual(expected_error, translated_error)
2873
def test_PermissionDenied_one_arg(self):
2875
translated_error = self.translateTuple(('PermissionDenied', path))
2876
expected_error = errors.PermissionDenied(path)
2877
self.assertEqual(expected_error, translated_error)
2879
def test_PermissionDenied_one_arg_and_context(self):
2880
"""Given a choice between a path from the local context and a path on
2881
the wire, _translate_error prefers the path from the local context.
2883
local_path = 'local path'
2884
remote_path = 'remote path'
2885
translated_error = self.translateTuple(
2886
('PermissionDenied', remote_path), path=local_path)
2887
expected_error = errors.PermissionDenied(local_path)
2888
self.assertEqual(expected_error, translated_error)
2890
def test_PermissionDenied_two_args(self):
2892
extra = 'a string with extra info'
2893
translated_error = self.translateTuple(
2894
('PermissionDenied', path, extra))
2895
expected_error = errors.PermissionDenied(path, extra)
2896
self.assertEqual(expected_error, translated_error)
2899
1338
class TestErrorTranslationRobustness(TestErrorTranslationBase):
2900
1339
"""Unit tests for bzrlib.remote._translate_error's robustness.
2902
1341
TestErrorTranslationSuccess is for cases where _translate_error can
2903
1342
translate successfully. This class about how _translate_err behaves when
2904
1343
it fails to translate: it re-raises the original error.
2930
1369
# In addition to re-raising ErrorFromSmartServer, some debug info has
2931
1370
# been muttered to the log file for developer to look at.
2932
1371
self.assertContainsRe(
1372
self._get_log(keep_log_file=True),
2934
1373
"Missing key 'branch' in context")
2936
def test_path_missing(self):
2937
"""Some translations (PermissionDenied, ReadError) can determine the
2938
'path' variable from either the wire or the local context. If neither
2939
has it, then an error is raised.
2941
error_tuple = ('ReadError',)
2942
server_error = errors.ErrorFromSmartServer(error_tuple)
2943
translated_error = self.translateErrorFromSmartServer(server_error)
2944
self.assertEqual(server_error, translated_error)
2945
# In addition to re-raising ErrorFromSmartServer, some debug info has
2946
# been muttered to the log file for developer to look at.
2947
self.assertContainsRe(self.get_log(), "Missing key 'path' in context")
2950
class TestStacking(tests.TestCaseWithTransport):
2951
"""Tests for operations on stacked remote repositories.
2953
The underlying format type must support stacking.
2956
def test_access_stacked_remote(self):
2957
# based on <http://launchpad.net/bugs/261315>
2958
# make a branch stacked on another repository containing an empty
2959
# revision, then open it over hpss - we should be able to see that
2961
base_transport = self.get_transport()
2962
base_builder = self.make_branch_builder('base', format='1.9')
2963
base_builder.start_series()
2964
base_revid = base_builder.build_snapshot('rev-id', None,
2965
[('add', ('', None, 'directory', None))],
2967
base_builder.finish_series()
2968
stacked_branch = self.make_branch('stacked', format='1.9')
2969
stacked_branch.set_stacked_on_url('../base')
2970
# start a server looking at this
2971
smart_server = test_server.SmartTCPServer_for_testing()
2972
self.start_server(smart_server)
2973
remote_bzrdir = BzrDir.open(smart_server.get_url() + '/stacked')
2974
# can get its branch and repository
2975
remote_branch = remote_bzrdir.open_branch()
2976
remote_repo = remote_branch.repository
2977
remote_repo.lock_read()
2979
# it should have an appropriate fallback repository, which should also
2980
# be a RemoteRepository
2981
self.assertLength(1, remote_repo._fallback_repositories)
2982
self.assertIsInstance(remote_repo._fallback_repositories[0],
2984
# and it has the revision committed to the underlying repository;
2985
# these have varying implementations so we try several of them
2986
self.assertTrue(remote_repo.has_revisions([base_revid]))
2987
self.assertTrue(remote_repo.has_revision(base_revid))
2988
self.assertEqual(remote_repo.get_revision(base_revid).message,
2991
remote_repo.unlock()
2993
def prepare_stacked_remote_branch(self):
2994
"""Get stacked_upon and stacked branches with content in each."""
2995
self.setup_smart_server_with_call_log()
2996
tree1 = self.make_branch_and_tree('tree1', format='1.9')
2997
tree1.commit('rev1', rev_id='rev1')
2998
tree2 = tree1.branch.bzrdir.sprout('tree2', stacked=True
2999
).open_workingtree()
3000
local_tree = tree2.branch.create_checkout('local')
3001
local_tree.commit('local changes make me feel good.')
3002
branch2 = Branch.open(self.get_url('tree2'))
3004
self.addCleanup(branch2.unlock)
3005
return tree1.branch, branch2
3007
def test_stacked_get_parent_map(self):
3008
# the public implementation of get_parent_map obeys stacking
3009
_, branch = self.prepare_stacked_remote_branch()
3010
repo = branch.repository
3011
self.assertEqual(['rev1'], repo.get_parent_map(['rev1']).keys())
3013
def test_unstacked_get_parent_map(self):
3014
# _unstacked_provider.get_parent_map ignores stacking
3015
_, branch = self.prepare_stacked_remote_branch()
3016
provider = branch.repository._unstacked_provider
3017
self.assertEqual([], provider.get_parent_map(['rev1']).keys())
3019
def fetch_stream_to_rev_order(self, stream):
3021
for kind, substream in stream:
3022
if not kind == 'revisions':
3025
for content in substream:
3026
result.append(content.key[-1])
3029
def get_ordered_revs(self, format, order, branch_factory=None):
3030
"""Get a list of the revisions in a stream to format format.
3032
:param format: The format of the target.
3033
:param order: the order that target should have requested.
3034
:param branch_factory: A callable to create a trunk and stacked branch
3035
to fetch from. If none, self.prepare_stacked_remote_branch is used.
3036
:result: The revision ids in the stream, in the order seen,
3037
the topological order of revisions in the source.
3039
unordered_format = bzrdir.format_registry.get(format)()
3040
target_repository_format = unordered_format.repository_format
3042
self.assertEqual(order, target_repository_format._fetch_order)
3043
if branch_factory is None:
3044
branch_factory = self.prepare_stacked_remote_branch
3045
_, stacked = branch_factory()
3046
source = stacked.repository._get_source(target_repository_format)
3047
tip = stacked.last_revision()
3048
revs = stacked.repository.get_ancestry(tip)
3049
search = graph.PendingAncestryResult([tip], stacked.repository)
3050
self.reset_smart_call_log()
3051
stream = source.get_stream(search)
3054
# We trust that if a revision is in the stream the rest of the new
3055
# content for it is too, as per our main fetch tests; here we are
3056
# checking that the revisions are actually included at all, and their
3058
return self.fetch_stream_to_rev_order(stream), revs
3060
def test_stacked_get_stream_unordered(self):
3061
# Repository._get_source.get_stream() from a stacked repository with
3062
# unordered yields the full data from both stacked and stacked upon
3064
rev_ord, expected_revs = self.get_ordered_revs('1.9', 'unordered')
3065
self.assertEqual(set(expected_revs), set(rev_ord))
3066
# Getting unordered results should have made a streaming data request
3067
# from the server, then one from the backing branch.
3068
self.assertLength(2, self.hpss_calls)
3070
def test_stacked_on_stacked_get_stream_unordered(self):
3071
# Repository._get_source.get_stream() from a stacked repository which
3072
# is itself stacked yields the full data from all three sources.
3073
def make_stacked_stacked():
3074
_, stacked = self.prepare_stacked_remote_branch()
3075
tree = stacked.bzrdir.sprout('tree3', stacked=True
3076
).open_workingtree()
3077
local_tree = tree.branch.create_checkout('local-tree3')
3078
local_tree.commit('more local changes are better')
3079
branch = Branch.open(self.get_url('tree3'))
3081
self.addCleanup(branch.unlock)
3083
rev_ord, expected_revs = self.get_ordered_revs('1.9', 'unordered',
3084
branch_factory=make_stacked_stacked)
3085
self.assertEqual(set(expected_revs), set(rev_ord))
3086
# Getting unordered results should have made a streaming data request
3087
# from the server, and one from each backing repo
3088
self.assertLength(3, self.hpss_calls)
3090
def test_stacked_get_stream_topological(self):
3091
# Repository._get_source.get_stream() from a stacked repository with
3092
# topological sorting yields the full data from both stacked and
3093
# stacked upon sources in topological order.
3094
rev_ord, expected_revs = self.get_ordered_revs('knit', 'topological')
3095
self.assertEqual(expected_revs, rev_ord)
3096
# Getting topological sort requires VFS calls still - one of which is
3097
# pushing up from the bound branch.
3098
self.assertLength(13, self.hpss_calls)
3100
def test_stacked_get_stream_groupcompress(self):
3101
# Repository._get_source.get_stream() from a stacked repository with
3102
# groupcompress sorting yields the full data from both stacked and
3103
# stacked upon sources in groupcompress order.
3104
raise tests.TestSkipped('No groupcompress ordered format available')
3105
rev_ord, expected_revs = self.get_ordered_revs('dev5', 'groupcompress')
3106
self.assertEqual(expected_revs, reversed(rev_ord))
3107
# Getting unordered results should have made a streaming data request
3108
# from the backing branch, and one from the stacked on branch.
3109
self.assertLength(2, self.hpss_calls)
3111
def test_stacked_pull_more_than_stacking_has_bug_360791(self):
3112
# When pulling some fixed amount of content that is more than the
3113
# source has (because some is coming from a fallback branch, no error
3114
# should be received. This was reported as bug 360791.
3115
# Need three branches: a trunk, a stacked branch, and a preexisting
3116
# branch pulling content from stacked and trunk.
3117
self.setup_smart_server_with_call_log()
3118
trunk = self.make_branch_and_tree('trunk', format="1.9-rich-root")
3119
r1 = trunk.commit('start')
3120
stacked_branch = trunk.branch.create_clone_on_transport(
3121
self.get_transport('stacked'), stacked_on=trunk.branch.base)
3122
local = self.make_branch('local', format='1.9-rich-root')
3123
local.repository.fetch(stacked_branch.repository,
3124
stacked_branch.last_revision())
3127
class TestRemoteBranchEffort(tests.TestCaseWithTransport):
3130
super(TestRemoteBranchEffort, self).setUp()
3131
# Create a smart server that publishes whatever the backing VFS server
3133
self.smart_server = test_server.SmartTCPServer_for_testing()
3134
self.start_server(self.smart_server, self.get_server())
3135
# Log all HPSS calls into self.hpss_calls.
3136
_SmartClient.hooks.install_named_hook(
3137
'call', self.capture_hpss_call, None)
3138
self.hpss_calls = []
3140
def capture_hpss_call(self, params):
3141
self.hpss_calls.append(params.method)
3143
def test_copy_content_into_avoids_revision_history(self):
3144
local = self.make_branch('local')
3145
remote_backing_tree = self.make_branch_and_tree('remote')
3146
remote_backing_tree.commit("Commit.")
3147
remote_branch_url = self.smart_server.get_url() + 'remote'
3148
remote_branch = bzrdir.BzrDir.open(remote_branch_url).open_branch()
3149
local.repository.fetch(remote_branch.repository)
3150
self.hpss_calls = []
3151
remote_branch.copy_content_into(local)
3152
self.assertFalse('Branch.revision_history' in self.hpss_calls)