57
53
RemoteRepositoryFormat,
59
from bzrlib.repofmt import groupcompress_repo, knitpack_repo
55
from bzrlib.repofmt import groupcompress_repo, pack_repo
60
56
from bzrlib.revision import NULL_REVISION
61
from bzrlib.smart import medium, request
57
from bzrlib.smart import server, medium
62
58
from bzrlib.smart.client import _SmartClient
63
from bzrlib.smart.repository import (
64
SmartServerRepositoryGetParentMap,
65
SmartServerRepositoryGetStream_1_19,
59
from bzrlib.smart.repository import SmartServerRepositoryGetParentMap
67
60
from bzrlib.tests import (
62
split_suite_by_condition,
70
from bzrlib.tests.scenarios import load_tests_apply_scenarios
65
from bzrlib.transport import get_transport
71
66
from bzrlib.transport.memory import MemoryTransport
72
67
from bzrlib.transport.remote import (
74
69
RemoteSSHTransport,
75
70
RemoteTCPTransport,
79
load_tests = load_tests_apply_scenarios
82
class BasicRemoteObjectTests(tests.TestCaseWithTransport):
73
def load_tests(standard_tests, module, loader):
74
to_adapt, result = split_suite_by_condition(
75
standard_tests, condition_isinstance(BasicRemoteObjectTests))
76
smart_server_version_scenarios = [
86
{'transport_server': test_server.SmartTCPServer_for_testing_v2_only}),
78
{'transport_server': server.SmartTCPServer_for_testing_v2_only}),
88
{'transport_server': test_server.SmartTCPServer_for_testing})]
80
{'transport_server': server.SmartTCPServer_for_testing})]
81
return multiply_tests(to_adapt, smart_server_version_scenarios, result)
84
class BasicRemoteObjectTests(tests.TestCaseWithTransport):
92
87
super(BasicRemoteObjectTests, self).setUp()
93
88
self.transport = self.get_transport()
94
89
# make a branch that can be opened over the smart transport
95
90
self.local_wt = BzrDir.create_standalone_workingtree('.')
96
self.addCleanup(self.transport.disconnect)
93
self.transport.disconnect()
94
tests.TestCaseWithTransport.tearDown(self)
98
96
def test_create_remote_bzrdir(self):
99
b = remote.RemoteBzrDir(self.transport, RemoteBzrDirFormat())
97
b = remote.RemoteBzrDir(self.transport, remote.RemoteBzrDirFormat())
100
98
self.assertIsInstance(b, BzrDir)
102
100
def test_open_remote_branch(self):
103
101
# open a standalone branch in the working directory
104
b = remote.RemoteBzrDir(self.transport, RemoteBzrDirFormat())
102
b = remote.RemoteBzrDir(self.transport, remote.RemoteBzrDirFormat())
105
103
branch = b.open_branch()
106
104
self.assertIsInstance(branch, Branch)
496
487
client.add_expected_call(
497
488
'BzrDir.open_2.1', ('quack/',), 'success', ('no',))
498
489
self.assertRaises(errors.NotBranchError, RemoteBzrDir, transport,
499
RemoteBzrDirFormat(), _client=client, _force_probe=True)
490
remote.RemoteBzrDirFormat(), _client=client, _force_probe=True)
500
491
self.assertFinished(client)
502
493
def test_present_without_workingtree(self):
503
494
client, transport = self.make_fake_client_and_transport()
504
495
client.add_expected_call(
505
496
'BzrDir.open_2.1', ('quack/',), 'success', ('yes', 'no'))
506
bd = RemoteBzrDir(transport, RemoteBzrDirFormat(),
497
bd = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
507
498
_client=client, _force_probe=True)
508
499
self.assertIsInstance(bd, RemoteBzrDir)
509
500
self.assertFalse(bd.has_workingtree())
527
518
'BzrDir.open_2.1', ('quack/',), 'unknown', ('BzrDir.open_2.1',))
528
519
client.add_expected_call(
529
520
'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(),
521
bd = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
553
522
_client=client, _force_probe=True)
554
523
self.assertIsInstance(bd, RemoteBzrDir)
555
524
self.assertFinished(client)
599
568
transport = transport.clone('quack')
600
569
client = FakeClient(transport.base)
601
570
client.add_error_response('nobranch')
602
bzrdir = RemoteBzrDir(transport, RemoteBzrDirFormat(),
571
bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
604
573
self.assertRaises(errors.NotBranchError, bzrdir.open_branch)
605
574
self.assertEqual(
606
[('call', 'BzrDir.open_branchV3', ('quack/',))],
575
[('call', 'BzrDir.open_branchV2', ('quack/',))],
609
578
def test__get_tree_branch(self):
610
579
# _get_tree_branch is a form of open_branch, but it should only ask for
611
580
# branch opening, not any other network requests.
613
def open_branch(name=None):
614
583
calls.append("Called")
615
584
return "a-branch"
616
585
transport = MemoryTransport()
617
586
# no requests on the network - catches other api calls being made.
618
587
client = FakeClient(transport.base)
619
bzrdir = RemoteBzrDir(transport, RemoteBzrDirFormat(),
588
bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
621
590
# patch the open_branch call to record that it was called.
622
591
bzrdir.open_branch = open_branch
725
694
format = branch._format
726
695
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())
757
698
class TestBzrDirCreateRepository(TestRemote):
1172
1113
[('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
client.add_expected_call(
1187
'Branch.get_config_file', ('quack/',),
1188
'success', ('ok',), '')
1189
transport.mkdir('quack')
1190
transport = transport.clone('quack')
1191
branch = self.make_remote_branch(transport, client)
1192
result = branch.heads_to_fetch()
1193
self.assertFinished(client)
1194
self.assertEqual((set(['rev-tip']), set()), result)
1196
def test_uses_last_revision_info_and_tags_when_set(self):
1197
transport = MemoryTransport()
1198
client = FakeClient(transport.base)
1199
client.add_expected_call(
1200
'Branch.get_stacked_on_url', ('quack/',),
1201
'error', ('NotStacked',))
1202
client.add_expected_call(
1203
'Branch.last_revision_info', ('quack/',),
1204
'success', ('ok', '1', 'rev-tip'))
1205
client.add_expected_call(
1206
'Branch.get_config_file', ('quack/',),
1207
'success', ('ok',), 'branch.fetch_tags = True')
1208
# XXX: this will break if the default format's serialization of tags
1209
# changes, or if the RPC for fetching tags changes from get_tags_bytes.
1210
client.add_expected_call(
1211
'Branch.get_tags_bytes', ('quack/',),
1212
'success', ('d5:tag-17:rev-foo5:tag-27:rev-bare',))
1213
transport.mkdir('quack')
1214
transport = transport.clone('quack')
1215
branch = self.make_remote_branch(transport, client)
1216
result = branch.heads_to_fetch()
1217
self.assertFinished(client)
1219
(set(['rev-tip']), set(['rev-foo', 'rev-bar'])), result)
1221
def test_uses_rpc_for_formats_with_non_default_heads_to_fetch(self):
1222
transport = MemoryTransport()
1223
client = FakeClient(transport.base)
1224
client.add_expected_call(
1225
'Branch.get_stacked_on_url', ('quack/',),
1226
'error', ('NotStacked',))
1227
client.add_expected_call(
1228
'Branch.heads_to_fetch', ('quack/',),
1229
'success', (['tip'], ['tagged-1', 'tagged-2']))
1230
transport.mkdir('quack')
1231
transport = transport.clone('quack')
1232
branch = self.make_remote_branch(transport, client)
1233
branch._format._use_default_local_heads_to_fetch = lambda: False
1234
result = branch.heads_to_fetch()
1235
self.assertFinished(client)
1236
self.assertEqual((set(['tip']), set(['tagged-1', 'tagged-2'])), result)
1238
def make_branch_with_tags(self):
1239
self.setup_smart_server_with_call_log()
1240
# Make a branch with a single revision.
1241
builder = self.make_branch_builder('foo')
1242
builder.start_series()
1243
builder.build_snapshot('tip', None, [
1244
('add', ('', 'root-id', 'directory', ''))])
1245
builder.finish_series()
1246
branch = builder.get_branch()
1247
# Add two tags to that branch
1248
branch.tags.set_tag('tag-1', 'rev-1')
1249
branch.tags.set_tag('tag-2', 'rev-2')
1252
def test_backwards_compatible(self):
1253
branch = self.make_branch_with_tags()
1254
c = branch.get_config()
1255
c.set_user_option('branch.fetch_tags', 'True')
1256
self.addCleanup(branch.lock_read().unlock)
1257
# Disable the heads_to_fetch verb
1258
verb = 'Branch.heads_to_fetch'
1259
self.disable_verb(verb)
1260
self.reset_smart_call_log()
1261
result = branch.heads_to_fetch()
1262
self.assertEqual((set(['tip']), set(['rev-1', 'rev-2'])), result)
1264
['Branch.last_revision_info', 'Branch.get_config_file',
1265
'Branch.get_tags_bytes'],
1266
[call.call.method for call in self.hpss_calls])
1268
def test_backwards_compatible_no_tags(self):
1269
branch = self.make_branch_with_tags()
1270
c = branch.get_config()
1271
c.set_user_option('branch.fetch_tags', 'False')
1272
self.addCleanup(branch.lock_read().unlock)
1273
# Disable the heads_to_fetch verb
1274
verb = 'Branch.heads_to_fetch'
1275
self.disable_verb(verb)
1276
self.reset_smart_call_log()
1277
result = branch.heads_to_fetch()
1278
self.assertEqual((set(['tip']), set()), result)
1280
['Branch.last_revision_info', 'Branch.get_config_file'],
1281
[call.call.method for call in self.hpss_calls])
1284
1116
class TestBranchLastRevisionInfo(RemoteBranchTestCase):
1286
1118
def test_empty_branch(self):
1386
1218
len(branch.repository._real_repository._fallback_repositories))
1388
1220
def test_get_stacked_on_real_branch(self):
1389
base_branch = self.make_branch('base')
1390
stacked_branch = self.make_branch('stacked')
1221
base_branch = self.make_branch('base', format='1.6')
1222
stacked_branch = self.make_branch('stacked', format='1.6')
1391
1223
stacked_branch.set_stacked_on_url('../base')
1392
1224
reference_format = self.get_repo_format()
1393
1225
network_name = reference_format.network_name()
1394
1226
client = FakeClient(self.get_url())
1395
1227
branch_network_name = self.get_branch_format().network_name()
1396
1228
client.add_expected_call(
1397
'BzrDir.open_branchV3', ('stacked/',),
1229
'BzrDir.open_branchV2', ('stacked/',),
1398
1230
'success', ('branch', branch_network_name))
1399
1231
client.add_expected_call(
1400
1232
'BzrDir.find_repositoryV3', ('stacked/',),
1401
'success', ('ok', '', 'yes', 'no', 'yes', network_name))
1233
'success', ('ok', '', 'no', 'no', 'yes', network_name))
1402
1234
# called twice, once from constructor and then again by us
1403
1235
client.add_expected_call(
1404
1236
'Branch.get_stacked_on_url', ('stacked/',),
1791
1622
branch.unlock()
1792
1623
self.assertFinished(client)
1794
def test_set_option_with_dict(self):
1795
client = FakeClient()
1796
client.add_expected_call(
1797
'Branch.get_stacked_on_url', ('memory:///',),
1798
'error', ('NotStacked',),)
1799
client.add_expected_call(
1800
'Branch.lock_write', ('memory:///', '', ''),
1801
'success', ('ok', 'branch token', 'repo token'))
1802
encoded_dict_value = 'd5:ascii1:a11:unicode \xe2\x8c\x9a3:\xe2\x80\xbde'
1803
client.add_expected_call(
1804
'Branch.set_config_option_dict', ('memory:///', 'branch token',
1805
'repo token', encoded_dict_value, 'foo', ''),
1807
client.add_expected_call(
1808
'Branch.unlock', ('memory:///', 'branch token', 'repo token'),
1810
transport = MemoryTransport()
1811
branch = self.make_remote_branch(transport, client)
1813
config = branch._get_config()
1815
{'ascii': 'a', u'unicode \N{WATCH}': u'\N{INTERROBANG}'},
1818
self.assertFinished(client)
1820
1625
def test_backwards_compat_set_option(self):
1821
1626
self.setup_smart_server_with_call_log()
1822
1627
branch = self.make_branch('.')
3099
2858
expected_error = errors.PermissionDenied(path, extra)
3100
2859
self.assertEqual(expected_error, translated_error)
3102
# GZ 2011-03-02: TODO test for PermissionDenied with non-ascii 'extra'
3104
def test_NoSuchFile_context_path(self):
3105
local_path = "local path"
3106
translated_error = self.translateTuple(('ReadError', "remote path"),
3108
expected_error = errors.ReadError(local_path)
3109
self.assertEqual(expected_error, translated_error)
3111
def test_NoSuchFile_without_context(self):
3112
remote_path = "remote path"
3113
translated_error = self.translateTuple(('ReadError', remote_path))
3114
expected_error = errors.ReadError(remote_path)
3115
self.assertEqual(expected_error, translated_error)
3117
def test_ReadOnlyError(self):
3118
translated_error = self.translateTuple(('ReadOnlyError',))
3119
expected_error = errors.TransportNotPossible("readonly transport")
3120
self.assertEqual(expected_error, translated_error)
3122
def test_MemoryError(self):
3123
translated_error = self.translateTuple(('MemoryError',))
3124
self.assertStartsWith(str(translated_error),
3125
"remote server out of memory")
3127
def test_generic_IndexError_no_classname(self):
3128
err = errors.ErrorFromSmartServer(('error', "list index out of range"))
3129
translated_error = self.translateErrorFromSmartServer(err)
3130
expected_error = errors.UnknownErrorFromSmartServer(err)
3131
self.assertEqual(expected_error, translated_error)
3133
# GZ 2011-03-02: TODO test generic non-ascii error string
3135
def test_generic_KeyError(self):
3136
err = errors.ErrorFromSmartServer(('error', 'KeyError', "1"))
3137
translated_error = self.translateErrorFromSmartServer(err)
3138
expected_error = errors.UnknownErrorFromSmartServer(err)
3139
self.assertEqual(expected_error, translated_error)
3142
2862
class TestErrorTranslationRobustness(TestErrorTranslationBase):
3143
2863
"""Unit tests for bzrlib.remote._translate_error's robustness.
3388
3106
def test_copy_content_into_avoids_revision_history(self):
3389
3107
local = self.make_branch('local')
3390
builder = self.make_branch_builder('remote')
3391
builder.build_commit(message="Commit.")
3108
remote_backing_tree = self.make_branch_and_tree('remote')
3109
remote_backing_tree.commit("Commit.")
3392
3110
remote_branch_url = self.smart_server.get_url() + 'remote'
3393
3111
remote_branch = bzrdir.BzrDir.open(remote_branch_url).open_branch()
3394
3112
local.repository.fetch(remote_branch.repository)
3395
3113
self.hpss_calls = []
3396
3114
remote_branch.copy_content_into(local)
3397
3115
self.assertFalse('Branch.revision_history' in self.hpss_calls)
3399
def test_fetch_everything_needs_just_one_call(self):
3400
local = self.make_branch('local')
3401
builder = self.make_branch_builder('remote')
3402
builder.build_commit(message="Commit.")
3403
remote_branch_url = self.smart_server.get_url() + 'remote'
3404
remote_branch = bzrdir.BzrDir.open(remote_branch_url).open_branch()
3405
self.hpss_calls = []
3406
local.repository.fetch(
3407
remote_branch.repository,
3408
fetch_spec=_mod_graph.EverythingResult(remote_branch.repository))
3409
self.assertEqual(['Repository.get_stream_1.19'], self.hpss_calls)
3411
def override_verb(self, verb_name, verb):
3412
request_handlers = request.request_handlers
3413
orig_verb = request_handlers.get(verb_name)
3414
request_handlers.register(verb_name, verb, override_existing=True)
3415
self.addCleanup(request_handlers.register, verb_name, orig_verb,
3416
override_existing=True)
3418
def test_fetch_everything_backwards_compat(self):
3419
"""Can fetch with EverythingResult even with pre 2.4 servers.
3421
Pre-2.4 do not support 'everything' searches with the
3422
Repository.get_stream_1.19 verb.
3425
class OldGetStreamVerb(SmartServerRepositoryGetStream_1_19):
3426
"""A version of the Repository.get_stream_1.19 verb patched to
3427
reject 'everything' searches the way 2.3 and earlier do.
3429
def recreate_search(self, repository, search_bytes,
3430
discard_excess=False):
3431
verb_log.append(search_bytes.split('\n', 1)[0])
3432
if search_bytes == 'everything':
3434
request.FailedSmartServerResponse(('BadSearch',)))
3435
return super(OldGetStreamVerb,
3436
self).recreate_search(repository, search_bytes,
3437
discard_excess=discard_excess)
3438
self.override_verb('Repository.get_stream_1.19', OldGetStreamVerb)
3439
local = self.make_branch('local')
3440
builder = self.make_branch_builder('remote')
3441
builder.build_commit(message="Commit.")
3442
remote_branch_url = self.smart_server.get_url() + 'remote'
3443
remote_branch = bzrdir.BzrDir.open(remote_branch_url).open_branch()
3444
self.hpss_calls = []
3445
local.repository.fetch(
3446
remote_branch.repository,
3447
fetch_spec=_mod_graph.EverythingResult(remote_branch.repository))
3448
# make sure the overridden verb was used
3449
self.assertLength(1, verb_log)
3450
# more than one HPSS call is needed, but because it's a VFS callback
3451
# its hard to predict exactly how many.
3452
self.assertTrue(len(self.hpss_calls) > 1)
3455
class TestUpdateBoundBranch(tests.TestCaseWithTransport):
3457
def test_bug_786980(self):
3458
self.transport_server = test_server.SmartTCPServer_for_testing
3459
wt = self.make_branch_and_tree('master')
3460
checkout = wt.branch.create_checkout('checkout')
3461
wt.commit('add stuff')
3462
last_revid = wt.commit('even more stuff')
3463
bound_location = checkout.branch.get_bound_location()
3464
# For unclear reasons some users have a bound_location without a final
3465
# '/', simulate that by forcing such a value
3466
self.assertEndsWith(bound_location, '/')
3467
new_location = bound_location.rstrip('/')
3468
checkout.branch.set_bound_location(new_location)
3469
# bug 786980 was raising ReadOnlyError: A write attempt was made in a
3470
# read only transaction during the update()
3472
self.assertEquals(last_revid, checkout.last_revision())