474
477
self.assertFinished(client)
480
class TestBzrDirOpen(TestRemote):
482
def make_fake_client_and_transport(self, path='quack'):
483
transport = MemoryTransport()
484
transport.mkdir(path)
485
transport = transport.clone(path)
486
client = FakeClient(transport.base)
487
return client, transport
489
def test_absent(self):
490
client, transport = self.make_fake_client_and_transport()
491
client.add_expected_call(
492
'BzrDir.open_2.1', ('quack/',), 'success', ('no',))
493
self.assertRaises(errors.NotBranchError, RemoteBzrDir, transport,
494
remote.RemoteBzrDirFormat(), _client=client, _force_probe=True)
495
self.assertFinished(client)
497
def test_present_without_workingtree(self):
498
client, transport = self.make_fake_client_and_transport()
499
client.add_expected_call(
500
'BzrDir.open_2.1', ('quack/',), 'success', ('yes', 'no'))
501
bd = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
502
_client=client, _force_probe=True)
503
self.assertIsInstance(bd, RemoteBzrDir)
504
self.assertFalse(bd.has_workingtree())
505
self.assertRaises(errors.NoWorkingTree, bd.open_workingtree)
506
self.assertFinished(client)
508
def test_present_with_workingtree(self):
509
client, transport = self.make_fake_client_and_transport()
510
client.add_expected_call(
511
'BzrDir.open_2.1', ('quack/',), 'success', ('yes', 'yes'))
512
bd = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
513
_client=client, _force_probe=True)
514
self.assertIsInstance(bd, RemoteBzrDir)
515
self.assertTrue(bd.has_workingtree())
516
self.assertRaises(errors.NotLocalUrl, bd.open_workingtree)
517
self.assertFinished(client)
519
def test_backwards_compat(self):
520
client, transport = self.make_fake_client_and_transport()
521
client.add_expected_call(
522
'BzrDir.open_2.1', ('quack/',), 'unknown', ('BzrDir.open_2.1',))
523
client.add_expected_call(
524
'BzrDir.open', ('quack/',), 'success', ('yes',))
525
bd = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(),
526
_client=client, _force_probe=True)
527
self.assertIsInstance(bd, RemoteBzrDir)
528
self.assertFinished(client)
477
531
class TestBzrDirOpenBranch(TestRemote):
479
533
def test_backwards_compat(self):
2219
2363
self.assertEqual([], client._calls)
2222
class TestRepositoryInsertStream(TestRemoteRepository):
2224
def test_unlocked_repo(self):
2225
transport_path = 'quack'
2226
repo, client = self.setup_fake_client_and_repository(transport_path)
2227
client.add_expected_call(
2228
'Repository.insert_stream', ('quack/', ''),
2230
client.add_expected_call(
2231
'Repository.insert_stream', ('quack/', ''),
2233
sink = repo._get_sink()
2234
fmt = repository.RepositoryFormat.get_default_format()
2235
resume_tokens, missing_keys = sink.insert_stream([], fmt, [])
2236
self.assertEqual([], resume_tokens)
2237
self.assertEqual(set(), missing_keys)
2238
self.assertFinished(client)
2240
def test_locked_repo_with_no_lock_token(self):
2241
transport_path = 'quack'
2242
repo, client = self.setup_fake_client_and_repository(transport_path)
2243
client.add_expected_call(
2244
'Repository.lock_write', ('quack/', ''),
2245
'success', ('ok', ''))
2246
client.add_expected_call(
2247
'Repository.insert_stream', ('quack/', ''),
2249
client.add_expected_call(
2250
'Repository.insert_stream', ('quack/', ''),
2253
sink = repo._get_sink()
2254
fmt = repository.RepositoryFormat.get_default_format()
2255
resume_tokens, missing_keys = sink.insert_stream([], fmt, [])
2256
self.assertEqual([], resume_tokens)
2257
self.assertEqual(set(), missing_keys)
2258
self.assertFinished(client)
2260
def test_locked_repo_with_lock_token(self):
2261
transport_path = 'quack'
2262
repo, client = self.setup_fake_client_and_repository(transport_path)
2263
client.add_expected_call(
2264
'Repository.lock_write', ('quack/', ''),
2265
'success', ('ok', 'a token'))
2266
client.add_expected_call(
2267
'Repository.insert_stream_locked', ('quack/', '', 'a token'),
2269
client.add_expected_call(
2270
'Repository.insert_stream_locked', ('quack/', '', 'a token'),
2273
sink = repo._get_sink()
2274
fmt = repository.RepositoryFormat.get_default_format()
2275
resume_tokens, missing_keys = sink.insert_stream([], fmt, [])
2276
self.assertEqual([], resume_tokens)
2277
self.assertEqual(set(), missing_keys)
2278
self.assertFinished(client)
2366
class TestRepositoryInsertStreamBase(TestRemoteRepository):
2367
"""Base class for Repository.insert_stream and .insert_stream_1.19
2371
def checkInsertEmptyStream(self, repo, client):
2372
"""Insert an empty stream, checking the result.
2374
This checks that there are no resume_tokens or missing_keys, and that
2375
the client is finished.
2377
sink = repo._get_sink()
2378
fmt = repository.RepositoryFormat.get_default_format()
2379
resume_tokens, missing_keys = sink.insert_stream([], fmt, [])
2380
self.assertEqual([], resume_tokens)
2381
self.assertEqual(set(), missing_keys)
2382
self.assertFinished(client)
2385
class TestRepositoryInsertStream(TestRepositoryInsertStreamBase):
2386
"""Tests for using Repository.insert_stream verb when the _1.19 variant is
2389
This test case is very similar to TestRepositoryInsertStream_1_19.
2393
TestRemoteRepository.setUp(self)
2394
self.disable_verb('Repository.insert_stream_1.19')
2396
def test_unlocked_repo(self):
2397
transport_path = 'quack'
2398
repo, client = self.setup_fake_client_and_repository(transport_path)
2399
client.add_expected_call(
2400
'Repository.insert_stream_1.19', ('quack/', ''),
2401
'unknown', ('Repository.insert_stream_1.19',))
2402
client.add_expected_call(
2403
'Repository.insert_stream', ('quack/', ''),
2405
client.add_expected_call(
2406
'Repository.insert_stream', ('quack/', ''),
2408
self.checkInsertEmptyStream(repo, client)
2410
def test_locked_repo_with_no_lock_token(self):
2411
transport_path = 'quack'
2412
repo, client = self.setup_fake_client_and_repository(transport_path)
2413
client.add_expected_call(
2414
'Repository.lock_write', ('quack/', ''),
2415
'success', ('ok', ''))
2416
client.add_expected_call(
2417
'Repository.insert_stream_1.19', ('quack/', ''),
2418
'unknown', ('Repository.insert_stream_1.19',))
2419
client.add_expected_call(
2420
'Repository.insert_stream', ('quack/', ''),
2422
client.add_expected_call(
2423
'Repository.insert_stream', ('quack/', ''),
2426
self.checkInsertEmptyStream(repo, client)
2428
def test_locked_repo_with_lock_token(self):
2429
transport_path = 'quack'
2430
repo, client = self.setup_fake_client_and_repository(transport_path)
2431
client.add_expected_call(
2432
'Repository.lock_write', ('quack/', ''),
2433
'success', ('ok', 'a token'))
2434
client.add_expected_call(
2435
'Repository.insert_stream_1.19', ('quack/', '', 'a token'),
2436
'unknown', ('Repository.insert_stream_1.19',))
2437
client.add_expected_call(
2438
'Repository.insert_stream_locked', ('quack/', '', 'a token'),
2440
client.add_expected_call(
2441
'Repository.insert_stream_locked', ('quack/', '', 'a token'),
2444
self.checkInsertEmptyStream(repo, client)
2446
def test_stream_with_inventory_deltas(self):
2447
"""'inventory-deltas' substreams cannot be sent to the
2448
Repository.insert_stream verb, because not all servers that implement
2449
that verb will accept them. So when one is encountered the RemoteSink
2450
immediately stops using that verb and falls back to VFS insert_stream.
2452
transport_path = 'quack'
2453
repo, client = self.setup_fake_client_and_repository(transport_path)
2454
client.add_expected_call(
2455
'Repository.insert_stream_1.19', ('quack/', ''),
2456
'unknown', ('Repository.insert_stream_1.19',))
2457
client.add_expected_call(
2458
'Repository.insert_stream', ('quack/', ''),
2460
client.add_expected_call(
2461
'Repository.insert_stream', ('quack/', ''),
2463
# Create a fake real repository for insert_stream to fall back on, so
2464
# that we can directly see the records the RemoteSink passes to the
2469
def insert_stream(self, stream, src_format, resume_tokens):
2470
for substream_kind, substream in stream:
2471
self.records.append(
2472
(substream_kind, [record.key for record in substream]))
2473
return ['fake tokens'], ['fake missing keys']
2474
fake_real_sink = FakeRealSink()
2475
class FakeRealRepository:
2476
def _get_sink(self):
2477
return fake_real_sink
2478
def is_in_write_group(self):
2480
def refresh_data(self):
2482
repo._real_repository = FakeRealRepository()
2483
sink = repo._get_sink()
2484
fmt = repository.RepositoryFormat.get_default_format()
2485
stream = self.make_stream_with_inv_deltas(fmt)
2486
resume_tokens, missing_keys = sink.insert_stream(stream, fmt, [])
2487
# Every record from the first inventory delta should have been sent to
2489
expected_records = [
2490
('inventory-deltas', [('rev2',), ('rev3',)]),
2491
('texts', [('some-rev', 'some-file')])]
2492
self.assertEqual(expected_records, fake_real_sink.records)
2493
# The return values from the real sink's insert_stream are propagated
2494
# back to the original caller.
2495
self.assertEqual(['fake tokens'], resume_tokens)
2496
self.assertEqual(['fake missing keys'], missing_keys)
2497
self.assertFinished(client)
2499
def make_stream_with_inv_deltas(self, fmt):
2500
"""Make a simple stream with an inventory delta followed by more
2501
records and more substreams to test that all records and substreams
2502
from that point on are used.
2504
This sends, in order:
2505
* inventories substream: rev1, rev2, rev3. rev2 and rev3 are
2507
* texts substream: (some-rev, some-file)
2509
# Define a stream using generators so that it isn't rewindable.
2510
inv = inventory.Inventory(revision_id='rev1')
2511
inv.root.revision = 'rev1'
2512
def stream_with_inv_delta():
2513
yield ('inventories', inventories_substream())
2514
yield ('inventory-deltas', inventory_delta_substream())
2516
versionedfile.FulltextContentFactory(
2517
('some-rev', 'some-file'), (), None, 'content')])
2518
def inventories_substream():
2519
# An empty inventory fulltext. This will be streamed normally.
2520
text = fmt._serializer.write_inventory_to_string(inv)
2521
yield versionedfile.FulltextContentFactory(
2522
('rev1',), (), None, text)
2523
def inventory_delta_substream():
2524
# An inventory delta. This can't be streamed via this verb, so it
2525
# will trigger a fallback to VFS insert_stream.
2526
entry = inv.make_entry(
2527
'directory', 'newdir', inv.root.file_id, 'newdir-id')
2528
entry.revision = 'ghost'
2529
delta = [(None, 'newdir', 'newdir-id', entry)]
2530
serializer = inventory_delta.InventoryDeltaSerializer(
2531
versioned_root=True, tree_references=False)
2532
lines = serializer.delta_to_lines('rev1', 'rev2', delta)
2533
yield versionedfile.ChunkedContentFactory(
2534
('rev2',), (('rev1',)), None, lines)
2536
lines = serializer.delta_to_lines('rev1', 'rev3', delta)
2537
yield versionedfile.ChunkedContentFactory(
2538
('rev3',), (('rev1',)), None, lines)
2539
return stream_with_inv_delta()
2542
class TestRepositoryInsertStream_1_19(TestRepositoryInsertStreamBase):
2544
def test_unlocked_repo(self):
2545
transport_path = 'quack'
2546
repo, client = self.setup_fake_client_and_repository(transport_path)
2547
client.add_expected_call(
2548
'Repository.insert_stream_1.19', ('quack/', ''),
2550
client.add_expected_call(
2551
'Repository.insert_stream_1.19', ('quack/', ''),
2553
self.checkInsertEmptyStream(repo, client)
2555
def test_locked_repo_with_no_lock_token(self):
2556
transport_path = 'quack'
2557
repo, client = self.setup_fake_client_and_repository(transport_path)
2558
client.add_expected_call(
2559
'Repository.lock_write', ('quack/', ''),
2560
'success', ('ok', ''))
2561
client.add_expected_call(
2562
'Repository.insert_stream_1.19', ('quack/', ''),
2564
client.add_expected_call(
2565
'Repository.insert_stream_1.19', ('quack/', ''),
2568
self.checkInsertEmptyStream(repo, client)
2570
def test_locked_repo_with_lock_token(self):
2571
transport_path = 'quack'
2572
repo, client = self.setup_fake_client_and_repository(transport_path)
2573
client.add_expected_call(
2574
'Repository.lock_write', ('quack/', ''),
2575
'success', ('ok', 'a token'))
2576
client.add_expected_call(
2577
'Repository.insert_stream_1.19', ('quack/', '', 'a token'),
2579
client.add_expected_call(
2580
'Repository.insert_stream_1.19', ('quack/', '', 'a token'),
2583
self.checkInsertEmptyStream(repo, client)
2281
2586
class TestRepositoryTarball(TestRemoteRepository):