53
53
RemoteBranchFormat,
56
57
RemoteRepositoryFormat,
58
from bzrlib.repofmt import groupcompress_repo, pack_repo
59
from bzrlib.repofmt import groupcompress_repo, knitpack_repo
59
60
from bzrlib.revision import NULL_REVISION
60
from bzrlib.smart import medium
61
from bzrlib.smart import medium, request
61
62
from bzrlib.smart.client import _SmartClient
62
from bzrlib.smart.repository import SmartServerRepositoryGetParentMap
63
from bzrlib.smart.repository import (
64
SmartServerRepositoryGetParentMap,
65
SmartServerRepositoryGetStream_1_19,
63
67
from bzrlib.tests import (
92
96
self.addCleanup(self.transport.disconnect)
94
98
def test_create_remote_bzrdir(self):
95
b = remote.RemoteBzrDir(self.transport, remote.RemoteBzrDirFormat())
99
b = remote.RemoteBzrDir(self.transport, RemoteBzrDirFormat())
96
100
self.assertIsInstance(b, BzrDir)
98
102
def test_open_remote_branch(self):
99
103
# open a standalone branch in the working directory
100
b = remote.RemoteBzrDir(self.transport, remote.RemoteBzrDirFormat())
104
b = remote.RemoteBzrDir(self.transport, RemoteBzrDirFormat())
101
105
branch = b.open_branch()
102
106
self.assertIsInstance(branch, Branch)
466
470
client.add_expected_call(
467
471
'BzrDir.cloning_metadir', ('quack/', 'False'),
468
472
'success', (control_name, '', ('branch', ''))),
469
a_bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
473
a_bzrdir = RemoteBzrDir(transport, RemoteBzrDirFormat(),
471
475
result = a_bzrdir.cloning_metadir()
472
476
# We should have got a reference control dir with default branch and
492
496
client.add_expected_call(
493
497
'BzrDir.open_2.1', ('quack/',), 'success', ('no',))
494
498
self.assertRaises(errors.NotBranchError, RemoteBzrDir, transport,
495
remote.RemoteBzrDirFormat(), _client=client, _force_probe=True)
499
RemoteBzrDirFormat(), _client=client, _force_probe=True)
496
500
self.assertFinished(client)
498
502
def test_present_without_workingtree(self):
499
503
client, transport = self.make_fake_client_and_transport()
500
504
client.add_expected_call(
501
505
'BzrDir.open_2.1', ('quack/',), 'success', ('yes', 'no'))
502
bd = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
506
bd = RemoteBzrDir(transport, RemoteBzrDirFormat(),
503
507
_client=client, _force_probe=True)
504
508
self.assertIsInstance(bd, RemoteBzrDir)
505
509
self.assertFalse(bd.has_workingtree())
510
514
client, transport = self.make_fake_client_and_transport()
511
515
client.add_expected_call(
512
516
'BzrDir.open_2.1', ('quack/',), 'success', ('yes', 'yes'))
513
bd = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
517
bd = RemoteBzrDir(transport, RemoteBzrDirFormat(),
514
518
_client=client, _force_probe=True)
515
519
self.assertIsInstance(bd, RemoteBzrDir)
516
520
self.assertTrue(bd.has_workingtree())
523
527
'BzrDir.open_2.1', ('quack/',), 'unknown', ('BzrDir.open_2.1',))
524
528
client.add_expected_call(
525
529
'BzrDir.open', ('quack/',), 'success', ('yes',))
526
bd = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
530
bd = RemoteBzrDir(transport, RemoteBzrDirFormat(),
527
531
_client=client, _force_probe=True)
528
532
self.assertIsInstance(bd, RemoteBzrDir)
529
533
self.assertFinished(client)
545
549
'BzrDir.open_2.1', ('quack/',), 'unknown', ('BzrDir.open_2.1',))
546
550
client.add_expected_call(
547
551
'BzrDir.open', ('quack/',), 'success', ('yes',))
548
bd = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
552
bd = RemoteBzrDir(transport, RemoteBzrDirFormat(),
549
553
_client=client, _force_probe=True)
550
554
self.assertIsInstance(bd, RemoteBzrDir)
551
555
self.assertFinished(client)
1168
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])
1171
1240
class TestBranchLastRevisionInfo(RemoteBranchTestCase):
1173
1242
def test_empty_branch(self):
1336
1405
# unnecessarily invokes _ensure_real upon a call to lock_write.
1337
1406
branch._ensure_real = lambda: None
1338
1407
branch.lock_write()
1339
result = branch.set_revision_history([])
1408
result = branch._set_last_revision(NULL_REVISION)
1340
1409
branch.unlock()
1341
1410
self.assertEqual(None, result)
1342
1411
self.assertFinished(client)
1344
1413
def test_set_nonempty(self):
1345
# set_revision_history([rev-id1, ..., rev-idN]) is translated to calling
1414
# set_last_revision_info(N, rev-idN) is translated to calling
1346
1415
# Branch.set_last_revision(path, rev-idN) on the wire.
1347
1416
transport = MemoryTransport()
1348
1417
transport.mkdir('branch')
1897
1967
true_format = RemoteRepositoryFormat()
1898
1968
true_format._network_name = true_name
1899
1969
self.assertEqual(True, true_format.fast_deltas)
1900
false_name = pack_repo.RepositoryFormatKnitPack1().network_name()
1970
false_name = knitpack_repo.RepositoryFormatKnitPack1().network_name()
1901
1971
false_format = RemoteRepositoryFormat()
1902
1972
false_format._network_name = false_name
1903
1973
self.assertEqual(False, false_format.fast_deltas)
1905
1975
def test_get_format_description(self):
1906
1976
remote_repo_format = RemoteRepositoryFormat()
1907
real_format = repository.RepositoryFormat.get_default_format()
1977
real_format = repository.format_registry.get_default()
1908
1978
remote_repo_format._network_name = real_format.network_name()
1909
1979
self.assertEqual(remoted_description(real_format),
1910
1980
remote_repo_format.get_format_description())
2800
2870
('pack collection autopack',)],
2873
def test_oom_error_reporting(self):
2874
"""An out-of-memory condition on the server is reported clearly"""
2875
transport_path = 'quack'
2876
repo, client = self.setup_fake_client_and_repository(transport_path)
2877
client.add_expected_call(
2878
'PackRepository.autopack', ('quack/',),
2879
'error', ('MemoryError',))
2880
err = self.assertRaises(errors.BzrError, repo.autopack)
2881
self.assertContainsRe(str(err), "^remote server out of mem")
2804
2884
class TestErrorTranslationBase(tests.TestCaseWithMemoryTransport):
2805
2885
"""Base class for unit tests for bzrlib.remote._translate_error."""
2911
2998
expected_error = errors.DivergedBranches(branch, other_branch)
2912
2999
self.assertEqual(expected_error, translated_error)
3001
def test_NotStacked(self):
3002
branch = self.make_branch('')
3003
translated_error = self.translateTuple(('NotStacked',), branch=branch)
3004
expected_error = errors.NotStacked(branch)
3005
self.assertEqual(expected_error, translated_error)
2914
3007
def test_ReadError_no_args(self):
2915
3008
path = 'a path'
2916
3009
translated_error = self.translateTuple(('ReadError',), path=path)
2961
3055
expected_error = errors.PermissionDenied(path, extra)
2962
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)
2965
3098
class TestErrorTranslationRobustness(TestErrorTranslationBase):
2966
3099
"""Unit tests for bzrlib.remote._translate_error's robustness.
3111
3244
_, stacked = branch_factory()
3112
3245
source = stacked.repository._get_source(target_repository_format)
3113
3246
tip = stacked.last_revision()
3114
revs = stacked.repository.get_ancestry(tip)
3115
search = graph.PendingAncestryResult([tip], stacked.repository)
3247
stacked.repository._ensure_real()
3248
graph = stacked.repository.get_graph()
3249
revs = [r for (r,ps) in graph.iter_ancestry([tip])
3250
if r != NULL_REVISION]
3252
search = _mod_graph.PendingAncestryResult([tip], stacked.repository)
3116
3253
self.reset_smart_call_log()
3117
3254
stream = source.get_stream(search)
3120
3255
# We trust that if a revision is in the stream the rest of the new
3121
3256
# content for it is too, as per our main fetch tests; here we are
3122
3257
# checking that the revisions are actually included at all, and their
3209
3344
def test_copy_content_into_avoids_revision_history(self):
3210
3345
local = self.make_branch('local')
3211
remote_backing_tree = self.make_branch_and_tree('remote')
3212
remote_backing_tree.commit("Commit.")
3346
builder = self.make_branch_builder('remote')
3347
builder.build_commit(message="Commit.")
3213
3348
remote_branch_url = self.smart_server.get_url() + 'remote'
3214
3349
remote_branch = bzrdir.BzrDir.open(remote_branch_url).open_branch()
3215
3350
local.repository.fetch(remote_branch.repository)
3216
3351
self.hpss_calls = []
3217
3352
remote_branch.copy_content_into(local)
3218
3353
self.assertFalse('Branch.revision_history' in self.hpss_calls)
3355
def test_fetch_everything_needs_just_one_call(self):
3356
local = self.make_branch('local')
3357
builder = self.make_branch_builder('remote')
3358
builder.build_commit(message="Commit.")
3359
remote_branch_url = self.smart_server.get_url() + 'remote'
3360
remote_branch = bzrdir.BzrDir.open(remote_branch_url).open_branch()
3361
self.hpss_calls = []
3362
local.repository.fetch(remote_branch.repository,
3363
fetch_spec=_mod_graph.EverythingResult(remote_branch.repository))
3364
self.assertEqual(['Repository.get_stream_1.19'], self.hpss_calls)
3366
def override_verb(self, verb_name, verb):
3367
request_handlers = request.request_handlers
3368
orig_verb = request_handlers.get(verb_name)
3369
request_handlers.register(verb_name, verb, override_existing=True)
3370
self.addCleanup(request_handlers.register, verb_name, orig_verb,
3371
override_existing=True)
3373
def test_fetch_everything_backwards_compat(self):
3374
"""Can fetch with EverythingResult even with pre 2.4 servers.
3376
Pre-2.4 do not support 'everything' searches with the
3377
Repository.get_stream_1.19 verb.
3380
class OldGetStreamVerb(SmartServerRepositoryGetStream_1_19):
3381
"""A version of the Repository.get_stream_1.19 verb patched to
3382
reject 'everything' searches the way 2.3 and earlier do.
3384
def recreate_search(self, repository, search_bytes, discard_excess=False):
3385
verb_log.append(search_bytes.split('\n', 1)[0])
3386
if search_bytes == 'everything':
3387
return (None, request.FailedSmartServerResponse(('BadSearch',)))
3388
return super(OldGetStreamVerb,
3389
self).recreate_search(repository, search_bytes,
3390
discard_excess=discard_excess)
3391
self.override_verb('Repository.get_stream_1.19', OldGetStreamVerb)
3392
local = self.make_branch('local')
3393
builder = self.make_branch_builder('remote')
3394
builder.build_commit(message="Commit.")
3395
remote_branch_url = self.smart_server.get_url() + 'remote'
3396
remote_branch = bzrdir.BzrDir.open(remote_branch_url).open_branch()
3397
self.hpss_calls = []
3398
local.repository.fetch(remote_branch.repository,
3399
fetch_spec=_mod_graph.EverythingResult(remote_branch.repository))
3400
# make sure the overridden verb was used
3401
self.assertLength(1, verb_log)
3402
# more than one HPSS call is needed, but because it's a VFS callback
3403
# its hard to predict exactly how many.
3404
self.assertTrue(len(self.hpss_calls) > 1)