45
45
from bzrlib.branch import Branch
46
from bzrlib.bzrdir import BzrDir, BzrDirFormat
46
from bzrlib.bzrdir import (
47
51
from bzrlib.remote import (
49
53
RemoteBranchFormat,
53
56
RemoteRepositoryFormat,
55
58
from bzrlib.repofmt import groupcompress_repo, pack_repo
56
59
from bzrlib.revision import NULL_REVISION
57
from bzrlib.smart import medium
60
from bzrlib.smart import medium, request
58
61
from bzrlib.smart.client import _SmartClient
59
from bzrlib.smart.repository import SmartServerRepositoryGetParentMap
62
from bzrlib.smart.repository import (
63
SmartServerRepositoryGetParentMap,
64
SmartServerRepositoryGetStream_1_19,
60
66
from bzrlib.tests import (
62
split_suite_by_condition,
66
from bzrlib.transport import get_transport
69
from bzrlib.tests.scenarios import load_tests_apply_scenarios
67
70
from bzrlib.transport.memory import MemoryTransport
68
71
from bzrlib.transport.remote import (
70
73
RemoteSSHTransport,
71
74
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 = [
78
load_tests = load_tests_apply_scenarios
81
class BasicRemoteObjectTests(tests.TestCaseWithTransport):
79
{'transport_server': test_server.SmartTCPServer_for_testing_v2_only}),
85
{'transport_server': test_server.SmartTCPServer_for_testing_v2_only}),
81
{'transport_server': test_server.SmartTCPServer_for_testing})]
82
return multiply_tests(to_adapt, smart_server_version_scenarios, result)
85
class BasicRemoteObjectTests(tests.TestCaseWithTransport):
87
{'transport_server': test_server.SmartTCPServer_for_testing})]
88
91
super(BasicRemoteObjectTests, self).setUp()
89
92
self.transport = self.get_transport()
90
93
# make a branch that can be opened over the smart transport
91
94
self.local_wt = BzrDir.create_standalone_workingtree('.')
94
self.transport.disconnect()
95
tests.TestCaseWithTransport.tearDown(self)
95
self.addCleanup(self.transport.disconnect)
97
97
def test_create_remote_bzrdir(self):
98
98
b = remote.RemoteBzrDir(self.transport, remote.RemoteBzrDirFormat())
724
724
format = branch._format
725
725
self.assertEqual(network_name, format.network_name())
727
def test_already_open_repo_and_reused_medium(self):
728
"""Bug 726584: create_branch(..., repository=repo) should work
729
regardless of what the smart medium's base URL is.
731
self.transport_server = test_server.SmartTCPServer_for_testing
732
transport = self.get_transport('.')
733
repo = self.make_repository('quack')
734
# Client's medium rooted a transport root (not at the bzrdir)
735
client = FakeClient(transport.base)
736
transport = transport.clone('quack')
737
reference_bzrdir_format = bzrdir.format_registry.get('default')()
738
reference_format = reference_bzrdir_format.get_branch_format()
739
network_name = reference_format.network_name()
740
reference_repo_fmt = reference_bzrdir_format.repository_format
741
reference_repo_name = reference_repo_fmt.network_name()
742
client.add_expected_call(
743
'BzrDir.create_branch', ('extra/quack/', network_name),
744
'success', ('ok', network_name, '', 'no', 'no', 'yes',
745
reference_repo_name))
746
a_bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
748
branch = a_bzrdir.create_branch(repository=repo)
749
# We should have got a remote branch
750
self.assertIsInstance(branch, remote.RemoteBranch)
751
# its format should have the settings from the response
752
format = branch._format
753
self.assertEqual(network_name, format.network_name())
728
756
class TestBzrDirCreateRepository(TestRemote):
1143
1171
[('set_tags_bytes', 'tags bytes')] * 2, real_branch.calls)
1174
class TestBranchHeadsToFetch(RemoteBranchTestCase):
1176
def test_uses_last_revision_info_and_tags_by_default(self):
1177
transport = MemoryTransport()
1178
client = FakeClient(transport.base)
1179
client.add_expected_call(
1180
'Branch.get_stacked_on_url', ('quack/',),
1181
'error', ('NotStacked',))
1182
client.add_expected_call(
1183
'Branch.last_revision_info', ('quack/',),
1184
'success', ('ok', '1', 'rev-tip'))
1185
# XXX: this will break if the default format's serialization of tags
1186
# changes, or if the RPC for fetching tags changes from get_tags_bytes.
1187
client.add_expected_call(
1188
'Branch.get_tags_bytes', ('quack/',),
1189
'success', ('d5:tag-17:rev-foo5:tag-27:rev-bare',))
1190
transport.mkdir('quack')
1191
transport = transport.clone('quack')
1192
branch = self.make_remote_branch(transport, client)
1193
result = branch.heads_to_fetch()
1194
self.assertFinished(client)
1196
(set(['rev-tip']), set(['rev-foo', 'rev-bar'])), result)
1198
def test_uses_rpc_for_formats_with_non_default_heads_to_fetch(self):
1199
transport = MemoryTransport()
1200
client = FakeClient(transport.base)
1201
client.add_expected_call(
1202
'Branch.get_stacked_on_url', ('quack/',),
1203
'error', ('NotStacked',))
1204
client.add_expected_call(
1205
'Branch.heads_to_fetch', ('quack/',),
1206
'success', (['tip'], ['tagged-1', 'tagged-2']))
1207
transport.mkdir('quack')
1208
transport = transport.clone('quack')
1209
branch = self.make_remote_branch(transport, client)
1210
branch._format._use_default_local_heads_to_fetch = lambda: False
1211
result = branch.heads_to_fetch()
1212
self.assertFinished(client)
1213
self.assertEqual((set(['tip']), set(['tagged-1', 'tagged-2'])), result)
1215
def test_backwards_compatible(self):
1216
self.setup_smart_server_with_call_log()
1217
# Make a branch with a single revision.
1218
builder = self.make_branch_builder('foo')
1219
builder.start_series()
1220
builder.build_snapshot('tip', None, [
1221
('add', ('', 'root-id', 'directory', ''))])
1222
builder.finish_series()
1223
branch = builder.get_branch()
1224
# Add two tags to that branch
1225
branch.tags.set_tag('tag-1', 'rev-1')
1226
branch.tags.set_tag('tag-2', 'rev-2')
1227
self.addCleanup(branch.lock_read().unlock)
1228
# Disable the heads_to_fetch verb
1229
verb = 'Branch.heads_to_fetch'
1230
self.disable_verb(verb)
1231
self.reset_smart_call_log()
1232
result = branch.heads_to_fetch()
1233
self.assertEqual((set(['tip']), set(['rev-1', 'rev-2'])), result)
1235
['Branch.last_revision_info', 'Branch.get_tags_bytes'],
1236
[call.call.method for call in self.hpss_calls])
1146
1239
class TestBranchLastRevisionInfo(RemoteBranchTestCase):
1148
1241
def test_empty_branch(self):
1652
1745
branch.unlock()
1653
1746
self.assertFinished(client)
1748
def test_set_option_with_dict(self):
1749
client = FakeClient()
1750
client.add_expected_call(
1751
'Branch.get_stacked_on_url', ('memory:///',),
1752
'error', ('NotStacked',),)
1753
client.add_expected_call(
1754
'Branch.lock_write', ('memory:///', '', ''),
1755
'success', ('ok', 'branch token', 'repo token'))
1756
encoded_dict_value = 'd5:ascii1:a11:unicode \xe2\x8c\x9a3:\xe2\x80\xbde'
1757
client.add_expected_call(
1758
'Branch.set_config_option_dict', ('memory:///', 'branch token',
1759
'repo token', encoded_dict_value, 'foo', ''),
1761
client.add_expected_call(
1762
'Branch.unlock', ('memory:///', 'branch token', 'repo token'),
1764
transport = MemoryTransport()
1765
branch = self.make_remote_branch(transport, client)
1767
config = branch._get_config()
1769
{'ascii': 'a', u'unicode \N{WATCH}': u'\N{INTERROBANG}'},
1772
self.assertFinished(client)
1655
1774
def test_backwards_compat_set_option(self):
1656
1775
self.setup_smart_server_with_call_log()
1657
1776
branch = self.make_branch('.')
1664
1783
self.assertLength(10, self.hpss_calls)
1665
1784
self.assertEqual('value', branch._get_config().get_option('name'))
1786
def test_backwards_compat_set_option_with_dict(self):
1787
self.setup_smart_server_with_call_log()
1788
branch = self.make_branch('.')
1789
verb = 'Branch.set_config_option_dict'
1790
self.disable_verb(verb)
1792
self.addCleanup(branch.unlock)
1793
self.reset_smart_call_log()
1794
config = branch._get_config()
1795
value_dict = {'ascii': 'a', u'unicode \N{WATCH}': u'\N{INTERROBANG}'}
1796
config.set_option(value_dict, 'name')
1797
self.assertLength(10, self.hpss_calls)
1798
self.assertEqual(value_dict, branch._get_config().get_option('name'))
1668
1801
class TestBranchLockWrite(RemoteBranchTestCase):
2734
2868
('pack collection autopack',)],
2871
def test_oom_error_reporting(self):
2872
"""An out-of-memory condition on the server is reported clearly"""
2873
transport_path = 'quack'
2874
repo, client = self.setup_fake_client_and_repository(transport_path)
2875
client.add_expected_call(
2876
'PackRepository.autopack', ('quack/',),
2877
'error', ('MemoryError',))
2878
err = self.assertRaises(errors.BzrError, repo.autopack)
2879
self.assertContainsRe(str(err), "^remote server out of mem")
2738
2882
class TestErrorTranslationBase(tests.TestCaseWithMemoryTransport):
2739
2883
"""Base class for unit tests for bzrlib.remote._translate_error."""
2845
2996
expected_error = errors.DivergedBranches(branch, other_branch)
2846
2997
self.assertEqual(expected_error, translated_error)
2999
def test_NotStacked(self):
3000
branch = self.make_branch('')
3001
translated_error = self.translateTuple(('NotStacked',), branch=branch)
3002
expected_error = errors.NotStacked(branch)
3003
self.assertEqual(expected_error, translated_error)
2848
3005
def test_ReadError_no_args(self):
2849
3006
path = 'a path'
2850
3007
translated_error = self.translateTuple(('ReadError',), path=path)
2895
3053
expected_error = errors.PermissionDenied(path, extra)
2896
3054
self.assertEqual(expected_error, translated_error)
3056
# GZ 2011-03-02: TODO test for PermissionDenied with non-ascii 'extra'
3058
def test_NoSuchFile_context_path(self):
3059
local_path = "local path"
3060
translated_error = self.translateTuple(('ReadError', "remote path"),
3062
expected_error = errors.ReadError(local_path)
3063
self.assertEqual(expected_error, translated_error)
3065
def test_NoSuchFile_without_context(self):
3066
remote_path = "remote path"
3067
translated_error = self.translateTuple(('ReadError', remote_path))
3068
expected_error = errors.ReadError(remote_path)
3069
self.assertEqual(expected_error, translated_error)
3071
def test_ReadOnlyError(self):
3072
translated_error = self.translateTuple(('ReadOnlyError',))
3073
expected_error = errors.TransportNotPossible("readonly transport")
3074
self.assertEqual(expected_error, translated_error)
3076
def test_MemoryError(self):
3077
translated_error = self.translateTuple(('MemoryError',))
3078
self.assertStartsWith(str(translated_error),
3079
"remote server out of memory")
3081
def test_generic_IndexError_no_classname(self):
3082
err = errors.ErrorFromSmartServer(('error', "list index out of range"))
3083
translated_error = self.translateErrorFromSmartServer(err)
3084
expected_error = errors.UnknownErrorFromSmartServer(err)
3085
self.assertEqual(expected_error, translated_error)
3087
# GZ 2011-03-02: TODO test generic non-ascii error string
3089
def test_generic_KeyError(self):
3090
err = errors.ErrorFromSmartServer(('error', 'KeyError', "1"))
3091
translated_error = self.translateErrorFromSmartServer(err)
3092
expected_error = errors.UnknownErrorFromSmartServer(err)
3093
self.assertEqual(expected_error, translated_error)
2899
3096
class TestErrorTranslationRobustness(TestErrorTranslationBase):
2900
3097
"""Unit tests for bzrlib.remote._translate_error's robustness.
3143
3340
def test_copy_content_into_avoids_revision_history(self):
3144
3341
local = self.make_branch('local')
3145
remote_backing_tree = self.make_branch_and_tree('remote')
3146
remote_backing_tree.commit("Commit.")
3342
builder = self.make_branch_builder('remote')
3343
builder.build_commit(message="Commit.")
3147
3344
remote_branch_url = self.smart_server.get_url() + 'remote'
3148
3345
remote_branch = bzrdir.BzrDir.open(remote_branch_url).open_branch()
3149
3346
local.repository.fetch(remote_branch.repository)
3150
3347
self.hpss_calls = []
3151
3348
remote_branch.copy_content_into(local)
3152
3349
self.assertFalse('Branch.revision_history' in self.hpss_calls)
3351
def test_fetch_everything_needs_just_one_call(self):
3352
local = self.make_branch('local')
3353
builder = self.make_branch_builder('remote')
3354
builder.build_commit(message="Commit.")
3355
remote_branch_url = self.smart_server.get_url() + 'remote'
3356
remote_branch = bzrdir.BzrDir.open(remote_branch_url).open_branch()
3357
self.hpss_calls = []
3358
local.repository.fetch(remote_branch.repository,
3359
fetch_spec=graph.EverythingResult(remote_branch.repository))
3360
self.assertEqual(['Repository.get_stream_1.19'], self.hpss_calls)
3362
def override_verb(self, verb_name, verb):
3363
request_handlers = request.request_handlers
3364
orig_verb = request_handlers.get(verb_name)
3365
request_handlers.register(verb_name, verb, override_existing=True)
3366
self.addCleanup(request_handlers.register, verb_name, orig_verb,
3367
override_existing=True)
3369
def test_fetch_everything_backwards_compat(self):
3370
"""Can fetch with EverythingResult even with pre 2.4 servers.
3372
Pre-2.4 do not support 'everything' searches with the
3373
Repository.get_stream_1.19 verb.
3376
class OldGetStreamVerb(SmartServerRepositoryGetStream_1_19):
3377
"""A version of the Repository.get_stream_1.19 verb patched to
3378
reject 'everything' searches the way 2.3 and earlier do.
3380
def recreate_search(self, repository, search_bytes, discard_excess=False):
3381
verb_log.append(search_bytes.split('\n', 1)[0])
3382
if search_bytes == 'everything':
3383
return (None, request.FailedSmartServerResponse(('BadSearch',)))
3384
return super(OldGetStreamVerb,
3385
self).recreate_search(repository, search_bytes,
3386
discard_excess=discard_excess)
3387
self.override_verb('Repository.get_stream_1.19', OldGetStreamVerb)
3388
local = self.make_branch('local')
3389
builder = self.make_branch_builder('remote')
3390
builder.build_commit(message="Commit.")
3391
remote_branch_url = self.smart_server.get_url() + 'remote'
3392
remote_branch = bzrdir.BzrDir.open(remote_branch_url).open_branch()
3393
self.hpss_calls = []
3394
local.repository.fetch(remote_branch.repository,
3395
fetch_spec=graph.EverythingResult(remote_branch.repository))
3396
# make sure the overridden verb was used
3397
self.assertLength(1, verb_log)
3398
# more than one HPSS call is needed, but because it's a VFS callback
3399
# its hard to predict exactly how many.
3400
self.assertTrue(len(self.hpss_calls) > 1)