2018
2035
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)
2021
2097
class TestRepositoryIsShared(TestRemoteRepository):
2023
2099
def test_is_shared(self):
2138
2214
self.assertEqual([], client._calls)
2141
class TestRepositoryInsertStream(TestRemoteRepository):
2143
def test_unlocked_repo(self):
2144
transport_path = 'quack'
2145
repo, client = self.setup_fake_client_and_repository(transport_path)
2146
client.add_expected_call(
2147
'Repository.insert_stream', ('quack/', ''),
2149
client.add_expected_call(
2150
'Repository.insert_stream', ('quack/', ''),
2152
sink = repo._get_sink()
2153
fmt = repository.RepositoryFormat.get_default_format()
2154
resume_tokens, missing_keys = sink.insert_stream([], fmt, [])
2155
self.assertEqual([], resume_tokens)
2156
self.assertEqual(set(), missing_keys)
2157
client.finished_test()
2159
def test_locked_repo_with_no_lock_token(self):
2160
transport_path = 'quack'
2161
repo, client = self.setup_fake_client_and_repository(transport_path)
2162
client.add_expected_call(
2163
'Repository.lock_write', ('quack/', ''),
2164
'success', ('ok', ''))
2165
client.add_expected_call(
2166
'Repository.insert_stream', ('quack/', ''),
2168
client.add_expected_call(
2169
'Repository.insert_stream', ('quack/', ''),
2172
sink = repo._get_sink()
2173
fmt = repository.RepositoryFormat.get_default_format()
2174
resume_tokens, missing_keys = sink.insert_stream([], fmt, [])
2175
self.assertEqual([], resume_tokens)
2176
self.assertEqual(set(), missing_keys)
2177
client.finished_test()
2179
def test_locked_repo_with_lock_token(self):
2180
transport_path = 'quack'
2181
repo, client = self.setup_fake_client_and_repository(transport_path)
2182
client.add_expected_call(
2183
'Repository.lock_write', ('quack/', ''),
2184
'success', ('ok', 'a token'))
2185
client.add_expected_call(
2186
'Repository.insert_stream_locked', ('quack/', '', 'a token'),
2188
client.add_expected_call(
2189
'Repository.insert_stream_locked', ('quack/', '', 'a token'),
2192
sink = repo._get_sink()
2193
fmt = repository.RepositoryFormat.get_default_format()
2194
resume_tokens, missing_keys = sink.insert_stream([], fmt, [])
2195
self.assertEqual([], resume_tokens)
2196
self.assertEqual(set(), missing_keys)
2197
client.finished_test()
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)
2200
2433
class TestRepositoryTarball(TestRemoteRepository):