758
741
self.assertEqual(network_name, repo._format.network_name())
761
class TestBzrDirFormatInitializeEx(TestRemote):
763
def test_success(self):
764
"""Simple test for typical successful call."""
765
fmt = bzrdir.RemoteBzrDirFormat()
766
default_format_name = BzrDirFormat.get_default_format().network_name()
767
transport = self.get_transport()
768
client = FakeClient(transport.base)
769
client.add_expected_call(
770
'BzrDirFormat.initialize_ex_1.16',
771
(default_format_name, 'path', 'False', 'False', 'False', '',
772
'', '', '', 'False'),
774
('.', 'no', 'no', 'yes', 'repo fmt', 'repo bzrdir fmt',
775
'bzrdir fmt', 'False', '', '', 'repo lock token'))
776
# XXX: It would be better to call fmt.initialize_on_transport_ex, but
777
# it's currently hard to test that without supplying a real remote
778
# transport connected to a real server.
779
result = fmt._initialize_on_transport_ex_rpc(client, 'path',
780
transport, False, False, False, None, None, None, None, False)
781
self.assertFinished(client)
783
def test_error(self):
784
"""Error responses are translated, e.g. 'PermissionDenied' raises the
785
corresponding error from the client.
787
fmt = bzrdir.RemoteBzrDirFormat()
788
default_format_name = BzrDirFormat.get_default_format().network_name()
789
transport = self.get_transport()
790
client = FakeClient(transport.base)
791
client.add_expected_call(
792
'BzrDirFormat.initialize_ex_1.16',
793
(default_format_name, 'path', 'False', 'False', 'False', '',
794
'', '', '', 'False'),
796
('PermissionDenied', 'path', 'extra info'))
797
# XXX: It would be better to call fmt.initialize_on_transport_ex, but
798
# it's currently hard to test that without supplying a real remote
799
# transport connected to a real server.
800
err = self.assertRaises(errors.PermissionDenied,
801
fmt._initialize_on_transport_ex_rpc, client, 'path', transport,
802
False, False, False, None, None, None, None, False)
803
self.assertEqual('path', err.path)
804
self.assertEqual(': extra info', err.extra)
805
self.assertFinished(client)
807
def test_error_from_real_server(self):
808
"""Integration test for error translation."""
809
transport = self.make_smart_server('foo')
810
transport = transport.clone('no-such-path')
811
fmt = bzrdir.RemoteBzrDirFormat()
812
err = self.assertRaises(errors.NoSuchFile,
813
fmt.initialize_on_transport_ex, transport, create_prefix=False)
816
744
class OldSmartClient(object):
817
745
"""A fake smart client for test_old_version that just returns a version one
818
746
response to the 'hello' (query version) command.
2035
1876
self.assertEqual(('AnUnexpectedError',), e.error_tuple)
2038
class TestRepositoryGetRevIdForRevno(TestRemoteRepository):
2041
repo, client = self.setup_fake_client_and_repository('quack')
2042
client.add_expected_call(
2043
'Repository.get_rev_id_for_revno', ('quack/', 5, (42, 'rev-foo')),
2044
'success', ('ok', 'rev-five'))
2045
result = repo.get_rev_id_for_revno(5, (42, 'rev-foo'))
2046
self.assertEqual((True, 'rev-five'), result)
2047
self.assertFinished(client)
2049
def test_history_incomplete(self):
2050
repo, client = self.setup_fake_client_and_repository('quack')
2051
client.add_expected_call(
2052
'Repository.get_rev_id_for_revno', ('quack/', 5, (42, 'rev-foo')),
2053
'success', ('history-incomplete', 10, 'rev-ten'))
2054
result = repo.get_rev_id_for_revno(5, (42, 'rev-foo'))
2055
self.assertEqual((False, (10, 'rev-ten')), result)
2056
self.assertFinished(client)
2058
def test_history_incomplete_with_fallback(self):
2059
"""A 'history-incomplete' response causes the fallback repository to be
2060
queried too, if one is set.
2062
# Make a repo with a fallback repo, both using a FakeClient.
2063
format = remote.response_tuple_to_repo_format(
2064
('yes', 'no', 'yes', 'fake-network-name'))
2065
repo, client = self.setup_fake_client_and_repository('quack')
2066
repo._format = format
2067
fallback_repo, ignored = self.setup_fake_client_and_repository(
2069
fallback_repo._client = client
2070
repo.add_fallback_repository(fallback_repo)
2071
# First the client should ask the primary repo
2072
client.add_expected_call(
2073
'Repository.get_rev_id_for_revno', ('quack/', 1, (42, 'rev-foo')),
2074
'success', ('history-incomplete', 2, 'rev-two'))
2075
# Then it should ask the fallback, using revno/revid from the
2076
# history-incomplete response as the known revno/revid.
2077
client.add_expected_call(
2078
'Repository.get_rev_id_for_revno',('fallback/', 1, (2, 'rev-two')),
2079
'success', ('ok', 'rev-one'))
2080
result = repo.get_rev_id_for_revno(1, (42, 'rev-foo'))
2081
self.assertEqual((True, 'rev-one'), result)
2082
self.assertFinished(client)
2084
def test_nosuchrevision(self):
2085
# 'nosuchrevision' is returned when the known-revid is not found in the
2086
# remote repo. The client translates that response to NoSuchRevision.
2087
repo, client = self.setup_fake_client_and_repository('quack')
2088
client.add_expected_call(
2089
'Repository.get_rev_id_for_revno', ('quack/', 5, (42, 'rev-foo')),
2090
'error', ('nosuchrevision', 'rev-foo'))
2092
errors.NoSuchRevision,
2093
repo.get_rev_id_for_revno, 5, (42, 'rev-foo'))
2094
self.assertFinished(client)
2097
1879
class TestRepositoryIsShared(TestRemoteRepository):
2099
1881
def test_is_shared(self):
2214
1996
self.assertEqual([], client._calls)
2217
class TestRepositoryInsertStreamBase(TestRemoteRepository):
2218
"""Base class for Repository.insert_stream and .insert_stream_1.19
2222
def checkInsertEmptyStream(self, repo, client):
2223
"""Insert an empty stream, checking the result.
2225
This checks that there are no resume_tokens or missing_keys, and that
2226
the client is finished.
2228
sink = repo._get_sink()
2229
fmt = repository.RepositoryFormat.get_default_format()
2230
resume_tokens, missing_keys = sink.insert_stream([], fmt, [])
2231
self.assertEqual([], resume_tokens)
2232
self.assertEqual(set(), missing_keys)
2233
self.assertFinished(client)
2236
class TestRepositoryInsertStream(TestRepositoryInsertStreamBase):
2237
"""Tests for using Repository.insert_stream verb when the _1.19 variant is
2240
This test case is very similar to TestRepositoryInsertStream_1_19.
2244
TestRemoteRepository.setUp(self)
2245
self.disable_verb('Repository.insert_stream_1.19')
2247
def test_unlocked_repo(self):
2248
transport_path = 'quack'
2249
repo, client = self.setup_fake_client_and_repository(transport_path)
2250
client.add_expected_call(
2251
'Repository.insert_stream_1.19', ('quack/', ''),
2252
'unknown', ('Repository.insert_stream_1.19',))
2253
client.add_expected_call(
2254
'Repository.insert_stream', ('quack/', ''),
2256
client.add_expected_call(
2257
'Repository.insert_stream', ('quack/', ''),
2259
self.checkInsertEmptyStream(repo, client)
2261
def test_locked_repo_with_no_lock_token(self):
2262
transport_path = 'quack'
2263
repo, client = self.setup_fake_client_and_repository(transport_path)
2264
client.add_expected_call(
2265
'Repository.lock_write', ('quack/', ''),
2266
'success', ('ok', ''))
2267
client.add_expected_call(
2268
'Repository.insert_stream_1.19', ('quack/', ''),
2269
'unknown', ('Repository.insert_stream_1.19',))
2270
client.add_expected_call(
2271
'Repository.insert_stream', ('quack/', ''),
2273
client.add_expected_call(
2274
'Repository.insert_stream', ('quack/', ''),
2277
self.checkInsertEmptyStream(repo, client)
2279
def test_locked_repo_with_lock_token(self):
2280
transport_path = 'quack'
2281
repo, client = self.setup_fake_client_and_repository(transport_path)
2282
client.add_expected_call(
2283
'Repository.lock_write', ('quack/', ''),
2284
'success', ('ok', 'a token'))
2285
client.add_expected_call(
2286
'Repository.insert_stream_1.19', ('quack/', '', 'a token'),
2287
'unknown', ('Repository.insert_stream_1.19',))
2288
client.add_expected_call(
2289
'Repository.insert_stream_locked', ('quack/', '', 'a token'),
2291
client.add_expected_call(
2292
'Repository.insert_stream_locked', ('quack/', '', 'a token'),
2295
self.checkInsertEmptyStream(repo, client)
2297
def test_stream_with_inventory_deltas(self):
2298
"""'inventory-deltas' substreams cannot be sent to the
2299
Repository.insert_stream verb, because not all servers that implement
2300
that verb will accept them. So when one is encountered the RemoteSink
2301
immediately stops using that verb and falls back to VFS insert_stream.
2303
transport_path = 'quack'
2304
repo, client = self.setup_fake_client_and_repository(transport_path)
2305
client.add_expected_call(
2306
'Repository.insert_stream_1.19', ('quack/', ''),
2307
'unknown', ('Repository.insert_stream_1.19',))
2308
client.add_expected_call(
2309
'Repository.insert_stream', ('quack/', ''),
2311
client.add_expected_call(
2312
'Repository.insert_stream', ('quack/', ''),
2314
# Create a fake real repository for insert_stream to fall back on, so
2315
# that we can directly see the records the RemoteSink passes to the
2320
def insert_stream(self, stream, src_format, resume_tokens):
2321
for substream_kind, substream in stream:
2322
self.records.append(
2323
(substream_kind, [record.key for record in substream]))
2324
return ['fake tokens'], ['fake missing keys']
2325
fake_real_sink = FakeRealSink()
2326
class FakeRealRepository:
2327
def _get_sink(self):
2328
return fake_real_sink
2329
repo._real_repository = FakeRealRepository()
2330
sink = repo._get_sink()
2331
fmt = repository.RepositoryFormat.get_default_format()
2332
stream = self.make_stream_with_inv_deltas(fmt)
2333
resume_tokens, missing_keys = sink.insert_stream(stream, fmt, [])
2334
# Every record from the first inventory delta should have been sent to
2336
expected_records = [
2337
('inventory-deltas', [('rev2',), ('rev3',)]),
2338
('texts', [('some-rev', 'some-file')])]
2339
self.assertEqual(expected_records, fake_real_sink.records)
2340
# The return values from the real sink's insert_stream are propagated
2341
# back to the original caller.
2342
self.assertEqual(['fake tokens'], resume_tokens)
2343
self.assertEqual(['fake missing keys'], missing_keys)
2344
self.assertFinished(client)
2346
def make_stream_with_inv_deltas(self, fmt):
2347
"""Make a simple stream with an inventory delta followed by more
2348
records and more substreams to test that all records and substreams
2349
from that point on are used.
2351
This sends, in order:
2352
* inventories substream: rev1, rev2, rev3. rev2 and rev3 are
2354
* texts substream: (some-rev, some-file)
2356
# Define a stream using generators so that it isn't rewindable.
2357
inv = inventory.Inventory(revision_id='rev1')
2358
inv.root.revision = 'rev1'
2359
def stream_with_inv_delta():
2360
yield ('inventories', inventories_substream())
2361
yield ('inventory-deltas', inventory_delta_substream())
2363
versionedfile.FulltextContentFactory(
2364
('some-rev', 'some-file'), (), None, 'content')])
2365
def inventories_substream():
2366
# An empty inventory fulltext. This will be streamed normally.
2367
text = fmt._serializer.write_inventory_to_string(inv)
2368
yield versionedfile.FulltextContentFactory(
2369
('rev1',), (), None, text)
2370
def inventory_delta_substream():
2371
# An inventory delta. This can't be streamed via this verb, so it
2372
# will trigger a fallback to VFS insert_stream.
2373
entry = inv.make_entry(
2374
'directory', 'newdir', inv.root.file_id, 'newdir-id')
2375
entry.revision = 'ghost'
2376
delta = [(None, 'newdir', 'newdir-id', entry)]
2377
serializer = inventory_delta.InventoryDeltaSerializer(
2378
versioned_root=True, tree_references=False)
2379
lines = serializer.delta_to_lines('rev1', 'rev2', delta)
2380
yield versionedfile.ChunkedContentFactory(
2381
('rev2',), (('rev1',)), None, lines)
2383
lines = serializer.delta_to_lines('rev1', 'rev3', delta)
2384
yield versionedfile.ChunkedContentFactory(
2385
('rev3',), (('rev1',)), None, lines)
2386
return stream_with_inv_delta()
2389
class TestRepositoryInsertStream_1_19(TestRepositoryInsertStreamBase):
2391
def test_unlocked_repo(self):
2392
transport_path = 'quack'
2393
repo, client = self.setup_fake_client_and_repository(transport_path)
2394
client.add_expected_call(
2395
'Repository.insert_stream_1.19', ('quack/', ''),
2397
client.add_expected_call(
2398
'Repository.insert_stream_1.19', ('quack/', ''),
2400
self.checkInsertEmptyStream(repo, client)
2402
def test_locked_repo_with_no_lock_token(self):
2403
transport_path = 'quack'
2404
repo, client = self.setup_fake_client_and_repository(transport_path)
2405
client.add_expected_call(
2406
'Repository.lock_write', ('quack/', ''),
2407
'success', ('ok', ''))
2408
client.add_expected_call(
2409
'Repository.insert_stream_1.19', ('quack/', ''),
2411
client.add_expected_call(
2412
'Repository.insert_stream_1.19', ('quack/', ''),
2415
self.checkInsertEmptyStream(repo, client)
2417
def test_locked_repo_with_lock_token(self):
2418
transport_path = 'quack'
2419
repo, client = self.setup_fake_client_and_repository(transport_path)
2420
client.add_expected_call(
2421
'Repository.lock_write', ('quack/', ''),
2422
'success', ('ok', 'a token'))
2423
client.add_expected_call(
2424
'Repository.insert_stream_1.19', ('quack/', '', 'a token'),
2426
client.add_expected_call(
2427
'Repository.insert_stream_1.19', ('quack/', '', 'a token'),
2430
self.checkInsertEmptyStream(repo, client)
1999
class TestRepositoryInsertStream(TestRemoteRepository):
2001
def test_unlocked_repo(self):
2002
transport_path = 'quack'
2003
repo, client = self.setup_fake_client_and_repository(transport_path)
2004
client.add_expected_call(
2005
'Repository.insert_stream', ('quack/', ''),
2007
client.add_expected_call(
2008
'Repository.insert_stream', ('quack/', ''),
2010
sink = repo._get_sink()
2011
fmt = repository.RepositoryFormat.get_default_format()
2012
resume_tokens, missing_keys = sink.insert_stream([], fmt, [])
2013
self.assertEqual([], resume_tokens)
2014
self.assertEqual(set(), missing_keys)
2015
client.finished_test()
2017
def test_locked_repo_with_no_lock_token(self):
2018
transport_path = 'quack'
2019
repo, client = self.setup_fake_client_and_repository(transport_path)
2020
client.add_expected_call(
2021
'Repository.lock_write', ('quack/', ''),
2022
'success', ('ok', ''))
2023
client.add_expected_call(
2024
'Repository.insert_stream', ('quack/', ''),
2026
client.add_expected_call(
2027
'Repository.insert_stream', ('quack/', ''),
2030
sink = repo._get_sink()
2031
fmt = repository.RepositoryFormat.get_default_format()
2032
resume_tokens, missing_keys = sink.insert_stream([], fmt, [])
2033
self.assertEqual([], resume_tokens)
2034
self.assertEqual(set(), missing_keys)
2035
client.finished_test()
2037
def test_locked_repo_with_lock_token(self):
2038
transport_path = 'quack'
2039
repo, client = self.setup_fake_client_and_repository(transport_path)
2040
client.add_expected_call(
2041
'Repository.lock_write', ('quack/', ''),
2042
'success', ('ok', 'a token'))
2043
client.add_expected_call(
2044
'Repository.insert_stream_locked', ('quack/', '', 'a token'),
2046
client.add_expected_call(
2047
'Repository.insert_stream_locked', ('quack/', '', 'a token'),
2050
sink = repo._get_sink()
2051
fmt = repository.RepositoryFormat.get_default_format()
2052
resume_tokens, missing_keys = sink.insert_stream([], fmt, [])
2053
self.assertEqual([], resume_tokens)
2054
self.assertEqual(set(), missing_keys)
2055
client.finished_test()
2433
2058
class TestRepositoryTarball(TestRemoteRepository):