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
2364
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)
2367
class TestRepositoryInsertStreamBase(TestRemoteRepository):
2368
"""Base class for Repository.insert_stream and .insert_stream_1.19
2372
def checkInsertEmptyStream(self, repo, client):
2373
"""Insert an empty stream, checking the result.
2375
This checks that there are no resume_tokens or missing_keys, and that
2376
the client is finished.
2378
sink = repo._get_sink()
2379
fmt = repository.RepositoryFormat.get_default_format()
2380
resume_tokens, missing_keys = sink.insert_stream([], fmt, [])
2381
self.assertEqual([], resume_tokens)
2382
self.assertEqual(set(), missing_keys)
2383
self.assertFinished(client)
2386
class TestRepositoryInsertStream(TestRepositoryInsertStreamBase):
2387
"""Tests for using Repository.insert_stream verb when the _1.19 variant is
2390
This test case is very similar to TestRepositoryInsertStream_1_19.
2394
TestRemoteRepository.setUp(self)
2395
self.disable_verb('Repository.insert_stream_1.19')
2397
def test_unlocked_repo(self):
2398
transport_path = 'quack'
2399
repo, client = self.setup_fake_client_and_repository(transport_path)
2400
client.add_expected_call(
2401
'Repository.insert_stream_1.19', ('quack/', ''),
2402
'unknown', ('Repository.insert_stream_1.19',))
2403
client.add_expected_call(
2404
'Repository.insert_stream', ('quack/', ''),
2406
client.add_expected_call(
2407
'Repository.insert_stream', ('quack/', ''),
2409
self.checkInsertEmptyStream(repo, client)
2411
def test_locked_repo_with_no_lock_token(self):
2412
transport_path = 'quack'
2413
repo, client = self.setup_fake_client_and_repository(transport_path)
2414
client.add_expected_call(
2415
'Repository.lock_write', ('quack/', ''),
2416
'success', ('ok', ''))
2417
client.add_expected_call(
2418
'Repository.insert_stream_1.19', ('quack/', ''),
2419
'unknown', ('Repository.insert_stream_1.19',))
2420
client.add_expected_call(
2421
'Repository.insert_stream', ('quack/', ''),
2423
client.add_expected_call(
2424
'Repository.insert_stream', ('quack/', ''),
2427
self.checkInsertEmptyStream(repo, client)
2429
def test_locked_repo_with_lock_token(self):
2430
transport_path = 'quack'
2431
repo, client = self.setup_fake_client_and_repository(transport_path)
2432
client.add_expected_call(
2433
'Repository.lock_write', ('quack/', ''),
2434
'success', ('ok', 'a token'))
2435
client.add_expected_call(
2436
'Repository.insert_stream_1.19', ('quack/', '', 'a token'),
2437
'unknown', ('Repository.insert_stream_1.19',))
2438
client.add_expected_call(
2439
'Repository.insert_stream_locked', ('quack/', '', 'a token'),
2441
client.add_expected_call(
2442
'Repository.insert_stream_locked', ('quack/', '', 'a token'),
2445
self.checkInsertEmptyStream(repo, client)
2447
def test_stream_with_inventory_deltas(self):
2448
"""'inventory-deltas' substreams cannot be sent to the
2449
Repository.insert_stream verb, because not all servers that implement
2450
that verb will accept them. So when one is encountered the RemoteSink
2451
immediately stops using that verb and falls back to VFS insert_stream.
2453
transport_path = 'quack'
2454
repo, client = self.setup_fake_client_and_repository(transport_path)
2455
client.add_expected_call(
2456
'Repository.insert_stream_1.19', ('quack/', ''),
2457
'unknown', ('Repository.insert_stream_1.19',))
2458
client.add_expected_call(
2459
'Repository.insert_stream', ('quack/', ''),
2461
client.add_expected_call(
2462
'Repository.insert_stream', ('quack/', ''),
2464
# Create a fake real repository for insert_stream to fall back on, so
2465
# that we can directly see the records the RemoteSink passes to the
2470
def insert_stream(self, stream, src_format, resume_tokens):
2471
for substream_kind, substream in stream:
2472
self.records.append(
2473
(substream_kind, [record.key for record in substream]))
2474
return ['fake tokens'], ['fake missing keys']
2475
fake_real_sink = FakeRealSink()
2476
class FakeRealRepository:
2477
def _get_sink(self):
2478
return fake_real_sink
2479
def is_in_write_group(self):
2481
def refresh_data(self):
2483
repo._real_repository = FakeRealRepository()
2484
sink = repo._get_sink()
2485
fmt = repository.RepositoryFormat.get_default_format()
2486
stream = self.make_stream_with_inv_deltas(fmt)
2487
resume_tokens, missing_keys = sink.insert_stream(stream, fmt, [])
2488
# Every record from the first inventory delta should have been sent to
2490
expected_records = [
2491
('inventory-deltas', [('rev2',), ('rev3',)]),
2492
('texts', [('some-rev', 'some-file')])]
2493
self.assertEqual(expected_records, fake_real_sink.records)
2494
# The return values from the real sink's insert_stream are propagated
2495
# back to the original caller.
2496
self.assertEqual(['fake tokens'], resume_tokens)
2497
self.assertEqual(['fake missing keys'], missing_keys)
2498
self.assertFinished(client)
2500
def make_stream_with_inv_deltas(self, fmt):
2501
"""Make a simple stream with an inventory delta followed by more
2502
records and more substreams to test that all records and substreams
2503
from that point on are used.
2505
This sends, in order:
2506
* inventories substream: rev1, rev2, rev3. rev2 and rev3 are
2508
* texts substream: (some-rev, some-file)
2510
# Define a stream using generators so that it isn't rewindable.
2511
inv = inventory.Inventory(revision_id='rev1')
2512
inv.root.revision = 'rev1'
2513
def stream_with_inv_delta():
2514
yield ('inventories', inventories_substream())
2515
yield ('inventory-deltas', inventory_delta_substream())
2517
versionedfile.FulltextContentFactory(
2518
('some-rev', 'some-file'), (), None, 'content')])
2519
def inventories_substream():
2520
# An empty inventory fulltext. This will be streamed normally.
2521
text = fmt._serializer.write_inventory_to_string(inv)
2522
yield versionedfile.FulltextContentFactory(
2523
('rev1',), (), None, text)
2524
def inventory_delta_substream():
2525
# An inventory delta. This can't be streamed via this verb, so it
2526
# will trigger a fallback to VFS insert_stream.
2527
entry = inv.make_entry(
2528
'directory', 'newdir', inv.root.file_id, 'newdir-id')
2529
entry.revision = 'ghost'
2530
delta = [(None, 'newdir', 'newdir-id', entry)]
2531
serializer = inventory_delta.InventoryDeltaSerializer(
2532
versioned_root=True, tree_references=False)
2533
lines = serializer.delta_to_lines('rev1', 'rev2', delta)
2534
yield versionedfile.ChunkedContentFactory(
2535
('rev2',), (('rev1',)), None, lines)
2537
lines = serializer.delta_to_lines('rev1', 'rev3', delta)
2538
yield versionedfile.ChunkedContentFactory(
2539
('rev3',), (('rev1',)), None, lines)
2540
return stream_with_inv_delta()
2543
class TestRepositoryInsertStream_1_19(TestRepositoryInsertStreamBase):
2545
def test_unlocked_repo(self):
2546
transport_path = 'quack'
2547
repo, client = self.setup_fake_client_and_repository(transport_path)
2548
client.add_expected_call(
2549
'Repository.insert_stream_1.19', ('quack/', ''),
2551
client.add_expected_call(
2552
'Repository.insert_stream_1.19', ('quack/', ''),
2554
self.checkInsertEmptyStream(repo, client)
2556
def test_locked_repo_with_no_lock_token(self):
2557
transport_path = 'quack'
2558
repo, client = self.setup_fake_client_and_repository(transport_path)
2559
client.add_expected_call(
2560
'Repository.lock_write', ('quack/', ''),
2561
'success', ('ok', ''))
2562
client.add_expected_call(
2563
'Repository.insert_stream_1.19', ('quack/', ''),
2565
client.add_expected_call(
2566
'Repository.insert_stream_1.19', ('quack/', ''),
2569
self.checkInsertEmptyStream(repo, client)
2571
def test_locked_repo_with_lock_token(self):
2572
transport_path = 'quack'
2573
repo, client = self.setup_fake_client_and_repository(transport_path)
2574
client.add_expected_call(
2575
'Repository.lock_write', ('quack/', ''),
2576
'success', ('ok', 'a token'))
2577
client.add_expected_call(
2578
'Repository.insert_stream_1.19', ('quack/', '', 'a token'),
2580
client.add_expected_call(
2581
'Repository.insert_stream_1.19', ('quack/', '', 'a token'),
2584
self.checkInsertEmptyStream(repo, client)
2281
2587
class TestRepositoryTarball(TestRemoteRepository):