321
326
# self.assertEqualDiff('', t.get('lock').read())
322
327
self.assertTrue(S_ISDIR(t.stat('knits').st_mode))
323
328
self.check_knits(t)
329
# Check per-file knits.
330
branch = control.create_branch()
331
tree = control.create_workingtree()
332
tree.add(['foo'], ['Nasty-IdC:'], ['file'])
333
tree.put_file_bytes_non_atomic('Nasty-IdC:', '')
334
tree.commit('1st post', rev_id='foo')
335
self.assertHasKnit(t, 'knits/e8/%254easty-%2549d%2543%253a',
336
'\nfoo fulltext 0 81 :')
325
def assertHasKnit(self, t, knit_name):
338
def assertHasKnit(self, t, knit_name, extra_content=''):
326
339
"""Assert that knit_name exists on t."""
327
self.assertEqualDiff('# bzr knit index 8\n',
340
self.assertEqualDiff('# bzr knit index 8\n' + extra_content,
328
341
t.get(knit_name + '.kndx').read())
330
self.assertTrue(t.has(knit_name + '.knit'))
332
343
def check_knits(self, t):
333
344
"""check knit content for a repository."""
420
421
self.assertFalse(repo._format.supports_external_lookups)
423
class KnitRepositoryStreamTests(test_knit.KnitTests):
424
"""Tests for knitrepo._get_stream_as_bytes."""
426
def test_get_stream_as_bytes(self):
428
k1 = self.make_test_knit()
429
k1.add_lines('text-a', [], test_knit.split_lines(test_knit.TEXT_1))
431
# Serialise it, check the output.
432
bytes = knitrepo._get_stream_as_bytes(k1, ['text-a'])
433
data = bencode.bdecode(bytes)
434
format, record = data
435
self.assertEqual('knit-plain', format)
436
self.assertEqual(['text-a', ['fulltext'], []], record[:3])
437
self.assertRecordContentEqual(k1, 'text-a', record[3])
439
def test_get_stream_as_bytes_all(self):
440
"""Get a serialised data stream for all the records in a knit.
442
Much like test_get_stream_all, except for get_stream_as_bytes.
444
k1 = self.make_test_knit()
445
# Insert the same data as BasicKnitTests.test_knit_join, as they seem
446
# to cover a range of cases (no parents, one parent, multiple parents).
448
('text-a', [], test_knit.TEXT_1),
449
('text-b', ['text-a'], test_knit.TEXT_1),
450
('text-c', [], test_knit.TEXT_1),
451
('text-d', ['text-c'], test_knit.TEXT_1),
452
('text-m', ['text-b', 'text-d'], test_knit.TEXT_1),
454
# This test is actually a bit strict as the order in which they're
455
# returned is not defined. This matches the current (deterministic)
457
expected_data_list = [
458
# version, options, parents
459
('text-a', ['fulltext'], []),
460
('text-b', ['line-delta'], ['text-a']),
461
('text-m', ['line-delta'], ['text-b', 'text-d']),
462
('text-c', ['fulltext'], []),
463
('text-d', ['line-delta'], ['text-c']),
465
for version_id, parents, lines in test_data:
466
k1.add_lines(version_id, parents, test_knit.split_lines(lines))
468
bytes = knitrepo._get_stream_as_bytes(
469
k1, ['text-a', 'text-b', 'text-m', 'text-c', 'text-d', ])
471
data = bencode.bdecode(bytes)
473
self.assertEqual('knit-plain', format)
475
for expected, actual in zip(expected_data_list, data):
476
expected_version = expected[0]
477
expected_options = expected[1]
478
expected_parents = expected[2]
479
version, options, parents, bytes = actual
480
self.assertEqual(expected_version, version)
481
self.assertEqual(expected_options, options)
482
self.assertEqual(expected_parents, parents)
483
self.assertRecordContentEqual(k1, version, bytes)
486
424
class DummyRepository(object):
487
425
"""A dummy repository for testing."""
593
531
repo_b).__class__)
596
class TestInterRemoteToOther(TestCaseWithTransport):
598
def make_remote_repository(self, path, backing_format=None):
599
"""Make a RemoteRepository object backed by a real repository that will
600
be created at the given path."""
601
self.make_repository(path, format=backing_format)
602
smart_server = server.SmartTCPServer_for_testing()
604
remote_transport = get_transport(smart_server.get_url()).clone(path)
605
self.addCleanup(smart_server.tearDown)
606
remote_bzrdir = bzrdir.BzrDir.open_from_transport(remote_transport)
607
remote_repo = remote_bzrdir.open_repository()
610
def test_is_compatible_same_format(self):
611
"""InterRemoteToOther is compatible with a remote repository and a
612
second repository that have the same format."""
613
local_repo = self.make_repository('local')
614
remote_repo = self.make_remote_repository('remote')
615
is_compatible = repository.InterRemoteToOther.is_compatible
617
is_compatible(remote_repo, local_repo),
618
"InterRemoteToOther(%r, %r) is false" % (remote_repo, local_repo))
620
def test_is_incompatible_different_format(self):
621
local_repo = self.make_repository('local', 'dirstate')
622
remote_repo = self.make_remote_repository('a', 'dirstate-with-subtree')
623
is_compatible = repository.InterRemoteToOther.is_compatible
625
is_compatible(remote_repo, local_repo),
626
"InterRemoteToOther(%r, %r) is true" % (local_repo, remote_repo))
628
def test_is_incompatible_different_format_both_remote(self):
629
remote_repo_a = self.make_remote_repository(
630
'a', 'dirstate-with-subtree')
631
remote_repo_b = self.make_remote_repository('b', 'dirstate')
632
is_compatible = repository.InterRemoteToOther.is_compatible
634
is_compatible(remote_repo_a, remote_repo_b),
635
"InterRemoteToOther(%r, %r) is true"
636
% (remote_repo_a, remote_repo_b))
639
534
class TestRepositoryConverter(TestCaseWithTransport):
641
536
def test_convert_empty(self):
670
565
tree = self.make_branch_and_tree('.', format)
671
566
tree.commit("Dull commit", rev_id="dull")
672
567
revision_tree = tree.branch.repository.revision_tree('dull')
673
self.assertRaises(errors.NoSuchFile, revision_tree.get_file_lines,
674
revision_tree.inventory.root.file_id)
568
revision_tree.lock_read()
570
self.assertRaises(errors.NoSuchFile, revision_tree.get_file_lines,
571
revision_tree.inventory.root.file_id)
573
revision_tree.unlock()
675
574
format = bzrdir.BzrDirMetaFormat1()
676
575
format.repository_format = knitrepo.RepositoryFormatKnit3()
677
576
upgrade.Convert('.', format)
678
577
tree = workingtree.WorkingTree.open('.')
679
578
revision_tree = tree.branch.repository.revision_tree('dull')
680
revision_tree.get_file_lines(revision_tree.inventory.root.file_id)
579
revision_tree.lock_read()
581
revision_tree.get_file_lines(revision_tree.inventory.root.file_id)
583
revision_tree.unlock()
681
584
tree.commit("Another dull commit", rev_id='dull2')
682
585
revision_tree = tree.branch.repository.revision_tree('dull2')
586
revision_tree.lock_read()
587
self.addCleanup(revision_tree.unlock)
683
588
self.assertEqual('dull', revision_tree.inventory.root.revision)
685
def test_exposed_versioned_files_are_marked_dirty(self):
686
format = bzrdir.BzrDirMetaFormat1()
687
format.repository_format = knitrepo.RepositoryFormatKnit3()
688
repo = self.make_repository('.', format=format)
690
inv = repo.get_inventory_weave()
692
self.assertRaises(errors.OutSideTransaction,
693
inv.add_lines, 'foo', [], [])
695
590
def test_supports_external_lookups(self):
696
591
format = bzrdir.BzrDirMetaFormat1()
697
592
format.repository_format = knitrepo.RepositoryFormatKnit3()
780
675
broken_repo = self.make_broken_repository()
781
676
empty_repo = self.make_repository('empty-repo')
782
search = graph.SearchResult(set(['rev1a', 'rev2', 'rev3']),
783
set(), 3, ['rev1a', 'rev2', 'rev3'])
784
broken_repo.lock_read()
785
self.addCleanup(broken_repo.unlock)
786
stream = broken_repo.get_data_stream_for_search(search)
787
empty_repo.lock_write()
788
self.addCleanup(empty_repo.unlock)
789
empty_repo.start_write_group()
792
errors.KnitCorrupt, empty_repo.insert_data_stream, stream)
794
empty_repo.abort_write_group()
677
self.assertRaises(errors.RevisionNotPresent, empty_repo.fetch, broken_repo)
797
680
class TestKnitPackNoSubtrees(TestCaseWithTransport):
946
824
pack_names = [node[1][0] for node in index.iter_all_entries()]
947
825
self.assertTrue(large_pack_name in pack_names)
827
def test_fail_obsolete_deletion(self):
828
# failing to delete obsolete packs is not fatal
829
format = self.get_format()
830
server = fakenfs.FakeNFSServer()
832
self.addCleanup(server.tearDown)
833
transport = get_transport(server.get_url())
834
bzrdir = self.get_format().initialize_on_transport(transport)
835
repo = bzrdir.create_repository()
836
repo_transport = bzrdir.get_repository_transport(None)
837
self.assertTrue(repo_transport.has('obsolete_packs'))
838
# these files are in use by another client and typically can't be deleted
839
repo_transport.put_bytes('obsolete_packs/.nfsblahblah', 'contents')
840
repo._pack_collection._clear_obsolete_packs()
841
self.assertTrue(repo_transport.has('obsolete_packs/.nfsblahblah'))
949
843
def test_pack_after_two_commits_packs_everything(self):
950
844
format = self.get_format()
951
845
tree = self.make_branch_and_tree('.', format=format)
1236
1127
t.get('format').read())
1130
class TestExternalDevelopment1(object):
1132
# mixin class for testing stack-supporting development formats
1134
def test_compatible_cross_formats(self):
1135
# early versions of the packing code relied on pack internals to
1136
# stack, but the current version should be able to stack on any
1138
repo = self.make_repository('repo', format=self.get_format())
1139
if repo.supports_rich_root():
1140
# can only stack on repositories that have compatible internal
1142
matching_format_name = 'pack-0.92-subtree'
1143
mismatching_format_name = 'pack-0.92'
1145
matching_format_name = 'pack-0.92'
1146
mismatching_format_name = 'pack-0.92-subtree'
1147
base = self.make_repository('base', format=matching_format_name)
1148
repo.add_fallback_repository(base)
1149
# you can't stack on something with incompatible data
1150
bad_repo = self.make_repository('mismatch',
1151
format=mismatching_format_name)
1152
self.assertRaises(errors.IncompatibleRepositories,
1153
repo.add_fallback_repository, bad_repo)
1155
def test_adding_pack_does_not_record_pack_names_from_other_repositories(self):
1156
base = self.make_branch_and_tree('base', format=self.get_format())
1158
referencing = self.make_branch_and_tree('repo', format=self.get_format())
1159
referencing.branch.repository.add_fallback_repository(base.branch.repository)
1160
referencing.commit('bar')
1161
new_instance = referencing.bzrdir.open_repository()
1162
new_instance.lock_read()
1163
self.addCleanup(new_instance.unlock)
1164
new_instance._pack_collection.ensure_loaded()
1165
self.assertEqual(1, len(new_instance._pack_collection.all_packs()))
1167
def test_autopack_only_considers_main_repo_packs(self):
1168
base = self.make_branch_and_tree('base', format=self.get_format())
1170
tree = self.make_branch_and_tree('repo', format=self.get_format())
1171
tree.branch.repository.add_fallback_repository(base.branch.repository)
1172
trans = tree.branch.repository.bzrdir.get_repository_transport(None)
1173
# This test could be a little cheaper by replacing the packs
1174
# attribute on the repository to allow a different pack distribution
1175
# and max packs policy - so we are checking the policy is honoured
1176
# in the test. But for now 11 commits is not a big deal in a single
1179
tree.commit('commit %s' % x)
1180
# there should be 9 packs:
1181
index = GraphIndex(trans, 'pack-names', None)
1182
self.assertEqual(9, len(list(index.iter_all_entries())))
1183
# committing one more should coalesce to 1 of 10.
1184
tree.commit('commit triggering pack')
1185
index = GraphIndex(trans, 'pack-names', None)
1186
self.assertEqual(1, len(list(index.iter_all_entries())))
1187
# packing should not damage data
1188
tree = tree.bzrdir.open_workingtree()
1189
check_result = tree.branch.repository.check(
1190
[tree.branch.last_revision()])
1191
# We should have 50 (10x5) files in the obsolete_packs directory.
1192
obsolete_files = list(trans.list_dir('obsolete_packs'))
1193
self.assertFalse('foo' in obsolete_files)
1194
self.assertFalse('bar' in obsolete_files)
1195
self.assertEqual(50, len(obsolete_files))
1196
# XXX: Todo check packs obsoleted correctly - old packs and indices
1197
# in the obsolete_packs directory.
1198
large_pack_name = list(index.iter_all_entries())[0][1][0]
1199
# finally, committing again should not touch the large pack.
1200
tree.commit('commit not triggering pack')
1201
index = GraphIndex(trans, 'pack-names', None)
1202
self.assertEqual(2, len(list(index.iter_all_entries())))
1203
pack_names = [node[1][0] for node in index.iter_all_entries()]
1204
self.assertTrue(large_pack_name in pack_names)
1207
class TestDevelopment1(TestKnitPackNoSubtrees, TestExternalDevelopment1):
1209
def get_format(self):
1210
return bzrdir.format_registry.make_bzrdir(
1213
def check_format(self, t):
1214
self.assertEqualDiff(
1215
"Bazaar development format 1 (needs bzr.dev from before 1.6)\n",
1216
t.get('format').read())
1218
def test_supports_external_lookups(self):
1219
repo = self.make_repository('.', format=self.get_format())
1220
self.assertTrue(repo._format.supports_external_lookups)
1223
class TestDevelopment1Subtree(TestKnitPackNoSubtrees, TestExternalDevelopment1):
1225
def get_format(self):
1226
return bzrdir.format_registry.make_bzrdir(
1227
'development-subtree')
1229
def check_format(self, t):
1230
self.assertEqualDiff(
1231
"Bazaar development format 1 with subtree support "
1232
"(needs bzr.dev from before 1.6)\n",
1233
t.get('format').read())
1235
def test_supports_external_lookups(self):
1236
repo = self.make_repository('.', format=self.get_format())
1237
self.assertTrue(repo._format.supports_external_lookups)
1239
1240
class TestRepositoryPackCollection(TestCaseWithTransport):
1241
1242
def get_format(self):
1398
1399
name = packs.names()[0]
1399
1400
pack_1 = packs.get_pack_by_name(name)
1400
1401
# the pack should be correctly initialised
1401
rev_index = GraphIndex(packs._index_transport, name + '.rix',
1402
packs._names[name][0])
1403
inv_index = GraphIndex(packs._index_transport, name + '.iix',
1404
packs._names[name][1])
1405
txt_index = GraphIndex(packs._index_transport, name + '.tix',
1406
packs._names[name][2])
1407
sig_index = GraphIndex(packs._index_transport, name + '.six',
1408
packs._names[name][3])
1402
sizes = packs._names[name]
1403
rev_index = GraphIndex(packs._index_transport, name + '.rix', sizes[0])
1404
inv_index = GraphIndex(packs._index_transport, name + '.iix', sizes[1])
1405
txt_index = GraphIndex(packs._index_transport, name + '.tix', sizes[2])
1406
sig_index = GraphIndex(packs._index_transport, name + '.six', sizes[3])
1409
1407
self.assertEqual(pack_repo.ExistingPack(packs._pack_transport,
1410
1408
name, rev_index, inv_index, txt_index, sig_index), pack_1)
1411
1409
# and the same instance should be returned on successive calls.