764
741
self.assertEqual(network_name, repo._format.network_name())
767
class TestBzrDirFormatInitializeEx(TestRemote):
769
def test_success(self):
770
"""Simple test for typical successful call."""
771
fmt = bzrdir.RemoteBzrDirFormat()
772
default_format_name = BzrDirFormat.get_default_format().network_name()
773
transport = self.get_transport()
774
client = FakeClient(transport.base)
775
client.add_expected_call(
776
'BzrDirFormat.initialize_ex_1.16',
777
(default_format_name, 'path', 'False', 'False', 'False', '',
778
'', '', '', 'False'),
780
('.', 'no', 'no', 'yes', 'repo fmt', 'repo bzrdir fmt',
781
'bzrdir fmt', 'False', '', '', 'repo lock token'))
782
# XXX: It would be better to call fmt.initialize_on_transport_ex, but
783
# it's currently hard to test that without supplying a real remote
784
# transport connected to a real server.
785
result = fmt._initialize_on_transport_ex_rpc(client, 'path',
786
transport, False, False, False, None, None, None, None, False)
787
self.assertFinished(client)
789
def test_error(self):
790
"""Error responses are translated, e.g. 'PermissionDenied' raises the
791
corresponding error from the client.
793
fmt = bzrdir.RemoteBzrDirFormat()
794
default_format_name = BzrDirFormat.get_default_format().network_name()
795
transport = self.get_transport()
796
client = FakeClient(transport.base)
797
client.add_expected_call(
798
'BzrDirFormat.initialize_ex_1.16',
799
(default_format_name, 'path', 'False', 'False', 'False', '',
800
'', '', '', 'False'),
802
('PermissionDenied', 'path', 'extra info'))
803
# XXX: It would be better to call fmt.initialize_on_transport_ex, but
804
# it's currently hard to test that without supplying a real remote
805
# transport connected to a real server.
806
err = self.assertRaises(errors.PermissionDenied,
807
fmt._initialize_on_transport_ex_rpc, client, 'path', transport,
808
False, False, False, None, None, None, None, False)
809
self.assertEqual('path', err.path)
810
self.assertEqual(': extra info', err.extra)
811
self.assertFinished(client)
813
def test_error_from_real_server(self):
814
"""Integration test for error translation."""
815
transport = self.make_smart_server('foo')
816
transport = transport.clone('no-such-path')
817
fmt = bzrdir.RemoteBzrDirFormat()
818
err = self.assertRaises(errors.NoSuchFile,
819
fmt.initialize_on_transport_ex, transport, create_prefix=False)
822
744
class OldSmartClient(object):
823
745
"""A fake smart client for test_old_version that just returns a version one
824
746
response to the 'hello' (query version) command.
1007
864
transport = transport.clone('quack')
1008
865
branch = self.make_remote_branch(transport, client)
1009
866
result = branch.tags.get_tag_dict()
1010
self.assertFinished(client)
867
client.finished_test()
1011
868
self.assertEqual({}, result)
1014
class TestBranchSetTagsBytes(RemoteBranchTestCase):
1016
def test_trivial(self):
1017
transport = MemoryTransport()
1018
client = FakeClient(transport.base)
1019
client.add_expected_call(
1020
'Branch.get_stacked_on_url', ('quack/',),
1021
'error', ('NotStacked',))
1022
client.add_expected_call(
1023
'Branch.set_tags_bytes', ('quack/', 'branch token', 'repo token'),
1025
transport.mkdir('quack')
1026
transport = transport.clone('quack')
1027
branch = self.make_remote_branch(transport, client)
1028
self.lock_remote_branch(branch)
1029
branch._set_tags_bytes('tags bytes')
1030
self.assertFinished(client)
1031
self.assertEqual('tags bytes', client._calls[-1][-1])
1033
def test_backwards_compatible(self):
1034
transport = MemoryTransport()
1035
client = FakeClient(transport.base)
1036
client.add_expected_call(
1037
'Branch.get_stacked_on_url', ('quack/',),
1038
'error', ('NotStacked',))
1039
client.add_expected_call(
1040
'Branch.set_tags_bytes', ('quack/', 'branch token', 'repo token'),
1041
'unknown', ('Branch.set_tags_bytes',))
1042
transport.mkdir('quack')
1043
transport = transport.clone('quack')
1044
branch = self.make_remote_branch(transport, client)
1045
self.lock_remote_branch(branch)
1046
class StubRealBranch(object):
1049
def _set_tags_bytes(self, bytes):
1050
self.calls.append(('set_tags_bytes', bytes))
1051
real_branch = StubRealBranch()
1052
branch._real_branch = real_branch
1053
branch._set_tags_bytes('tags bytes')
1054
# Call a second time, to exercise the 'remote version already inferred'
1056
branch._set_tags_bytes('tags bytes')
1057
self.assertFinished(client)
1059
[('set_tags_bytes', 'tags bytes')] * 2, real_branch.calls)
1062
871
class TestBranchLastRevisionInfo(RemoteBranchTestCase):
1064
873
def test_empty_branch(self):
1531
1357
('call_expecting_body', 'Branch.get_config_file', ('memory:///',))],
1534
def test_get_multi_line_branch_conf(self):
1535
# Make sure that multiple-line branch.conf files are supported
1537
# https://bugs.edge.launchpad.net/bzr/+bug/354075
1538
client = FakeClient()
1539
client.add_expected_call(
1540
'Branch.get_stacked_on_url', ('memory:///',),
1541
'error', ('NotStacked',),)
1542
client.add_success_response_with_body('a = 1\nb = 2\nc = 3\n', 'ok')
1543
transport = MemoryTransport()
1544
branch = self.make_remote_branch(transport, client)
1545
config = branch.get_config()
1546
self.assertEqual(u'2', config.get_user_option('b'))
1548
def test_set_option(self):
1549
client = FakeClient()
1550
client.add_expected_call(
1551
'Branch.get_stacked_on_url', ('memory:///',),
1552
'error', ('NotStacked',),)
1553
client.add_expected_call(
1554
'Branch.lock_write', ('memory:///', '', ''),
1555
'success', ('ok', 'branch token', 'repo token'))
1556
client.add_expected_call(
1557
'Branch.set_config_option', ('memory:///', 'branch token',
1558
'repo token', 'foo', 'bar', ''),
1560
client.add_expected_call(
1561
'Branch.unlock', ('memory:///', 'branch token', 'repo token'),
1563
transport = MemoryTransport()
1564
branch = self.make_remote_branch(transport, client)
1566
config = branch._get_config()
1567
config.set_option('foo', 'bar')
1569
self.assertFinished(client)
1571
def test_backwards_compat_set_option(self):
1572
self.setup_smart_server_with_call_log()
1573
branch = self.make_branch('.')
1574
verb = 'Branch.set_config_option'
1575
self.disable_verb(verb)
1577
self.addCleanup(branch.unlock)
1578
self.reset_smart_call_log()
1579
branch._get_config().set_option('value', 'name')
1580
self.assertLength(10, self.hpss_calls)
1581
self.assertEqual('value', branch._get_config().get_option('name'))
1584
1361
class TestBranchLockWrite(RemoteBranchTestCase):
2089
1834
self.assertEqual(('AnUnexpectedError',), e.error_tuple)
2092
class TestRepositoryGetRevIdForRevno(TestRemoteRepository):
2095
repo, client = self.setup_fake_client_and_repository('quack')
2096
client.add_expected_call(
2097
'Repository.get_rev_id_for_revno', ('quack/', 5, (42, 'rev-foo')),
2098
'success', ('ok', 'rev-five'))
2099
result = repo.get_rev_id_for_revno(5, (42, 'rev-foo'))
2100
self.assertEqual((True, 'rev-five'), result)
2101
self.assertFinished(client)
2103
def test_history_incomplete(self):
2104
repo, client = self.setup_fake_client_and_repository('quack')
2105
client.add_expected_call(
2106
'Repository.get_rev_id_for_revno', ('quack/', 5, (42, 'rev-foo')),
2107
'success', ('history-incomplete', 10, 'rev-ten'))
2108
result = repo.get_rev_id_for_revno(5, (42, 'rev-foo'))
2109
self.assertEqual((False, (10, 'rev-ten')), result)
2110
self.assertFinished(client)
2112
def test_history_incomplete_with_fallback(self):
2113
"""A 'history-incomplete' response causes the fallback repository to be
2114
queried too, if one is set.
2116
# Make a repo with a fallback repo, both using a FakeClient.
2117
format = remote.response_tuple_to_repo_format(
2118
('yes', 'no', 'yes', 'fake-network-name'))
2119
repo, client = self.setup_fake_client_and_repository('quack')
2120
repo._format = format
2121
fallback_repo, ignored = self.setup_fake_client_and_repository(
2123
fallback_repo._client = client
2124
repo.add_fallback_repository(fallback_repo)
2125
# First the client should ask the primary repo
2126
client.add_expected_call(
2127
'Repository.get_rev_id_for_revno', ('quack/', 1, (42, 'rev-foo')),
2128
'success', ('history-incomplete', 2, 'rev-two'))
2129
# Then it should ask the fallback, using revno/revid from the
2130
# history-incomplete response as the known revno/revid.
2131
client.add_expected_call(
2132
'Repository.get_rev_id_for_revno',('fallback/', 1, (2, 'rev-two')),
2133
'success', ('ok', 'rev-one'))
2134
result = repo.get_rev_id_for_revno(1, (42, 'rev-foo'))
2135
self.assertEqual((True, 'rev-one'), result)
2136
self.assertFinished(client)
2138
def test_nosuchrevision(self):
2139
# 'nosuchrevision' is returned when the known-revid is not found in the
2140
# remote repo. The client translates that response to NoSuchRevision.
2141
repo, client = self.setup_fake_client_and_repository('quack')
2142
client.add_expected_call(
2143
'Repository.get_rev_id_for_revno', ('quack/', 5, (42, 'rev-foo')),
2144
'error', ('nosuchrevision', 'rev-foo'))
2146
errors.NoSuchRevision,
2147
repo.get_rev_id_for_revno, 5, (42, 'rev-foo'))
2148
self.assertFinished(client)
2150
def test_branch_fallback_locking(self):
2151
"""RemoteBranch.get_rev_id takes a read lock, and tries to call the
2152
get_rev_id_for_revno verb. If the verb is unknown the VFS fallback
2153
will be invoked, which will fail if the repo is unlocked.
2155
self.setup_smart_server_with_call_log()
2156
tree = self.make_branch_and_memory_tree('.')
2158
rev1 = tree.commit('First')
2159
rev2 = tree.commit('Second')
2161
branch = tree.branch
2162
self.assertFalse(branch.is_locked())
2163
self.reset_smart_call_log()
2164
verb = 'Repository.get_rev_id_for_revno'
2165
self.disable_verb(verb)
2166
self.assertEqual(rev1, branch.get_rev_id(1))
2167
self.assertLength(1, [call for call in self.hpss_calls if
2168
call.call.method == verb])
2171
1837
class TestRepositoryIsShared(TestRemoteRepository):
2173
1839
def test_is_shared(self):
2288
1954
self.assertEqual([], client._calls)
2291
class TestRepositoryInsertStreamBase(TestRemoteRepository):
2292
"""Base class for Repository.insert_stream and .insert_stream_1.19
2296
def checkInsertEmptyStream(self, repo, client):
2297
"""Insert an empty stream, checking the result.
2299
This checks that there are no resume_tokens or missing_keys, and that
2300
the client is finished.
2302
sink = repo._get_sink()
2303
fmt = repository.RepositoryFormat.get_default_format()
2304
resume_tokens, missing_keys = sink.insert_stream([], fmt, [])
2305
self.assertEqual([], resume_tokens)
2306
self.assertEqual(set(), missing_keys)
2307
self.assertFinished(client)
2310
class TestRepositoryInsertStream(TestRepositoryInsertStreamBase):
2311
"""Tests for using Repository.insert_stream verb when the _1.19 variant is
2314
This test case is very similar to TestRepositoryInsertStream_1_19.
2318
TestRemoteRepository.setUp(self)
2319
self.disable_verb('Repository.insert_stream_1.19')
2321
def test_unlocked_repo(self):
2322
transport_path = 'quack'
2323
repo, client = self.setup_fake_client_and_repository(transport_path)
2324
client.add_expected_call(
2325
'Repository.insert_stream_1.19', ('quack/', ''),
2326
'unknown', ('Repository.insert_stream_1.19',))
2327
client.add_expected_call(
2328
'Repository.insert_stream', ('quack/', ''),
2330
client.add_expected_call(
2331
'Repository.insert_stream', ('quack/', ''),
2333
self.checkInsertEmptyStream(repo, client)
2335
def test_locked_repo_with_no_lock_token(self):
2336
transport_path = 'quack'
2337
repo, client = self.setup_fake_client_and_repository(transport_path)
2338
client.add_expected_call(
2339
'Repository.lock_write', ('quack/', ''),
2340
'success', ('ok', ''))
2341
client.add_expected_call(
2342
'Repository.insert_stream_1.19', ('quack/', ''),
2343
'unknown', ('Repository.insert_stream_1.19',))
2344
client.add_expected_call(
2345
'Repository.insert_stream', ('quack/', ''),
2347
client.add_expected_call(
2348
'Repository.insert_stream', ('quack/', ''),
2351
self.checkInsertEmptyStream(repo, client)
2353
def test_locked_repo_with_lock_token(self):
2354
transport_path = 'quack'
2355
repo, client = self.setup_fake_client_and_repository(transport_path)
2356
client.add_expected_call(
2357
'Repository.lock_write', ('quack/', ''),
2358
'success', ('ok', 'a token'))
2359
client.add_expected_call(
2360
'Repository.insert_stream_1.19', ('quack/', '', 'a token'),
2361
'unknown', ('Repository.insert_stream_1.19',))
2362
client.add_expected_call(
2363
'Repository.insert_stream_locked', ('quack/', '', 'a token'),
2365
client.add_expected_call(
2366
'Repository.insert_stream_locked', ('quack/', '', 'a token'),
2369
self.checkInsertEmptyStream(repo, client)
2371
def test_stream_with_inventory_deltas(self):
2372
"""'inventory-deltas' substreams cannot be sent to the
2373
Repository.insert_stream verb, because not all servers that implement
2374
that verb will accept them. So when one is encountered the RemoteSink
2375
immediately stops using that verb and falls back to VFS insert_stream.
2377
transport_path = 'quack'
2378
repo, client = self.setup_fake_client_and_repository(transport_path)
2379
client.add_expected_call(
2380
'Repository.insert_stream_1.19', ('quack/', ''),
2381
'unknown', ('Repository.insert_stream_1.19',))
2382
client.add_expected_call(
2383
'Repository.insert_stream', ('quack/', ''),
2385
client.add_expected_call(
2386
'Repository.insert_stream', ('quack/', ''),
2388
# Create a fake real repository for insert_stream to fall back on, so
2389
# that we can directly see the records the RemoteSink passes to the
2394
def insert_stream(self, stream, src_format, resume_tokens):
2395
for substream_kind, substream in stream:
2396
self.records.append(
2397
(substream_kind, [record.key for record in substream]))
2398
return ['fake tokens'], ['fake missing keys']
2399
fake_real_sink = FakeRealSink()
2400
class FakeRealRepository:
2401
def _get_sink(self):
2402
return fake_real_sink
2403
def is_in_write_group(self):
2405
def refresh_data(self):
2407
repo._real_repository = FakeRealRepository()
2408
sink = repo._get_sink()
2409
fmt = repository.RepositoryFormat.get_default_format()
2410
stream = self.make_stream_with_inv_deltas(fmt)
2411
resume_tokens, missing_keys = sink.insert_stream(stream, fmt, [])
2412
# Every record from the first inventory delta should have been sent to
2414
expected_records = [
2415
('inventory-deltas', [('rev2',), ('rev3',)]),
2416
('texts', [('some-rev', 'some-file')])]
2417
self.assertEqual(expected_records, fake_real_sink.records)
2418
# The return values from the real sink's insert_stream are propagated
2419
# back to the original caller.
2420
self.assertEqual(['fake tokens'], resume_tokens)
2421
self.assertEqual(['fake missing keys'], missing_keys)
2422
self.assertFinished(client)
2424
def make_stream_with_inv_deltas(self, fmt):
2425
"""Make a simple stream with an inventory delta followed by more
2426
records and more substreams to test that all records and substreams
2427
from that point on are used.
2429
This sends, in order:
2430
* inventories substream: rev1, rev2, rev3. rev2 and rev3 are
2432
* texts substream: (some-rev, some-file)
2434
# Define a stream using generators so that it isn't rewindable.
2435
inv = inventory.Inventory(revision_id='rev1')
2436
inv.root.revision = 'rev1'
2437
def stream_with_inv_delta():
2438
yield ('inventories', inventories_substream())
2439
yield ('inventory-deltas', inventory_delta_substream())
2441
versionedfile.FulltextContentFactory(
2442
('some-rev', 'some-file'), (), None, 'content')])
2443
def inventories_substream():
2444
# An empty inventory fulltext. This will be streamed normally.
2445
text = fmt._serializer.write_inventory_to_string(inv)
2446
yield versionedfile.FulltextContentFactory(
2447
('rev1',), (), None, text)
2448
def inventory_delta_substream():
2449
# An inventory delta. This can't be streamed via this verb, so it
2450
# will trigger a fallback to VFS insert_stream.
2451
entry = inv.make_entry(
2452
'directory', 'newdir', inv.root.file_id, 'newdir-id')
2453
entry.revision = 'ghost'
2454
delta = [(None, 'newdir', 'newdir-id', entry)]
2455
serializer = inventory_delta.InventoryDeltaSerializer(
2456
versioned_root=True, tree_references=False)
2457
lines = serializer.delta_to_lines('rev1', 'rev2', delta)
2458
yield versionedfile.ChunkedContentFactory(
2459
('rev2',), (('rev1',)), None, lines)
2461
lines = serializer.delta_to_lines('rev1', 'rev3', delta)
2462
yield versionedfile.ChunkedContentFactory(
2463
('rev3',), (('rev1',)), None, lines)
2464
return stream_with_inv_delta()
2467
class TestRepositoryInsertStream_1_19(TestRepositoryInsertStreamBase):
2469
def test_unlocked_repo(self):
2470
transport_path = 'quack'
2471
repo, client = self.setup_fake_client_and_repository(transport_path)
2472
client.add_expected_call(
2473
'Repository.insert_stream_1.19', ('quack/', ''),
2475
client.add_expected_call(
2476
'Repository.insert_stream_1.19', ('quack/', ''),
2478
self.checkInsertEmptyStream(repo, client)
2480
def test_locked_repo_with_no_lock_token(self):
2481
transport_path = 'quack'
2482
repo, client = self.setup_fake_client_and_repository(transport_path)
2483
client.add_expected_call(
2484
'Repository.lock_write', ('quack/', ''),
2485
'success', ('ok', ''))
2486
client.add_expected_call(
2487
'Repository.insert_stream_1.19', ('quack/', ''),
2489
client.add_expected_call(
2490
'Repository.insert_stream_1.19', ('quack/', ''),
2493
self.checkInsertEmptyStream(repo, client)
2495
def test_locked_repo_with_lock_token(self):
2496
transport_path = 'quack'
2497
repo, client = self.setup_fake_client_and_repository(transport_path)
2498
client.add_expected_call(
2499
'Repository.lock_write', ('quack/', ''),
2500
'success', ('ok', 'a token'))
2501
client.add_expected_call(
2502
'Repository.insert_stream_1.19', ('quack/', '', 'a token'),
2504
client.add_expected_call(
2505
'Repository.insert_stream_1.19', ('quack/', '', 'a token'),
2508
self.checkInsertEmptyStream(repo, client)
1957
class TestRepositoryInsertStream(TestRemoteRepository):
1959
def test_unlocked_repo(self):
1960
transport_path = 'quack'
1961
repo, client = self.setup_fake_client_and_repository(transport_path)
1962
client.add_expected_call(
1963
'Repository.insert_stream', ('quack/', ''),
1965
client.add_expected_call(
1966
'Repository.insert_stream', ('quack/', ''),
1968
sink = repo._get_sink()
1969
fmt = repository.RepositoryFormat.get_default_format()
1970
resume_tokens, missing_keys = sink.insert_stream([], fmt, [])
1971
self.assertEqual([], resume_tokens)
1972
self.assertEqual(set(), missing_keys)
1973
client.finished_test()
1975
def test_locked_repo_with_no_lock_token(self):
1976
transport_path = 'quack'
1977
repo, client = self.setup_fake_client_and_repository(transport_path)
1978
client.add_expected_call(
1979
'Repository.lock_write', ('quack/', ''),
1980
'success', ('ok', ''))
1981
client.add_expected_call(
1982
'Repository.insert_stream', ('quack/', ''),
1984
client.add_expected_call(
1985
'Repository.insert_stream', ('quack/', ''),
1988
sink = repo._get_sink()
1989
fmt = repository.RepositoryFormat.get_default_format()
1990
resume_tokens, missing_keys = sink.insert_stream([], fmt, [])
1991
self.assertEqual([], resume_tokens)
1992
self.assertEqual(set(), missing_keys)
1993
client.finished_test()
1995
def test_locked_repo_with_lock_token(self):
1996
transport_path = 'quack'
1997
repo, client = self.setup_fake_client_and_repository(transport_path)
1998
client.add_expected_call(
1999
'Repository.lock_write', ('quack/', ''),
2000
'success', ('ok', 'a token'))
2001
client.add_expected_call(
2002
'Repository.insert_stream_locked', ('quack/', '', 'a token'),
2004
client.add_expected_call(
2005
'Repository.insert_stream_locked', ('quack/', '', 'a token'),
2008
sink = repo._get_sink()
2009
fmt = repository.RepositoryFormat.get_default_format()
2010
resume_tokens, missing_keys = sink.insert_stream([], fmt, [])
2011
self.assertEqual([], resume_tokens)
2012
self.assertEqual(set(), missing_keys)
2013
client.finished_test()
2511
2016
class TestRepositoryTarball(TestRemoteRepository):