53
57
RemoteRepositoryFormat,
55
from bzrlib.repofmt import groupcompress_repo, pack_repo
59
from bzrlib.repofmt import groupcompress_repo, knitpack_repo
56
60
from bzrlib.revision import NULL_REVISION
57
from bzrlib.smart import server, medium
61
from bzrlib.smart import medium, request
58
62
from bzrlib.smart.client import _SmartClient
59
from bzrlib.smart.repository import SmartServerRepositoryGetParentMap
63
from bzrlib.smart.repository import (
64
SmartServerRepositoryGetParentMap,
65
SmartServerRepositoryGetStream_1_19,
60
67
from bzrlib.tests import (
62
split_suite_by_condition,
66
from bzrlib.transport import get_transport, http
70
from bzrlib.tests.scenarios import load_tests_apply_scenarios
67
71
from bzrlib.transport.memory import MemoryTransport
68
72
from bzrlib.transport.remote import (
70
74
RemoteSSHTransport,
71
75
RemoteTCPTransport,
74
def load_tests(standard_tests, module, loader):
75
to_adapt, result = split_suite_by_condition(
76
standard_tests, condition_isinstance(BasicRemoteObjectTests))
77
smart_server_version_scenarios = [
79
load_tests = load_tests_apply_scenarios
82
class BasicRemoteObjectTests(tests.TestCaseWithTransport):
79
{'transport_server': server.SmartTCPServer_for_testing_v2_only}),
86
{'transport_server': test_server.SmartTCPServer_for_testing_v2_only}),
81
{'transport_server': server.SmartTCPServer_for_testing})]
82
return multiply_tests(to_adapt, smart_server_version_scenarios, result)
85
class BasicRemoteObjectTests(tests.TestCaseWithTransport):
88
{'transport_server': test_server.SmartTCPServer_for_testing})]
88
92
super(BasicRemoteObjectTests, self).setUp()
89
93
self.transport = self.get_transport()
90
94
# make a branch that can be opened over the smart transport
91
95
self.local_wt = BzrDir.create_standalone_workingtree('.')
94
self.transport.disconnect()
95
tests.TestCaseWithTransport.tearDown(self)
96
self.addCleanup(self.transport.disconnect)
97
98
def test_create_remote_bzrdir(self):
98
b = remote.RemoteBzrDir(self.transport, remote.RemoteBzrDirFormat())
99
b = remote.RemoteBzrDir(self.transport, RemoteBzrDirFormat())
99
100
self.assertIsInstance(b, BzrDir)
101
102
def test_open_remote_branch(self):
102
103
# open a standalone branch in the working directory
103
b = remote.RemoteBzrDir(self.transport, remote.RemoteBzrDirFormat())
104
b = remote.RemoteBzrDir(self.transport, RemoteBzrDirFormat())
104
105
branch = b.open_branch()
105
106
self.assertIsInstance(branch, Branch)
468
482
self.assertFinished(client)
485
class TestBzrDirOpen(TestRemote):
487
def make_fake_client_and_transport(self, path='quack'):
488
transport = MemoryTransport()
489
transport.mkdir(path)
490
transport = transport.clone(path)
491
client = FakeClient(transport.base)
492
return client, transport
494
def test_absent(self):
495
client, transport = self.make_fake_client_and_transport()
496
client.add_expected_call(
497
'BzrDir.open_2.1', ('quack/',), 'success', ('no',))
498
self.assertRaises(errors.NotBranchError, RemoteBzrDir, transport,
499
RemoteBzrDirFormat(), _client=client, _force_probe=True)
500
self.assertFinished(client)
502
def test_present_without_workingtree(self):
503
client, transport = self.make_fake_client_and_transport()
504
client.add_expected_call(
505
'BzrDir.open_2.1', ('quack/',), 'success', ('yes', 'no'))
506
bd = RemoteBzrDir(transport, RemoteBzrDirFormat(),
507
_client=client, _force_probe=True)
508
self.assertIsInstance(bd, RemoteBzrDir)
509
self.assertFalse(bd.has_workingtree())
510
self.assertRaises(errors.NoWorkingTree, bd.open_workingtree)
511
self.assertFinished(client)
513
def test_present_with_workingtree(self):
514
client, transport = self.make_fake_client_and_transport()
515
client.add_expected_call(
516
'BzrDir.open_2.1', ('quack/',), 'success', ('yes', 'yes'))
517
bd = RemoteBzrDir(transport, RemoteBzrDirFormat(),
518
_client=client, _force_probe=True)
519
self.assertIsInstance(bd, RemoteBzrDir)
520
self.assertTrue(bd.has_workingtree())
521
self.assertRaises(errors.NotLocalUrl, bd.open_workingtree)
522
self.assertFinished(client)
524
def test_backwards_compat(self):
525
client, transport = self.make_fake_client_and_transport()
526
client.add_expected_call(
527
'BzrDir.open_2.1', ('quack/',), 'unknown', ('BzrDir.open_2.1',))
528
client.add_expected_call(
529
'BzrDir.open', ('quack/',), 'success', ('yes',))
530
bd = RemoteBzrDir(transport, RemoteBzrDirFormat(),
531
_client=client, _force_probe=True)
532
self.assertIsInstance(bd, RemoteBzrDir)
533
self.assertFinished(client)
535
def test_backwards_compat_hpss_v2(self):
536
client, transport = self.make_fake_client_and_transport()
537
# Monkey-patch fake client to simulate real-world behaviour with v2
538
# server: upon first RPC call detect the protocol version, and because
539
# the version is 2 also do _remember_remote_is_before((1, 6)) before
540
# continuing with the RPC.
541
orig_check_call = client._check_call
542
def check_call(method, args):
543
client._medium._protocol_version = 2
544
client._medium._remember_remote_is_before((1, 6))
545
client._check_call = orig_check_call
546
client._check_call(method, args)
547
client._check_call = check_call
548
client.add_expected_call(
549
'BzrDir.open_2.1', ('quack/',), 'unknown', ('BzrDir.open_2.1',))
550
client.add_expected_call(
551
'BzrDir.open', ('quack/',), 'success', ('yes',))
552
bd = RemoteBzrDir(transport, RemoteBzrDirFormat(),
553
_client=client, _force_probe=True)
554
self.assertIsInstance(bd, RemoteBzrDir)
555
self.assertFinished(client)
471
558
class TestBzrDirOpenBranch(TestRemote):
473
560
def test_backwards_compat(self):
512
599
transport = transport.clone('quack')
513
600
client = FakeClient(transport.base)
514
601
client.add_error_response('nobranch')
515
bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
602
bzrdir = RemoteBzrDir(transport, RemoteBzrDirFormat(),
517
604
self.assertRaises(errors.NotBranchError, bzrdir.open_branch)
518
605
self.assertEqual(
519
[('call', 'BzrDir.open_branchV2', ('quack/',))],
606
[('call', 'BzrDir.open_branchV3', ('quack/',))],
522
609
def test__get_tree_branch(self):
523
610
# _get_tree_branch is a form of open_branch, but it should only ask for
524
611
# branch opening, not any other network requests.
613
def open_branch(name=None):
527
614
calls.append("Called")
528
615
return "a-branch"
529
616
transport = MemoryTransport()
530
617
# no requests on the network - catches other api calls being made.
531
618
client = FakeClient(transport.base)
532
bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
619
bzrdir = RemoteBzrDir(transport, RemoteBzrDirFormat(),
534
621
# patch the open_branch call to record that it was called.
535
622
bzrdir.open_branch = open_branch
638
725
format = branch._format
639
726
self.assertEqual(network_name, format.network_name())
728
def test_already_open_repo_and_reused_medium(self):
729
"""Bug 726584: create_branch(..., repository=repo) should work
730
regardless of what the smart medium's base URL is.
732
self.transport_server = test_server.SmartTCPServer_for_testing
733
transport = self.get_transport('.')
734
repo = self.make_repository('quack')
735
# Client's medium rooted a transport root (not at the bzrdir)
736
client = FakeClient(transport.base)
737
transport = transport.clone('quack')
738
reference_bzrdir_format = bzrdir.format_registry.get('default')()
739
reference_format = reference_bzrdir_format.get_branch_format()
740
network_name = reference_format.network_name()
741
reference_repo_fmt = reference_bzrdir_format.repository_format
742
reference_repo_name = reference_repo_fmt.network_name()
743
client.add_expected_call(
744
'BzrDir.create_branch', ('extra/quack/', network_name),
745
'success', ('ok', network_name, '', 'no', 'no', 'yes',
746
reference_repo_name))
747
a_bzrdir = RemoteBzrDir(transport, RemoteBzrDirFormat(),
749
branch = a_bzrdir.create_branch(repository=repo)
750
# We should have got a remote branch
751
self.assertIsInstance(branch, remote.RemoteBranch)
752
# its format should have the settings from the response
753
format = branch._format
754
self.assertEqual(network_name, format.network_name())
642
757
class TestBzrDirCreateRepository(TestRemote):
995
1124
self.assertEqual({}, result)
1127
class TestBranchSetTagsBytes(RemoteBranchTestCase):
1129
def test_trivial(self):
1130
transport = MemoryTransport()
1131
client = FakeClient(transport.base)
1132
client.add_expected_call(
1133
'Branch.get_stacked_on_url', ('quack/',),
1134
'error', ('NotStacked',))
1135
client.add_expected_call(
1136
'Branch.set_tags_bytes', ('quack/', 'branch token', 'repo token'),
1138
transport.mkdir('quack')
1139
transport = transport.clone('quack')
1140
branch = self.make_remote_branch(transport, client)
1141
self.lock_remote_branch(branch)
1142
branch._set_tags_bytes('tags bytes')
1143
self.assertFinished(client)
1144
self.assertEqual('tags bytes', client._calls[-1][-1])
1146
def test_backwards_compatible(self):
1147
transport = MemoryTransport()
1148
client = FakeClient(transport.base)
1149
client.add_expected_call(
1150
'Branch.get_stacked_on_url', ('quack/',),
1151
'error', ('NotStacked',))
1152
client.add_expected_call(
1153
'Branch.set_tags_bytes', ('quack/', 'branch token', 'repo token'),
1154
'unknown', ('Branch.set_tags_bytes',))
1155
transport.mkdir('quack')
1156
transport = transport.clone('quack')
1157
branch = self.make_remote_branch(transport, client)
1158
self.lock_remote_branch(branch)
1159
class StubRealBranch(object):
1162
def _set_tags_bytes(self, bytes):
1163
self.calls.append(('set_tags_bytes', bytes))
1164
real_branch = StubRealBranch()
1165
branch._real_branch = real_branch
1166
branch._set_tags_bytes('tags bytes')
1167
# Call a second time, to exercise the 'remote version already inferred'
1169
branch._set_tags_bytes('tags bytes')
1170
self.assertFinished(client)
1172
[('set_tags_bytes', 'tags bytes')] * 2, real_branch.calls)
1175
class TestBranchHeadsToFetch(RemoteBranchTestCase):
1177
def test_uses_last_revision_info_and_tags_by_default(self):
1178
transport = MemoryTransport()
1179
client = FakeClient(transport.base)
1180
client.add_expected_call(
1181
'Branch.get_stacked_on_url', ('quack/',),
1182
'error', ('NotStacked',))
1183
client.add_expected_call(
1184
'Branch.last_revision_info', ('quack/',),
1185
'success', ('ok', '1', 'rev-tip'))
1186
# XXX: this will break if the default format's serialization of tags
1187
# changes, or if the RPC for fetching tags changes from get_tags_bytes.
1188
client.add_expected_call(
1189
'Branch.get_tags_bytes', ('quack/',),
1190
'success', ('d5:tag-17:rev-foo5:tag-27:rev-bare',))
1191
transport.mkdir('quack')
1192
transport = transport.clone('quack')
1193
branch = self.make_remote_branch(transport, client)
1194
result = branch.heads_to_fetch()
1195
self.assertFinished(client)
1197
(set(['rev-tip']), set(['rev-foo', 'rev-bar'])), result)
1199
def test_uses_rpc_for_formats_with_non_default_heads_to_fetch(self):
1200
transport = MemoryTransport()
1201
client = FakeClient(transport.base)
1202
client.add_expected_call(
1203
'Branch.get_stacked_on_url', ('quack/',),
1204
'error', ('NotStacked',))
1205
client.add_expected_call(
1206
'Branch.heads_to_fetch', ('quack/',),
1207
'success', (['tip'], ['tagged-1', 'tagged-2']))
1208
transport.mkdir('quack')
1209
transport = transport.clone('quack')
1210
branch = self.make_remote_branch(transport, client)
1211
branch._format._use_default_local_heads_to_fetch = lambda: False
1212
result = branch.heads_to_fetch()
1213
self.assertFinished(client)
1214
self.assertEqual((set(['tip']), set(['tagged-1', 'tagged-2'])), result)
1216
def test_backwards_compatible(self):
1217
self.setup_smart_server_with_call_log()
1218
# Make a branch with a single revision.
1219
builder = self.make_branch_builder('foo')
1220
builder.start_series()
1221
builder.build_snapshot('tip', None, [
1222
('add', ('', 'root-id', 'directory', ''))])
1223
builder.finish_series()
1224
branch = builder.get_branch()
1225
# Add two tags to that branch
1226
branch.tags.set_tag('tag-1', 'rev-1')
1227
branch.tags.set_tag('tag-2', 'rev-2')
1228
self.addCleanup(branch.lock_read().unlock)
1229
# Disable the heads_to_fetch verb
1230
verb = 'Branch.heads_to_fetch'
1231
self.disable_verb(verb)
1232
self.reset_smart_call_log()
1233
result = branch.heads_to_fetch()
1234
self.assertEqual((set(['tip']), set(['rev-1', 'rev-2'])), result)
1236
['Branch.last_revision_info', 'Branch.get_tags_bytes'],
1237
[call.call.method for call in self.hpss_calls])
998
1240
class TestBranchLastRevisionInfo(RemoteBranchTestCase):
1000
1242
def test_empty_branch(self):
1100
1342
len(branch.repository._real_repository._fallback_repositories))
1102
1344
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')
1345
base_branch = self.make_branch('base')
1346
stacked_branch = self.make_branch('stacked')
1105
1347
stacked_branch.set_stacked_on_url('../base')
1106
1348
reference_format = self.get_repo_format()
1107
1349
network_name = reference_format.network_name()
1108
1350
client = FakeClient(self.get_url())
1109
1351
branch_network_name = self.get_branch_format().network_name()
1110
1352
client.add_expected_call(
1111
'BzrDir.open_branchV2', ('stacked/',),
1353
'BzrDir.open_branchV3', ('stacked/',),
1112
1354
'success', ('branch', branch_network_name))
1113
1355
client.add_expected_call(
1114
1356
'BzrDir.find_repositoryV3', ('stacked/',),
1115
'success', ('ok', '', 'no', 'no', 'yes', network_name))
1357
'success', ('ok', '', 'yes', 'no', 'yes', network_name))
1116
1358
# called twice, once from constructor and then again by us
1117
1359
client.add_expected_call(
1118
1360
'Branch.get_stacked_on_url', ('stacked/',),
1514
1747
branch.unlock()
1515
1748
self.assertFinished(client)
1750
def test_set_option_with_dict(self):
1751
client = FakeClient()
1752
client.add_expected_call(
1753
'Branch.get_stacked_on_url', ('memory:///',),
1754
'error', ('NotStacked',),)
1755
client.add_expected_call(
1756
'Branch.lock_write', ('memory:///', '', ''),
1757
'success', ('ok', 'branch token', 'repo token'))
1758
encoded_dict_value = 'd5:ascii1:a11:unicode \xe2\x8c\x9a3:\xe2\x80\xbde'
1759
client.add_expected_call(
1760
'Branch.set_config_option_dict', ('memory:///', 'branch token',
1761
'repo token', encoded_dict_value, 'foo', ''),
1763
client.add_expected_call(
1764
'Branch.unlock', ('memory:///', 'branch token', 'repo token'),
1766
transport = MemoryTransport()
1767
branch = self.make_remote_branch(transport, client)
1769
config = branch._get_config()
1771
{'ascii': 'a', u'unicode \N{WATCH}': u'\N{INTERROBANG}'},
1774
self.assertFinished(client)
1517
1776
def test_backwards_compat_set_option(self):
1518
1777
self.setup_smart_server_with_call_log()
1519
1778
branch = self.make_branch('.')
1667
1940
client = FakeClient(transport.base)
1668
1941
transport = transport.clone(transport_path)
1669
1942
# we do not want bzrdir to make any remote calls
1670
bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
1943
bzrdir = RemoteBzrDir(transport, RemoteBzrDirFormat(),
1672
1945
repo = RemoteRepository(bzrdir, None, _client=client)
1673
1946
return repo, client
1949
def remoted_description(format):
1950
return 'Remote: ' + format.get_format_description()
1953
class TestBranchFormat(tests.TestCase):
1955
def test_get_format_description(self):
1956
remote_format = RemoteBranchFormat()
1957
real_format = branch.format_registry.get_default()
1958
remote_format._network_name = real_format.network_name()
1959
self.assertEqual(remoted_description(real_format),
1960
remote_format.get_format_description())
1676
1963
class TestRepositoryFormat(TestRemoteRepository):
1678
1965
def test_fast_delta(self):
1679
true_name = groupcompress_repo.RepositoryFormatCHK1().network_name()
1966
true_name = groupcompress_repo.RepositoryFormat2a().network_name()
1680
1967
true_format = RemoteRepositoryFormat()
1681
1968
true_format._network_name = true_name
1682
1969
self.assertEqual(True, true_format.fast_deltas)
1683
false_name = pack_repo.RepositoryFormatKnitPack1().network_name()
1970
false_name = knitpack_repo.RepositoryFormatKnitPack1().network_name()
1684
1971
false_format = RemoteRepositoryFormat()
1685
1972
false_format._network_name = false_name
1686
1973
self.assertEqual(False, false_format.fast_deltas)
1975
def test_get_format_description(self):
1976
remote_repo_format = RemoteRepositoryFormat()
1977
real_format = repository.format_registry.get_default()
1978
remote_repo_format._network_name = real_format.network_name()
1979
self.assertEqual(remoted_description(real_format),
1980
remote_repo_format.get_format_description())
1689
1983
class TestRepositoryGatherStats(TestRemoteRepository):
2699
3055
expected_error = errors.PermissionDenied(path, extra)
2700
3056
self.assertEqual(expected_error, translated_error)
3058
# GZ 2011-03-02: TODO test for PermissionDenied with non-ascii 'extra'
3060
def test_NoSuchFile_context_path(self):
3061
local_path = "local path"
3062
translated_error = self.translateTuple(('ReadError', "remote path"),
3064
expected_error = errors.ReadError(local_path)
3065
self.assertEqual(expected_error, translated_error)
3067
def test_NoSuchFile_without_context(self):
3068
remote_path = "remote path"
3069
translated_error = self.translateTuple(('ReadError', remote_path))
3070
expected_error = errors.ReadError(remote_path)
3071
self.assertEqual(expected_error, translated_error)
3073
def test_ReadOnlyError(self):
3074
translated_error = self.translateTuple(('ReadOnlyError',))
3075
expected_error = errors.TransportNotPossible("readonly transport")
3076
self.assertEqual(expected_error, translated_error)
3078
def test_MemoryError(self):
3079
translated_error = self.translateTuple(('MemoryError',))
3080
self.assertStartsWith(str(translated_error),
3081
"remote server out of memory")
3083
def test_generic_IndexError_no_classname(self):
3084
err = errors.ErrorFromSmartServer(('error', "list index out of range"))
3085
translated_error = self.translateErrorFromSmartServer(err)
3086
expected_error = errors.UnknownErrorFromSmartServer(err)
3087
self.assertEqual(expected_error, translated_error)
3089
# GZ 2011-03-02: TODO test generic non-ascii error string
3091
def test_generic_KeyError(self):
3092
err = errors.ErrorFromSmartServer(('error', 'KeyError', "1"))
3093
translated_error = self.translateErrorFromSmartServer(err)
3094
expected_error = errors.UnknownErrorFromSmartServer(err)
3095
self.assertEqual(expected_error, translated_error)
2703
3098
class TestErrorTranslationRobustness(TestErrorTranslationBase):
2704
3099
"""Unit tests for bzrlib.remote._translate_error's robustness.
2949
3342
def test_copy_content_into_avoids_revision_history(self):
2950
3343
local = self.make_branch('local')
2951
remote_backing_tree = self.make_branch_and_tree('remote')
2952
remote_backing_tree.commit("Commit.")
3344
builder = self.make_branch_builder('remote')
3345
builder.build_commit(message="Commit.")
2953
3346
remote_branch_url = self.smart_server.get_url() + 'remote'
2954
3347
remote_branch = bzrdir.BzrDir.open(remote_branch_url).open_branch()
2955
3348
local.repository.fetch(remote_branch.repository)
2956
3349
self.hpss_calls = []
2957
3350
remote_branch.copy_content_into(local)
2958
3351
self.assertFalse('Branch.revision_history' in self.hpss_calls)
3353
def test_fetch_everything_needs_just_one_call(self):
3354
local = self.make_branch('local')
3355
builder = self.make_branch_builder('remote')
3356
builder.build_commit(message="Commit.")
3357
remote_branch_url = self.smart_server.get_url() + 'remote'
3358
remote_branch = bzrdir.BzrDir.open(remote_branch_url).open_branch()
3359
self.hpss_calls = []
3360
local.repository.fetch(remote_branch.repository,
3361
fetch_spec=graph.EverythingResult(remote_branch.repository))
3362
self.assertEqual(['Repository.get_stream_1.19'], self.hpss_calls)
3364
def override_verb(self, verb_name, verb):
3365
request_handlers = request.request_handlers
3366
orig_verb = request_handlers.get(verb_name)
3367
request_handlers.register(verb_name, verb, override_existing=True)
3368
self.addCleanup(request_handlers.register, verb_name, orig_verb,
3369
override_existing=True)
3371
def test_fetch_everything_backwards_compat(self):
3372
"""Can fetch with EverythingResult even with pre 2.4 servers.
3374
Pre-2.4 do not support 'everything' searches with the
3375
Repository.get_stream_1.19 verb.
3378
class OldGetStreamVerb(SmartServerRepositoryGetStream_1_19):
3379
"""A version of the Repository.get_stream_1.19 verb patched to
3380
reject 'everything' searches the way 2.3 and earlier do.
3382
def recreate_search(self, repository, search_bytes, discard_excess=False):
3383
verb_log.append(search_bytes.split('\n', 1)[0])
3384
if search_bytes == 'everything':
3385
return (None, request.FailedSmartServerResponse(('BadSearch',)))
3386
return super(OldGetStreamVerb,
3387
self).recreate_search(repository, search_bytes,
3388
discard_excess=discard_excess)
3389
self.override_verb('Repository.get_stream_1.19', OldGetStreamVerb)
3390
local = self.make_branch('local')
3391
builder = self.make_branch_builder('remote')
3392
builder.build_commit(message="Commit.")
3393
remote_branch_url = self.smart_server.get_url() + 'remote'
3394
remote_branch = bzrdir.BzrDir.open(remote_branch_url).open_branch()
3395
self.hpss_calls = []
3396
local.repository.fetch(remote_branch.repository,
3397
fetch_spec=graph.EverythingResult(remote_branch.repository))
3398
# make sure the overridden verb was used
3399
self.assertLength(1, verb_log)
3400
# more than one HPSS call is needed, but because it's a VFS callback
3401
# its hard to predict exactly how many.
3402
self.assertTrue(len(self.hpss_calls) > 1)