478
449
self.assertEqual(bzrdir.BzrDirMetaFormat1, type(result))
479
450
self.assertEqual(None, result._repository_format)
480
451
self.assertEqual(None, result._branch_format)
481
self.assertFinished(client)
484
class TestBzrDirOpen(TestRemote):
486
def make_fake_client_and_transport(self, path='quack'):
487
transport = MemoryTransport()
488
transport.mkdir(path)
489
transport = transport.clone(path)
490
client = FakeClient(transport.base)
491
return client, transport
493
def test_absent(self):
494
client, transport = self.make_fake_client_and_transport()
495
client.add_expected_call(
496
'BzrDir.open_2.1', ('quack/',), 'success', ('no',))
497
self.assertRaises(errors.NotBranchError, RemoteBzrDir, transport,
498
remote.RemoteBzrDirFormat(), _client=client, _force_probe=True)
499
self.assertFinished(client)
501
def test_present_without_workingtree(self):
502
client, transport = self.make_fake_client_and_transport()
503
client.add_expected_call(
504
'BzrDir.open_2.1', ('quack/',), 'success', ('yes', 'no'))
505
bd = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
506
_client=client, _force_probe=True)
507
self.assertIsInstance(bd, RemoteBzrDir)
508
self.assertFalse(bd.has_workingtree())
509
self.assertRaises(errors.NoWorkingTree, bd.open_workingtree)
510
self.assertFinished(client)
512
def test_present_with_workingtree(self):
513
client, transport = self.make_fake_client_and_transport()
514
client.add_expected_call(
515
'BzrDir.open_2.1', ('quack/',), 'success', ('yes', 'yes'))
516
bd = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
517
_client=client, _force_probe=True)
518
self.assertIsInstance(bd, RemoteBzrDir)
519
self.assertTrue(bd.has_workingtree())
520
self.assertRaises(errors.NotLocalUrl, bd.open_workingtree)
521
self.assertFinished(client)
523
def test_backwards_compat(self):
524
client, transport = self.make_fake_client_and_transport()
525
client.add_expected_call(
526
'BzrDir.open_2.1', ('quack/',), 'unknown', ('BzrDir.open_2.1',))
527
client.add_expected_call(
528
'BzrDir.open', ('quack/',), 'success', ('yes',))
529
bd = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
530
_client=client, _force_probe=True)
531
self.assertIsInstance(bd, RemoteBzrDir)
532
self.assertFinished(client)
534
def test_backwards_compat_hpss_v2(self):
535
client, transport = self.make_fake_client_and_transport()
536
# Monkey-patch fake client to simulate real-world behaviour with v2
537
# server: upon first RPC call detect the protocol version, and because
538
# the version is 2 also do _remember_remote_is_before((1, 6)) before
539
# continuing with the RPC.
540
orig_check_call = client._check_call
541
def check_call(method, args):
542
client._medium._protocol_version = 2
543
client._medium._remember_remote_is_before((1, 6))
544
client._check_call = orig_check_call
545
client._check_call(method, args)
546
client._check_call = check_call
547
client.add_expected_call(
548
'BzrDir.open_2.1', ('quack/',), 'unknown', ('BzrDir.open_2.1',))
549
client.add_expected_call(
550
'BzrDir.open', ('quack/',), 'success', ('yes',))
551
bd = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
552
_client=client, _force_probe=True)
553
self.assertIsInstance(bd, RemoteBzrDir)
554
self.assertFinished(client)
452
client.finished_test()
557
455
class TestBzrDirOpenBranch(TestRemote):
1091
974
transport = transport.clone('quack')
1092
975
branch = self.make_remote_branch(transport, client)
1093
976
result = branch.tags.get_tag_dict()
1094
self.assertFinished(client)
977
client.finished_test()
1095
978
self.assertEqual({}, result)
1098
class TestBranchSetTagsBytes(RemoteBranchTestCase):
1100
def test_trivial(self):
1101
transport = MemoryTransport()
1102
client = FakeClient(transport.base)
1103
client.add_expected_call(
1104
'Branch.get_stacked_on_url', ('quack/',),
1105
'error', ('NotStacked',))
1106
client.add_expected_call(
1107
'Branch.set_tags_bytes', ('quack/', 'branch token', 'repo token'),
1109
transport.mkdir('quack')
1110
transport = transport.clone('quack')
1111
branch = self.make_remote_branch(transport, client)
1112
self.lock_remote_branch(branch)
1113
branch._set_tags_bytes('tags bytes')
1114
self.assertFinished(client)
1115
self.assertEqual('tags bytes', client._calls[-1][-1])
1117
def test_backwards_compatible(self):
1118
transport = MemoryTransport()
1119
client = FakeClient(transport.base)
1120
client.add_expected_call(
1121
'Branch.get_stacked_on_url', ('quack/',),
1122
'error', ('NotStacked',))
1123
client.add_expected_call(
1124
'Branch.set_tags_bytes', ('quack/', 'branch token', 'repo token'),
1125
'unknown', ('Branch.set_tags_bytes',))
1126
transport.mkdir('quack')
1127
transport = transport.clone('quack')
1128
branch = self.make_remote_branch(transport, client)
1129
self.lock_remote_branch(branch)
1130
class StubRealBranch(object):
1133
def _set_tags_bytes(self, bytes):
1134
self.calls.append(('set_tags_bytes', bytes))
1135
real_branch = StubRealBranch()
1136
branch._real_branch = real_branch
1137
branch._set_tags_bytes('tags bytes')
1138
# Call a second time, to exercise the 'remote version already inferred'
1140
branch._set_tags_bytes('tags bytes')
1141
self.assertFinished(client)
1143
[('set_tags_bytes', 'tags bytes')] * 2, real_branch.calls)
1146
981
class TestBranchLastRevisionInfo(RemoteBranchTestCase):
1148
983
def test_empty_branch(self):
2190
2018
self.assertEqual(('AnUnexpectedError',), e.error_tuple)
2193
class TestRepositoryGetRevIdForRevno(TestRemoteRepository):
2196
repo, client = self.setup_fake_client_and_repository('quack')
2197
client.add_expected_call(
2198
'Repository.get_rev_id_for_revno', ('quack/', 5, (42, 'rev-foo')),
2199
'success', ('ok', 'rev-five'))
2200
result = repo.get_rev_id_for_revno(5, (42, 'rev-foo'))
2201
self.assertEqual((True, 'rev-five'), result)
2202
self.assertFinished(client)
2204
def test_history_incomplete(self):
2205
repo, client = self.setup_fake_client_and_repository('quack')
2206
client.add_expected_call(
2207
'Repository.get_rev_id_for_revno', ('quack/', 5, (42, 'rev-foo')),
2208
'success', ('history-incomplete', 10, 'rev-ten'))
2209
result = repo.get_rev_id_for_revno(5, (42, 'rev-foo'))
2210
self.assertEqual((False, (10, 'rev-ten')), result)
2211
self.assertFinished(client)
2213
def test_history_incomplete_with_fallback(self):
2214
"""A 'history-incomplete' response causes the fallback repository to be
2215
queried too, if one is set.
2217
# Make a repo with a fallback repo, both using a FakeClient.
2218
format = remote.response_tuple_to_repo_format(
2219
('yes', 'no', 'yes', self.get_repo_format().network_name()))
2220
repo, client = self.setup_fake_client_and_repository('quack')
2221
repo._format = format
2222
fallback_repo, ignored = self.setup_fake_client_and_repository(
2224
fallback_repo._client = client
2225
fallback_repo._format = format
2226
repo.add_fallback_repository(fallback_repo)
2227
# First the client should ask the primary repo
2228
client.add_expected_call(
2229
'Repository.get_rev_id_for_revno', ('quack/', 1, (42, 'rev-foo')),
2230
'success', ('history-incomplete', 2, 'rev-two'))
2231
# Then it should ask the fallback, using revno/revid from the
2232
# history-incomplete response as the known revno/revid.
2233
client.add_expected_call(
2234
'Repository.get_rev_id_for_revno',('fallback/', 1, (2, 'rev-two')),
2235
'success', ('ok', 'rev-one'))
2236
result = repo.get_rev_id_for_revno(1, (42, 'rev-foo'))
2237
self.assertEqual((True, 'rev-one'), result)
2238
self.assertFinished(client)
2240
def test_nosuchrevision(self):
2241
# 'nosuchrevision' is returned when the known-revid is not found in the
2242
# remote repo. The client translates that response to NoSuchRevision.
2243
repo, client = self.setup_fake_client_and_repository('quack')
2244
client.add_expected_call(
2245
'Repository.get_rev_id_for_revno', ('quack/', 5, (42, 'rev-foo')),
2246
'error', ('nosuchrevision', 'rev-foo'))
2248
errors.NoSuchRevision,
2249
repo.get_rev_id_for_revno, 5, (42, 'rev-foo'))
2250
self.assertFinished(client)
2252
def test_branch_fallback_locking(self):
2253
"""RemoteBranch.get_rev_id takes a read lock, and tries to call the
2254
get_rev_id_for_revno verb. If the verb is unknown the VFS fallback
2255
will be invoked, which will fail if the repo is unlocked.
2257
self.setup_smart_server_with_call_log()
2258
tree = self.make_branch_and_memory_tree('.')
2260
rev1 = tree.commit('First')
2261
rev2 = tree.commit('Second')
2263
branch = tree.branch
2264
self.assertFalse(branch.is_locked())
2265
self.reset_smart_call_log()
2266
verb = 'Repository.get_rev_id_for_revno'
2267
self.disable_verb(verb)
2268
self.assertEqual(rev1, branch.get_rev_id(1))
2269
self.assertLength(1, [call for call in self.hpss_calls if
2270
call.call.method == verb])
2273
2021
class TestRepositoryIsShared(TestRemoteRepository):
2275
2023
def test_is_shared(self):
2390
2138
self.assertEqual([], client._calls)
2393
class TestRepositoryInsertStreamBase(TestRemoteRepository):
2394
"""Base class for Repository.insert_stream and .insert_stream_1.19
2398
def checkInsertEmptyStream(self, repo, client):
2399
"""Insert an empty stream, checking the result.
2401
This checks that there are no resume_tokens or missing_keys, and that
2402
the client is finished.
2404
sink = repo._get_sink()
2405
fmt = repository.RepositoryFormat.get_default_format()
2406
resume_tokens, missing_keys = sink.insert_stream([], fmt, [])
2407
self.assertEqual([], resume_tokens)
2408
self.assertEqual(set(), missing_keys)
2409
self.assertFinished(client)
2412
class TestRepositoryInsertStream(TestRepositoryInsertStreamBase):
2413
"""Tests for using Repository.insert_stream verb when the _1.19 variant is
2416
This test case is very similar to TestRepositoryInsertStream_1_19.
2420
TestRemoteRepository.setUp(self)
2421
self.disable_verb('Repository.insert_stream_1.19')
2423
def test_unlocked_repo(self):
2424
transport_path = 'quack'
2425
repo, client = self.setup_fake_client_and_repository(transport_path)
2426
client.add_expected_call(
2427
'Repository.insert_stream_1.19', ('quack/', ''),
2428
'unknown', ('Repository.insert_stream_1.19',))
2429
client.add_expected_call(
2430
'Repository.insert_stream', ('quack/', ''),
2432
client.add_expected_call(
2433
'Repository.insert_stream', ('quack/', ''),
2435
self.checkInsertEmptyStream(repo, client)
2437
def test_locked_repo_with_no_lock_token(self):
2438
transport_path = 'quack'
2439
repo, client = self.setup_fake_client_and_repository(transport_path)
2440
client.add_expected_call(
2441
'Repository.lock_write', ('quack/', ''),
2442
'success', ('ok', ''))
2443
client.add_expected_call(
2444
'Repository.insert_stream_1.19', ('quack/', ''),
2445
'unknown', ('Repository.insert_stream_1.19',))
2446
client.add_expected_call(
2447
'Repository.insert_stream', ('quack/', ''),
2449
client.add_expected_call(
2450
'Repository.insert_stream', ('quack/', ''),
2453
self.checkInsertEmptyStream(repo, client)
2455
def test_locked_repo_with_lock_token(self):
2456
transport_path = 'quack'
2457
repo, client = self.setup_fake_client_and_repository(transport_path)
2458
client.add_expected_call(
2459
'Repository.lock_write', ('quack/', ''),
2460
'success', ('ok', 'a token'))
2461
client.add_expected_call(
2462
'Repository.insert_stream_1.19', ('quack/', '', 'a token'),
2463
'unknown', ('Repository.insert_stream_1.19',))
2464
client.add_expected_call(
2465
'Repository.insert_stream_locked', ('quack/', '', 'a token'),
2467
client.add_expected_call(
2468
'Repository.insert_stream_locked', ('quack/', '', 'a token'),
2471
self.checkInsertEmptyStream(repo, client)
2473
def test_stream_with_inventory_deltas(self):
2474
"""'inventory-deltas' substreams cannot be sent to the
2475
Repository.insert_stream verb, because not all servers that implement
2476
that verb will accept them. So when one is encountered the RemoteSink
2477
immediately stops using that verb and falls back to VFS insert_stream.
2479
transport_path = 'quack'
2480
repo, client = self.setup_fake_client_and_repository(transport_path)
2481
client.add_expected_call(
2482
'Repository.insert_stream_1.19', ('quack/', ''),
2483
'unknown', ('Repository.insert_stream_1.19',))
2484
client.add_expected_call(
2485
'Repository.insert_stream', ('quack/', ''),
2487
client.add_expected_call(
2488
'Repository.insert_stream', ('quack/', ''),
2490
# Create a fake real repository for insert_stream to fall back on, so
2491
# that we can directly see the records the RemoteSink passes to the
2496
def insert_stream(self, stream, src_format, resume_tokens):
2497
for substream_kind, substream in stream:
2498
self.records.append(
2499
(substream_kind, [record.key for record in substream]))
2500
return ['fake tokens'], ['fake missing keys']
2501
fake_real_sink = FakeRealSink()
2502
class FakeRealRepository:
2503
def _get_sink(self):
2504
return fake_real_sink
2505
def is_in_write_group(self):
2507
def refresh_data(self):
2509
repo._real_repository = FakeRealRepository()
2510
sink = repo._get_sink()
2511
fmt = repository.RepositoryFormat.get_default_format()
2512
stream = self.make_stream_with_inv_deltas(fmt)
2513
resume_tokens, missing_keys = sink.insert_stream(stream, fmt, [])
2514
# Every record from the first inventory delta should have been sent to
2516
expected_records = [
2517
('inventory-deltas', [('rev2',), ('rev3',)]),
2518
('texts', [('some-rev', 'some-file')])]
2519
self.assertEqual(expected_records, fake_real_sink.records)
2520
# The return values from the real sink's insert_stream are propagated
2521
# back to the original caller.
2522
self.assertEqual(['fake tokens'], resume_tokens)
2523
self.assertEqual(['fake missing keys'], missing_keys)
2524
self.assertFinished(client)
2526
def make_stream_with_inv_deltas(self, fmt):
2527
"""Make a simple stream with an inventory delta followed by more
2528
records and more substreams to test that all records and substreams
2529
from that point on are used.
2531
This sends, in order:
2532
* inventories substream: rev1, rev2, rev3. rev2 and rev3 are
2534
* texts substream: (some-rev, some-file)
2536
# Define a stream using generators so that it isn't rewindable.
2537
inv = inventory.Inventory(revision_id='rev1')
2538
inv.root.revision = 'rev1'
2539
def stream_with_inv_delta():
2540
yield ('inventories', inventories_substream())
2541
yield ('inventory-deltas', inventory_delta_substream())
2543
versionedfile.FulltextContentFactory(
2544
('some-rev', 'some-file'), (), None, 'content')])
2545
def inventories_substream():
2546
# An empty inventory fulltext. This will be streamed normally.
2547
text = fmt._serializer.write_inventory_to_string(inv)
2548
yield versionedfile.FulltextContentFactory(
2549
('rev1',), (), None, text)
2550
def inventory_delta_substream():
2551
# An inventory delta. This can't be streamed via this verb, so it
2552
# will trigger a fallback to VFS insert_stream.
2553
entry = inv.make_entry(
2554
'directory', 'newdir', inv.root.file_id, 'newdir-id')
2555
entry.revision = 'ghost'
2556
delta = [(None, 'newdir', 'newdir-id', entry)]
2557
serializer = inventory_delta.InventoryDeltaSerializer(
2558
versioned_root=True, tree_references=False)
2559
lines = serializer.delta_to_lines('rev1', 'rev2', delta)
2560
yield versionedfile.ChunkedContentFactory(
2561
('rev2',), (('rev1',)), None, lines)
2563
lines = serializer.delta_to_lines('rev1', 'rev3', delta)
2564
yield versionedfile.ChunkedContentFactory(
2565
('rev3',), (('rev1',)), None, lines)
2566
return stream_with_inv_delta()
2569
class TestRepositoryInsertStream_1_19(TestRepositoryInsertStreamBase):
2571
def test_unlocked_repo(self):
2572
transport_path = 'quack'
2573
repo, client = self.setup_fake_client_and_repository(transport_path)
2574
client.add_expected_call(
2575
'Repository.insert_stream_1.19', ('quack/', ''),
2577
client.add_expected_call(
2578
'Repository.insert_stream_1.19', ('quack/', ''),
2580
self.checkInsertEmptyStream(repo, client)
2582
def test_locked_repo_with_no_lock_token(self):
2583
transport_path = 'quack'
2584
repo, client = self.setup_fake_client_and_repository(transport_path)
2585
client.add_expected_call(
2586
'Repository.lock_write', ('quack/', ''),
2587
'success', ('ok', ''))
2588
client.add_expected_call(
2589
'Repository.insert_stream_1.19', ('quack/', ''),
2591
client.add_expected_call(
2592
'Repository.insert_stream_1.19', ('quack/', ''),
2595
self.checkInsertEmptyStream(repo, client)
2597
def test_locked_repo_with_lock_token(self):
2598
transport_path = 'quack'
2599
repo, client = self.setup_fake_client_and_repository(transport_path)
2600
client.add_expected_call(
2601
'Repository.lock_write', ('quack/', ''),
2602
'success', ('ok', 'a token'))
2603
client.add_expected_call(
2604
'Repository.insert_stream_1.19', ('quack/', '', 'a token'),
2606
client.add_expected_call(
2607
'Repository.insert_stream_1.19', ('quack/', '', 'a token'),
2610
self.checkInsertEmptyStream(repo, client)
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()
2613
2200
class TestRepositoryTarball(TestRemoteRepository):