57
51
RemoteRepositoryFormat,
59
from bzrlib.repofmt import groupcompress_repo, knitpack_repo
53
from bzrlib.repofmt import pack_repo
60
54
from bzrlib.revision import NULL_REVISION
61
from bzrlib.smart import medium, request
55
from bzrlib.smart import server, medium
62
56
from bzrlib.smart.client import _SmartClient
63
from bzrlib.smart.repository import (
64
SmartServerRepositoryGetParentMap,
65
SmartServerRepositoryGetStream_1_19,
57
from bzrlib.smart.repository import SmartServerRepositoryGetParentMap
67
58
from bzrlib.tests import (
60
split_suite_by_condition,
70
from bzrlib.tests.scenarios import load_tests_apply_scenarios
63
from bzrlib.transport import get_transport, http
71
64
from bzrlib.transport.memory import MemoryTransport
72
65
from bzrlib.transport.remote import (
74
67
RemoteSSHTransport,
75
68
RemoteTCPTransport,
79
load_tests = load_tests_apply_scenarios
82
class BasicRemoteObjectTests(tests.TestCaseWithTransport):
71
def load_tests(standard_tests, module, loader):
72
to_adapt, result = split_suite_by_condition(
73
standard_tests, condition_isinstance(BasicRemoteObjectTests))
74
smart_server_version_scenarios = [
86
{'transport_server': test_server.SmartTCPServer_for_testing_v2_only}),
76
{'transport_server': server.SmartTCPServer_for_testing_v2_only}),
88
{'transport_server': test_server.SmartTCPServer_for_testing})]
78
{'transport_server': server.SmartTCPServer_for_testing})]
79
return multiply_tests(to_adapt, smart_server_version_scenarios, result)
82
class BasicRemoteObjectTests(tests.TestCaseWithTransport):
92
85
super(BasicRemoteObjectTests, self).setUp()
93
86
self.transport = self.get_transport()
94
87
# make a branch that can be opened over the smart transport
95
88
self.local_wt = BzrDir.create_standalone_workingtree('.')
96
self.addCleanup(self.transport.disconnect)
91
self.transport.disconnect()
92
tests.TestCaseWithTransport.tearDown(self)
98
94
def test_create_remote_bzrdir(self):
99
b = remote.RemoteBzrDir(self.transport, RemoteBzrDirFormat())
95
b = remote.RemoteBzrDir(self.transport, remote.RemoteBzrDirFormat())
100
96
self.assertIsInstance(b, BzrDir)
102
98
def test_open_remote_branch(self):
103
99
# open a standalone branch in the working directory
104
b = remote.RemoteBzrDir(self.transport, RemoteBzrDirFormat())
100
b = remote.RemoteBzrDir(self.transport, remote.RemoteBzrDirFormat())
105
101
branch = b.open_branch()
106
102
self.assertIsInstance(branch, Branch)
479
450
self.assertEqual(bzrdir.BzrDirMetaFormat1, type(result))
480
451
self.assertEqual(None, result._repository_format)
481
452
self.assertEqual(None, result._branch_format)
482
self.assertFinished(client)
485
class TestBzrDirOpen(TestRemote):
487
def make_fake_client_and_transport(self, path='quack'):
488
transport = MemoryTransport()
489
transport.mkdir(path)
490
transport = transport.clone(path)
491
client = FakeClient(transport.base)
492
return client, transport
494
def test_absent(self):
495
client, transport = self.make_fake_client_and_transport()
496
client.add_expected_call(
497
'BzrDir.open_2.1', ('quack/',), 'success', ('no',))
498
self.assertRaises(errors.NotBranchError, RemoteBzrDir, transport,
499
RemoteBzrDirFormat(), _client=client, _force_probe=True)
500
self.assertFinished(client)
502
def test_present_without_workingtree(self):
503
client, transport = self.make_fake_client_and_transport()
504
client.add_expected_call(
505
'BzrDir.open_2.1', ('quack/',), 'success', ('yes', 'no'))
506
bd = RemoteBzrDir(transport, RemoteBzrDirFormat(),
507
_client=client, _force_probe=True)
508
self.assertIsInstance(bd, RemoteBzrDir)
509
self.assertFalse(bd.has_workingtree())
510
self.assertRaises(errors.NoWorkingTree, bd.open_workingtree)
511
self.assertFinished(client)
513
def test_present_with_workingtree(self):
514
client, transport = self.make_fake_client_and_transport()
515
client.add_expected_call(
516
'BzrDir.open_2.1', ('quack/',), 'success', ('yes', 'yes'))
517
bd = RemoteBzrDir(transport, RemoteBzrDirFormat(),
518
_client=client, _force_probe=True)
519
self.assertIsInstance(bd, RemoteBzrDir)
520
self.assertTrue(bd.has_workingtree())
521
self.assertRaises(errors.NotLocalUrl, bd.open_workingtree)
522
self.assertFinished(client)
524
def test_backwards_compat(self):
525
client, transport = self.make_fake_client_and_transport()
526
client.add_expected_call(
527
'BzrDir.open_2.1', ('quack/',), 'unknown', ('BzrDir.open_2.1',))
528
client.add_expected_call(
529
'BzrDir.open', ('quack/',), 'success', ('yes',))
530
bd = RemoteBzrDir(transport, RemoteBzrDirFormat(),
531
_client=client, _force_probe=True)
532
self.assertIsInstance(bd, RemoteBzrDir)
533
self.assertFinished(client)
535
def test_backwards_compat_hpss_v2(self):
536
client, transport = self.make_fake_client_and_transport()
537
# Monkey-patch fake client to simulate real-world behaviour with v2
538
# server: upon first RPC call detect the protocol version, and because
539
# the version is 2 also do _remember_remote_is_before((1, 6)) before
540
# continuing with the RPC.
541
orig_check_call = client._check_call
542
def check_call(method, args):
543
client._medium._protocol_version = 2
544
client._medium._remember_remote_is_before((1, 6))
545
client._check_call = orig_check_call
546
client._check_call(method, args)
547
client._check_call = check_call
548
client.add_expected_call(
549
'BzrDir.open_2.1', ('quack/',), 'unknown', ('BzrDir.open_2.1',))
550
client.add_expected_call(
551
'BzrDir.open', ('quack/',), 'success', ('yes',))
552
bd = RemoteBzrDir(transport, RemoteBzrDirFormat(),
553
_client=client, _force_probe=True)
554
self.assertIsInstance(bd, RemoteBzrDir)
555
self.assertFinished(client)
453
client.finished_test()
558
456
class TestBzrDirOpenBranch(TestRemote):
877
742
self.assertEqual(network_name, repo._format.network_name())
880
class TestBzrDirFormatInitializeEx(TestRemote):
882
def test_success(self):
883
"""Simple test for typical successful call."""
884
fmt = RemoteBzrDirFormat()
885
default_format_name = BzrDirFormat.get_default_format().network_name()
886
transport = self.get_transport()
887
client = FakeClient(transport.base)
888
client.add_expected_call(
889
'BzrDirFormat.initialize_ex_1.16',
890
(default_format_name, 'path', 'False', 'False', 'False', '',
891
'', '', '', 'False'),
893
('.', 'no', 'no', 'yes', 'repo fmt', 'repo bzrdir fmt',
894
'bzrdir fmt', 'False', '', '', 'repo lock token'))
895
# XXX: It would be better to call fmt.initialize_on_transport_ex, but
896
# it's currently hard to test that without supplying a real remote
897
# transport connected to a real server.
898
result = fmt._initialize_on_transport_ex_rpc(client, 'path',
899
transport, False, False, False, None, None, None, None, False)
900
self.assertFinished(client)
902
def test_error(self):
903
"""Error responses are translated, e.g. 'PermissionDenied' raises the
904
corresponding error from the client.
906
fmt = RemoteBzrDirFormat()
907
default_format_name = BzrDirFormat.get_default_format().network_name()
908
transport = self.get_transport()
909
client = FakeClient(transport.base)
910
client.add_expected_call(
911
'BzrDirFormat.initialize_ex_1.16',
912
(default_format_name, 'path', 'False', 'False', 'False', '',
913
'', '', '', 'False'),
915
('PermissionDenied', 'path', 'extra info'))
916
# XXX: It would be better to call fmt.initialize_on_transport_ex, but
917
# it's currently hard to test that without supplying a real remote
918
# transport connected to a real server.
919
err = self.assertRaises(errors.PermissionDenied,
920
fmt._initialize_on_transport_ex_rpc, client, 'path', transport,
921
False, False, False, None, None, None, None, False)
922
self.assertEqual('path', err.path)
923
self.assertEqual(': extra info', err.extra)
924
self.assertFinished(client)
926
def test_error_from_real_server(self):
927
"""Integration test for error translation."""
928
transport = self.make_smart_server('foo')
929
transport = transport.clone('no-such-path')
930
fmt = RemoteBzrDirFormat()
931
err = self.assertRaises(errors.NoSuchFile,
932
fmt.initialize_on_transport_ex, transport, create_prefix=False)
935
745
class OldSmartClient(object):
936
746
"""A fake smart client for test_old_version that just returns a version one
937
747
response to the 'hello' (query version) command.
1120
865
transport = transport.clone('quack')
1121
866
branch = self.make_remote_branch(transport, client)
1122
867
result = branch.tags.get_tag_dict()
1123
self.assertFinished(client)
868
client.finished_test()
1124
869
self.assertEqual({}, result)
1127
class TestBranchSetTagsBytes(RemoteBranchTestCase):
1129
def test_trivial(self):
1130
transport = MemoryTransport()
1131
client = FakeClient(transport.base)
1132
client.add_expected_call(
1133
'Branch.get_stacked_on_url', ('quack/',),
1134
'error', ('NotStacked',))
1135
client.add_expected_call(
1136
'Branch.set_tags_bytes', ('quack/', 'branch token', 'repo token'),
1138
transport.mkdir('quack')
1139
transport = transport.clone('quack')
1140
branch = self.make_remote_branch(transport, client)
1141
self.lock_remote_branch(branch)
1142
branch._set_tags_bytes('tags bytes')
1143
self.assertFinished(client)
1144
self.assertEqual('tags bytes', client._calls[-1][-1])
1146
def test_backwards_compatible(self):
1147
transport = MemoryTransport()
1148
client = FakeClient(transport.base)
1149
client.add_expected_call(
1150
'Branch.get_stacked_on_url', ('quack/',),
1151
'error', ('NotStacked',))
1152
client.add_expected_call(
1153
'Branch.set_tags_bytes', ('quack/', 'branch token', 'repo token'),
1154
'unknown', ('Branch.set_tags_bytes',))
1155
transport.mkdir('quack')
1156
transport = transport.clone('quack')
1157
branch = self.make_remote_branch(transport, client)
1158
self.lock_remote_branch(branch)
1159
class StubRealBranch(object):
1162
def _set_tags_bytes(self, bytes):
1163
self.calls.append(('set_tags_bytes', bytes))
1164
real_branch = StubRealBranch()
1165
branch._real_branch = real_branch
1166
branch._set_tags_bytes('tags bytes')
1167
# Call a second time, to exercise the 'remote version already inferred'
1169
branch._set_tags_bytes('tags bytes')
1170
self.assertFinished(client)
1172
[('set_tags_bytes', 'tags bytes')] * 2, real_branch.calls)
1175
class TestBranchHeadsToFetch(RemoteBranchTestCase):
1177
def test_uses_last_revision_info_and_tags_by_default(self):
1178
transport = MemoryTransport()
1179
client = FakeClient(transport.base)
1180
client.add_expected_call(
1181
'Branch.get_stacked_on_url', ('quack/',),
1182
'error', ('NotStacked',))
1183
client.add_expected_call(
1184
'Branch.last_revision_info', ('quack/',),
1185
'success', ('ok', '1', 'rev-tip'))
1186
# XXX: this will break if the default format's serialization of tags
1187
# changes, or if the RPC for fetching tags changes from get_tags_bytes.
1188
client.add_expected_call(
1189
'Branch.get_tags_bytes', ('quack/',),
1190
'success', ('d5:tag-17:rev-foo5:tag-27:rev-bare',))
1191
transport.mkdir('quack')
1192
transport = transport.clone('quack')
1193
branch = self.make_remote_branch(transport, client)
1194
result = branch.heads_to_fetch()
1195
self.assertFinished(client)
1197
(set(['rev-tip']), set(['rev-foo', 'rev-bar'])), result)
1199
def test_uses_rpc_for_formats_with_non_default_heads_to_fetch(self):
1200
transport = MemoryTransport()
1201
client = FakeClient(transport.base)
1202
client.add_expected_call(
1203
'Branch.get_stacked_on_url', ('quack/',),
1204
'error', ('NotStacked',))
1205
client.add_expected_call(
1206
'Branch.heads_to_fetch', ('quack/',),
1207
'success', (['tip'], ['tagged-1', 'tagged-2']))
1208
transport.mkdir('quack')
1209
transport = transport.clone('quack')
1210
branch = self.make_remote_branch(transport, client)
1211
branch._format._use_default_local_heads_to_fetch = lambda: False
1212
result = branch.heads_to_fetch()
1213
self.assertFinished(client)
1214
self.assertEqual((set(['tip']), set(['tagged-1', 'tagged-2'])), result)
1216
def test_backwards_compatible(self):
1217
self.setup_smart_server_with_call_log()
1218
# Make a branch with a single revision.
1219
builder = self.make_branch_builder('foo')
1220
builder.start_series()
1221
builder.build_snapshot('tip', None, [
1222
('add', ('', 'root-id', 'directory', ''))])
1223
builder.finish_series()
1224
branch = builder.get_branch()
1225
# Add two tags to that branch
1226
branch.tags.set_tag('tag-1', 'rev-1')
1227
branch.tags.set_tag('tag-2', 'rev-2')
1228
self.addCleanup(branch.lock_read().unlock)
1229
# Disable the heads_to_fetch verb
1230
verb = 'Branch.heads_to_fetch'
1231
self.disable_verb(verb)
1232
self.reset_smart_call_log()
1233
result = branch.heads_to_fetch()
1234
self.assertEqual((set(['tip']), set(['rev-1', 'rev-2'])), result)
1236
['Branch.last_revision_info', 'Branch.get_tags_bytes'],
1237
[call.call.method for call in self.hpss_calls])
1240
872
class TestBranchLastRevisionInfo(RemoteBranchTestCase):
1242
874
def test_empty_branch(self):
2325
1877
self.assertEqual(('AnUnexpectedError',), e.error_tuple)
2328
class TestRepositoryGetRevIdForRevno(TestRemoteRepository):
2331
repo, client = self.setup_fake_client_and_repository('quack')
2332
client.add_expected_call(
2333
'Repository.get_rev_id_for_revno', ('quack/', 5, (42, 'rev-foo')),
2334
'success', ('ok', 'rev-five'))
2335
result = repo.get_rev_id_for_revno(5, (42, 'rev-foo'))
2336
self.assertEqual((True, 'rev-five'), result)
2337
self.assertFinished(client)
2339
def test_history_incomplete(self):
2340
repo, client = self.setup_fake_client_and_repository('quack')
2341
client.add_expected_call(
2342
'Repository.get_rev_id_for_revno', ('quack/', 5, (42, 'rev-foo')),
2343
'success', ('history-incomplete', 10, 'rev-ten'))
2344
result = repo.get_rev_id_for_revno(5, (42, 'rev-foo'))
2345
self.assertEqual((False, (10, 'rev-ten')), result)
2346
self.assertFinished(client)
2348
def test_history_incomplete_with_fallback(self):
2349
"""A 'history-incomplete' response causes the fallback repository to be
2350
queried too, if one is set.
2352
# Make a repo with a fallback repo, both using a FakeClient.
2353
format = remote.response_tuple_to_repo_format(
2354
('yes', 'no', 'yes', self.get_repo_format().network_name()))
2355
repo, client = self.setup_fake_client_and_repository('quack')
2356
repo._format = format
2357
fallback_repo, ignored = self.setup_fake_client_and_repository(
2359
fallback_repo._client = client
2360
fallback_repo._format = format
2361
repo.add_fallback_repository(fallback_repo)
2362
# First the client should ask the primary repo
2363
client.add_expected_call(
2364
'Repository.get_rev_id_for_revno', ('quack/', 1, (42, 'rev-foo')),
2365
'success', ('history-incomplete', 2, 'rev-two'))
2366
# Then it should ask the fallback, using revno/revid from the
2367
# history-incomplete response as the known revno/revid.
2368
client.add_expected_call(
2369
'Repository.get_rev_id_for_revno',('fallback/', 1, (2, 'rev-two')),
2370
'success', ('ok', 'rev-one'))
2371
result = repo.get_rev_id_for_revno(1, (42, 'rev-foo'))
2372
self.assertEqual((True, 'rev-one'), result)
2373
self.assertFinished(client)
2375
def test_nosuchrevision(self):
2376
# 'nosuchrevision' is returned when the known-revid is not found in the
2377
# remote repo. The client translates that response to NoSuchRevision.
2378
repo, client = self.setup_fake_client_and_repository('quack')
2379
client.add_expected_call(
2380
'Repository.get_rev_id_for_revno', ('quack/', 5, (42, 'rev-foo')),
2381
'error', ('nosuchrevision', 'rev-foo'))
2383
errors.NoSuchRevision,
2384
repo.get_rev_id_for_revno, 5, (42, 'rev-foo'))
2385
self.assertFinished(client)
2387
def test_branch_fallback_locking(self):
2388
"""RemoteBranch.get_rev_id takes a read lock, and tries to call the
2389
get_rev_id_for_revno verb. If the verb is unknown the VFS fallback
2390
will be invoked, which will fail if the repo is unlocked.
2392
self.setup_smart_server_with_call_log()
2393
tree = self.make_branch_and_memory_tree('.')
2396
rev1 = tree.commit('First')
2397
rev2 = tree.commit('Second')
2399
branch = tree.branch
2400
self.assertFalse(branch.is_locked())
2401
self.reset_smart_call_log()
2402
verb = 'Repository.get_rev_id_for_revno'
2403
self.disable_verb(verb)
2404
self.assertEqual(rev1, branch.get_rev_id(1))
2405
self.assertLength(1, [call for call in self.hpss_calls if
2406
call.call.method == verb])
2409
1880
class TestRepositoryIsShared(TestRemoteRepository):
2411
1882
def test_is_shared(self):
2526
1997
self.assertEqual([], client._calls)
2529
class TestRepositoryInsertStreamBase(TestRemoteRepository):
2530
"""Base class for Repository.insert_stream and .insert_stream_1.19
2534
def checkInsertEmptyStream(self, repo, client):
2535
"""Insert an empty stream, checking the result.
2537
This checks that there are no resume_tokens or missing_keys, and that
2538
the client is finished.
2540
sink = repo._get_sink()
2541
fmt = repository.format_registry.get_default()
2542
resume_tokens, missing_keys = sink.insert_stream([], fmt, [])
2543
self.assertEqual([], resume_tokens)
2544
self.assertEqual(set(), missing_keys)
2545
self.assertFinished(client)
2548
class TestRepositoryInsertStream(TestRepositoryInsertStreamBase):
2549
"""Tests for using Repository.insert_stream verb when the _1.19 variant is
2552
This test case is very similar to TestRepositoryInsertStream_1_19.
2556
TestRemoteRepository.setUp(self)
2557
self.disable_verb('Repository.insert_stream_1.19')
2559
def test_unlocked_repo(self):
2560
transport_path = 'quack'
2561
repo, client = self.setup_fake_client_and_repository(transport_path)
2562
client.add_expected_call(
2563
'Repository.insert_stream_1.19', ('quack/', ''),
2564
'unknown', ('Repository.insert_stream_1.19',))
2565
client.add_expected_call(
2566
'Repository.insert_stream', ('quack/', ''),
2568
client.add_expected_call(
2569
'Repository.insert_stream', ('quack/', ''),
2571
self.checkInsertEmptyStream(repo, client)
2573
def test_locked_repo_with_no_lock_token(self):
2574
transport_path = 'quack'
2575
repo, client = self.setup_fake_client_and_repository(transport_path)
2576
client.add_expected_call(
2577
'Repository.lock_write', ('quack/', ''),
2578
'success', ('ok', ''))
2579
client.add_expected_call(
2580
'Repository.insert_stream_1.19', ('quack/', ''),
2581
'unknown', ('Repository.insert_stream_1.19',))
2582
client.add_expected_call(
2583
'Repository.insert_stream', ('quack/', ''),
2585
client.add_expected_call(
2586
'Repository.insert_stream', ('quack/', ''),
2589
self.checkInsertEmptyStream(repo, client)
2591
def test_locked_repo_with_lock_token(self):
2592
transport_path = 'quack'
2593
repo, client = self.setup_fake_client_and_repository(transport_path)
2594
client.add_expected_call(
2595
'Repository.lock_write', ('quack/', ''),
2596
'success', ('ok', 'a token'))
2597
client.add_expected_call(
2598
'Repository.insert_stream_1.19', ('quack/', '', 'a token'),
2599
'unknown', ('Repository.insert_stream_1.19',))
2600
client.add_expected_call(
2601
'Repository.insert_stream_locked', ('quack/', '', 'a token'),
2603
client.add_expected_call(
2604
'Repository.insert_stream_locked', ('quack/', '', 'a token'),
2607
self.checkInsertEmptyStream(repo, client)
2609
def test_stream_with_inventory_deltas(self):
2610
"""'inventory-deltas' substreams cannot be sent to the
2611
Repository.insert_stream verb, because not all servers that implement
2612
that verb will accept them. So when one is encountered the RemoteSink
2613
immediately stops using that verb and falls back to VFS insert_stream.
2615
transport_path = 'quack'
2616
repo, client = self.setup_fake_client_and_repository(transport_path)
2617
client.add_expected_call(
2618
'Repository.insert_stream_1.19', ('quack/', ''),
2619
'unknown', ('Repository.insert_stream_1.19',))
2620
client.add_expected_call(
2621
'Repository.insert_stream', ('quack/', ''),
2623
client.add_expected_call(
2624
'Repository.insert_stream', ('quack/', ''),
2626
# Create a fake real repository for insert_stream to fall back on, so
2627
# that we can directly see the records the RemoteSink passes to the
2632
def insert_stream(self, stream, src_format, resume_tokens):
2633
for substream_kind, substream in stream:
2634
self.records.append(
2635
(substream_kind, [record.key for record in substream]))
2636
return ['fake tokens'], ['fake missing keys']
2637
fake_real_sink = FakeRealSink()
2638
class FakeRealRepository:
2639
def _get_sink(self):
2640
return fake_real_sink
2641
def is_in_write_group(self):
2643
def refresh_data(self):
2645
repo._real_repository = FakeRealRepository()
2646
sink = repo._get_sink()
2647
fmt = repository.format_registry.get_default()
2648
stream = self.make_stream_with_inv_deltas(fmt)
2649
resume_tokens, missing_keys = sink.insert_stream(stream, fmt, [])
2650
# Every record from the first inventory delta should have been sent to
2652
expected_records = [
2653
('inventory-deltas', [('rev2',), ('rev3',)]),
2654
('texts', [('some-rev', 'some-file')])]
2655
self.assertEqual(expected_records, fake_real_sink.records)
2656
# The return values from the real sink's insert_stream are propagated
2657
# back to the original caller.
2658
self.assertEqual(['fake tokens'], resume_tokens)
2659
self.assertEqual(['fake missing keys'], missing_keys)
2660
self.assertFinished(client)
2662
def make_stream_with_inv_deltas(self, fmt):
2663
"""Make a simple stream with an inventory delta followed by more
2664
records and more substreams to test that all records and substreams
2665
from that point on are used.
2667
This sends, in order:
2668
* inventories substream: rev1, rev2, rev3. rev2 and rev3 are
2670
* texts substream: (some-rev, some-file)
2672
# Define a stream using generators so that it isn't rewindable.
2673
inv = inventory.Inventory(revision_id='rev1')
2674
inv.root.revision = 'rev1'
2675
def stream_with_inv_delta():
2676
yield ('inventories', inventories_substream())
2677
yield ('inventory-deltas', inventory_delta_substream())
2679
versionedfile.FulltextContentFactory(
2680
('some-rev', 'some-file'), (), None, 'content')])
2681
def inventories_substream():
2682
# An empty inventory fulltext. This will be streamed normally.
2683
text = fmt._serializer.write_inventory_to_string(inv)
2684
yield versionedfile.FulltextContentFactory(
2685
('rev1',), (), None, text)
2686
def inventory_delta_substream():
2687
# An inventory delta. This can't be streamed via this verb, so it
2688
# will trigger a fallback to VFS insert_stream.
2689
entry = inv.make_entry(
2690
'directory', 'newdir', inv.root.file_id, 'newdir-id')
2691
entry.revision = 'ghost'
2692
delta = [(None, 'newdir', 'newdir-id', entry)]
2693
serializer = inventory_delta.InventoryDeltaSerializer(
2694
versioned_root=True, tree_references=False)
2695
lines = serializer.delta_to_lines('rev1', 'rev2', delta)
2696
yield versionedfile.ChunkedContentFactory(
2697
('rev2',), (('rev1',)), None, lines)
2699
lines = serializer.delta_to_lines('rev1', 'rev3', delta)
2700
yield versionedfile.ChunkedContentFactory(
2701
('rev3',), (('rev1',)), None, lines)
2702
return stream_with_inv_delta()
2705
class TestRepositoryInsertStream_1_19(TestRepositoryInsertStreamBase):
2707
def test_unlocked_repo(self):
2708
transport_path = 'quack'
2709
repo, client = self.setup_fake_client_and_repository(transport_path)
2710
client.add_expected_call(
2711
'Repository.insert_stream_1.19', ('quack/', ''),
2713
client.add_expected_call(
2714
'Repository.insert_stream_1.19', ('quack/', ''),
2716
self.checkInsertEmptyStream(repo, client)
2718
def test_locked_repo_with_no_lock_token(self):
2719
transport_path = 'quack'
2720
repo, client = self.setup_fake_client_and_repository(transport_path)
2721
client.add_expected_call(
2722
'Repository.lock_write', ('quack/', ''),
2723
'success', ('ok', ''))
2724
client.add_expected_call(
2725
'Repository.insert_stream_1.19', ('quack/', ''),
2727
client.add_expected_call(
2728
'Repository.insert_stream_1.19', ('quack/', ''),
2731
self.checkInsertEmptyStream(repo, client)
2733
def test_locked_repo_with_lock_token(self):
2734
transport_path = 'quack'
2735
repo, client = self.setup_fake_client_and_repository(transport_path)
2736
client.add_expected_call(
2737
'Repository.lock_write', ('quack/', ''),
2738
'success', ('ok', 'a token'))
2739
client.add_expected_call(
2740
'Repository.insert_stream_1.19', ('quack/', '', 'a token'),
2742
client.add_expected_call(
2743
'Repository.insert_stream_1.19', ('quack/', '', 'a token'),
2746
self.checkInsertEmptyStream(repo, client)
2000
class TestRepositoryInsertStream(TestRemoteRepository):
2002
def test_unlocked_repo(self):
2003
transport_path = 'quack'
2004
repo, client = self.setup_fake_client_and_repository(transport_path)
2005
client.add_expected_call(
2006
'Repository.insert_stream', ('quack/', ''),
2008
client.add_expected_call(
2009
'Repository.insert_stream', ('quack/', ''),
2011
sink = repo._get_sink()
2012
fmt = repository.RepositoryFormat.get_default_format()
2013
resume_tokens, missing_keys = sink.insert_stream([], fmt, [])
2014
self.assertEqual([], resume_tokens)
2015
self.assertEqual(set(), missing_keys)
2016
client.finished_test()
2018
def test_locked_repo_with_no_lock_token(self):
2019
transport_path = 'quack'
2020
repo, client = self.setup_fake_client_and_repository(transport_path)
2021
client.add_expected_call(
2022
'Repository.lock_write', ('quack/', ''),
2023
'success', ('ok', ''))
2024
client.add_expected_call(
2025
'Repository.insert_stream', ('quack/', ''),
2027
client.add_expected_call(
2028
'Repository.insert_stream', ('quack/', ''),
2031
sink = repo._get_sink()
2032
fmt = repository.RepositoryFormat.get_default_format()
2033
resume_tokens, missing_keys = sink.insert_stream([], fmt, [])
2034
self.assertEqual([], resume_tokens)
2035
self.assertEqual(set(), missing_keys)
2036
client.finished_test()
2038
def test_locked_repo_with_lock_token(self):
2039
transport_path = 'quack'
2040
repo, client = self.setup_fake_client_and_repository(transport_path)
2041
client.add_expected_call(
2042
'Repository.lock_write', ('quack/', ''),
2043
'success', ('ok', 'a token'))
2044
client.add_expected_call(
2045
'Repository.insert_stream_locked', ('quack/', '', 'a token'),
2047
client.add_expected_call(
2048
'Repository.insert_stream_locked', ('quack/', '', 'a token'),
2051
sink = repo._get_sink()
2052
fmt = repository.RepositoryFormat.get_default_format()
2053
resume_tokens, missing_keys = sink.insert_stream([], fmt, [])
2054
self.assertEqual([], resume_tokens)
2055
self.assertEqual(set(), missing_keys)
2056
client.finished_test()
2749
2059
class TestRepositoryTarball(TestRemoteRepository):
3344
2535
def test_copy_content_into_avoids_revision_history(self):
3345
2536
local = self.make_branch('local')
3346
builder = self.make_branch_builder('remote')
3347
builder.build_commit(message="Commit.")
2537
remote_backing_tree = self.make_branch_and_tree('remote')
2538
remote_backing_tree.commit("Commit.")
3348
2539
remote_branch_url = self.smart_server.get_url() + 'remote'
3349
2540
remote_branch = bzrdir.BzrDir.open(remote_branch_url).open_branch()
3350
2541
local.repository.fetch(remote_branch.repository)
3351
2542
self.hpss_calls = []
3352
2543
remote_branch.copy_content_into(local)
3353
2544
self.assertFalse('Branch.revision_history' in self.hpss_calls)
3355
def test_fetch_everything_needs_just_one_call(self):
3356
local = self.make_branch('local')
3357
builder = self.make_branch_builder('remote')
3358
builder.build_commit(message="Commit.")
3359
remote_branch_url = self.smart_server.get_url() + 'remote'
3360
remote_branch = bzrdir.BzrDir.open(remote_branch_url).open_branch()
3361
self.hpss_calls = []
3362
local.repository.fetch(remote_branch.repository,
3363
fetch_spec=_mod_graph.EverythingResult(remote_branch.repository))
3364
self.assertEqual(['Repository.get_stream_1.19'], self.hpss_calls)
3366
def override_verb(self, verb_name, verb):
3367
request_handlers = request.request_handlers
3368
orig_verb = request_handlers.get(verb_name)
3369
request_handlers.register(verb_name, verb, override_existing=True)
3370
self.addCleanup(request_handlers.register, verb_name, orig_verb,
3371
override_existing=True)
3373
def test_fetch_everything_backwards_compat(self):
3374
"""Can fetch with EverythingResult even with pre 2.4 servers.
3376
Pre-2.4 do not support 'everything' searches with the
3377
Repository.get_stream_1.19 verb.
3380
class OldGetStreamVerb(SmartServerRepositoryGetStream_1_19):
3381
"""A version of the Repository.get_stream_1.19 verb patched to
3382
reject 'everything' searches the way 2.3 and earlier do.
3384
def recreate_search(self, repository, search_bytes, discard_excess=False):
3385
verb_log.append(search_bytes.split('\n', 1)[0])
3386
if search_bytes == 'everything':
3387
return (None, request.FailedSmartServerResponse(('BadSearch',)))
3388
return super(OldGetStreamVerb,
3389
self).recreate_search(repository, search_bytes,
3390
discard_excess=discard_excess)
3391
self.override_verb('Repository.get_stream_1.19', OldGetStreamVerb)
3392
local = self.make_branch('local')
3393
builder = self.make_branch_builder('remote')
3394
builder.build_commit(message="Commit.")
3395
remote_branch_url = self.smart_server.get_url() + 'remote'
3396
remote_branch = bzrdir.BzrDir.open(remote_branch_url).open_branch()
3397
self.hpss_calls = []
3398
local.repository.fetch(remote_branch.repository,
3399
fetch_spec=_mod_graph.EverythingResult(remote_branch.repository))
3400
# make sure the overridden verb was used
3401
self.assertLength(1, verb_log)
3402
# more than one HPSS call is needed, but because it's a VFS callback
3403
# its hard to predict exactly how many.
3404
self.assertTrue(len(self.hpss_calls) > 1)