429
604
repo = repo_dir.open_repository()
430
605
self.assertTrue(isinstance(target_format, repo._format.__class__))
608
class TestMisc(TestCase):
610
def test_unescape_xml(self):
611
"""We get some kind of error when malformed entities are passed"""
612
self.assertRaises(KeyError, repository._unescape_xml, 'foo&bar;')
615
class TestRepositoryFormatKnit3(TestCaseWithTransport):
617
def test_attribute__fetch_order(self):
618
"""Knits need topological data insertion."""
619
format = bzrdir.BzrDirMetaFormat1()
620
format.repository_format = knitrepo.RepositoryFormatKnit3()
621
repo = self.make_repository('.', format=format)
622
self.assertEqual('topological', repo._fetch_order)
624
def test_attribute__fetch_uses_deltas(self):
625
"""Knits reuse deltas."""
626
format = bzrdir.BzrDirMetaFormat1()
627
format.repository_format = knitrepo.RepositoryFormatKnit3()
628
repo = self.make_repository('.', format=format)
629
self.assertEqual(True, repo._fetch_uses_deltas)
631
def test_convert(self):
632
"""Ensure the upgrade adds weaves for roots"""
633
format = bzrdir.BzrDirMetaFormat1()
634
format.repository_format = knitrepo.RepositoryFormatKnit1()
635
tree = self.make_branch_and_tree('.', format)
636
tree.commit("Dull commit", rev_id="dull")
637
revision_tree = tree.branch.repository.revision_tree('dull')
638
revision_tree.lock_read()
640
self.assertRaises(errors.NoSuchFile, revision_tree.get_file_lines,
641
revision_tree.inventory.root.file_id)
643
revision_tree.unlock()
644
format = bzrdir.BzrDirMetaFormat1()
645
format.repository_format = knitrepo.RepositoryFormatKnit3()
646
upgrade.Convert('.', format)
647
tree = workingtree.WorkingTree.open('.')
648
revision_tree = tree.branch.repository.revision_tree('dull')
649
revision_tree.lock_read()
651
revision_tree.get_file_lines(revision_tree.inventory.root.file_id)
653
revision_tree.unlock()
654
tree.commit("Another dull commit", rev_id='dull2')
655
revision_tree = tree.branch.repository.revision_tree('dull2')
656
revision_tree.lock_read()
657
self.addCleanup(revision_tree.unlock)
658
self.assertEqual('dull', revision_tree.inventory.root.revision)
660
def test_supports_external_lookups(self):
661
format = bzrdir.BzrDirMetaFormat1()
662
format.repository_format = knitrepo.RepositoryFormatKnit3()
663
repo = self.make_repository('.', format=format)
664
self.assertFalse(repo._format.supports_external_lookups)
667
class TestWithBrokenRepo(TestCaseWithTransport):
668
"""These tests seem to be more appropriate as interface tests?"""
670
def make_broken_repository(self):
671
# XXX: This function is borrowed from Aaron's "Reconcile can fix bad
672
# parent references" branch which is due to land in bzr.dev soon. Once
673
# it does, this duplication should be removed.
674
repo = self.make_repository('broken-repo')
678
cleanups.append(repo.unlock)
679
repo.start_write_group()
680
cleanups.append(repo.commit_write_group)
681
# make rev1a: A well-formed revision, containing 'file1'
682
inv = inventory.Inventory(revision_id='rev1a')
683
inv.root.revision = 'rev1a'
684
self.add_file(repo, inv, 'file1', 'rev1a', [])
685
repo.add_inventory('rev1a', inv, [])
686
revision = _mod_revision.Revision('rev1a',
687
committer='jrandom@example.com', timestamp=0,
688
inventory_sha1='', timezone=0, message='foo', parent_ids=[])
689
repo.add_revision('rev1a',revision, inv)
691
# make rev1b, which has no Revision, but has an Inventory, and
693
inv = inventory.Inventory(revision_id='rev1b')
694
inv.root.revision = 'rev1b'
695
self.add_file(repo, inv, 'file1', 'rev1b', [])
696
repo.add_inventory('rev1b', inv, [])
698
# make rev2, with file1 and file2
700
# file1 has 'rev1b' as an ancestor, even though this is not
701
# mentioned by 'rev1a', making it an unreferenced ancestor
702
inv = inventory.Inventory()
703
self.add_file(repo, inv, 'file1', 'rev2', ['rev1a', 'rev1b'])
704
self.add_file(repo, inv, 'file2', 'rev2', [])
705
self.add_revision(repo, 'rev2', inv, ['rev1a'])
707
# make ghost revision rev1c
708
inv = inventory.Inventory()
709
self.add_file(repo, inv, 'file2', 'rev1c', [])
711
# make rev3 with file2
712
# file2 refers to 'rev1c', which is a ghost in this repository, so
713
# file2 cannot have rev1c as its ancestor.
714
inv = inventory.Inventory()
715
self.add_file(repo, inv, 'file2', 'rev3', ['rev1c'])
716
self.add_revision(repo, 'rev3', inv, ['rev1c'])
719
for cleanup in reversed(cleanups):
722
def add_revision(self, repo, revision_id, inv, parent_ids):
723
inv.revision_id = revision_id
724
inv.root.revision = revision_id
725
repo.add_inventory(revision_id, inv, parent_ids)
726
revision = _mod_revision.Revision(revision_id,
727
committer='jrandom@example.com', timestamp=0, inventory_sha1='',
728
timezone=0, message='foo', parent_ids=parent_ids)
729
repo.add_revision(revision_id,revision, inv)
731
def add_file(self, repo, inv, filename, revision, parents):
732
file_id = filename + '-id'
733
entry = inventory.InventoryFile(file_id, filename, 'TREE_ROOT')
734
entry.revision = revision
737
text_key = (file_id, revision)
738
parent_keys = [(file_id, parent) for parent in parents]
739
repo.texts.add_lines(text_key, parent_keys, ['line\n'])
741
def test_insert_from_broken_repo(self):
742
"""Inserting a data stream from a broken repository won't silently
743
corrupt the target repository.
745
broken_repo = self.make_broken_repository()
746
empty_repo = self.make_repository('empty-repo')
747
self.assertRaises((errors.RevisionNotPresent, errors.BzrCheckError),
748
empty_repo.fetch, broken_repo)
751
class TestRepositoryPackCollection(TestCaseWithTransport):
753
def get_format(self):
754
return bzrdir.format_registry.make_bzrdir('pack-0.92')
757
format = self.get_format()
758
repo = self.make_repository('.', format=format)
759
return repo._pack_collection
761
def make_packs_and_alt_repo(self, write_lock=False):
762
"""Create a pack repo with 3 packs, and access it via a second repo."""
763
tree = self.make_branch_and_tree('.')
765
self.addCleanup(tree.unlock)
766
rev1 = tree.commit('one')
767
rev2 = tree.commit('two')
768
rev3 = tree.commit('three')
769
r = repository.Repository.open('.')
774
self.addCleanup(r.unlock)
775
packs = r._pack_collection
776
packs.ensure_loaded()
777
return tree, r, packs, [rev1, rev2, rev3]
779
def test__max_pack_count(self):
780
"""The maximum pack count is a function of the number of revisions."""
781
# no revisions - one pack, so that we can have a revision free repo
782
# without it blowing up
783
packs = self.get_packs()
784
self.assertEqual(1, packs._max_pack_count(0))
785
# after that the sum of the digits, - check the first 1-9
786
self.assertEqual(1, packs._max_pack_count(1))
787
self.assertEqual(2, packs._max_pack_count(2))
788
self.assertEqual(3, packs._max_pack_count(3))
789
self.assertEqual(4, packs._max_pack_count(4))
790
self.assertEqual(5, packs._max_pack_count(5))
791
self.assertEqual(6, packs._max_pack_count(6))
792
self.assertEqual(7, packs._max_pack_count(7))
793
self.assertEqual(8, packs._max_pack_count(8))
794
self.assertEqual(9, packs._max_pack_count(9))
795
# check the boundary cases with two digits for the next decade
796
self.assertEqual(1, packs._max_pack_count(10))
797
self.assertEqual(2, packs._max_pack_count(11))
798
self.assertEqual(10, packs._max_pack_count(19))
799
self.assertEqual(2, packs._max_pack_count(20))
800
self.assertEqual(3, packs._max_pack_count(21))
801
# check some arbitrary big numbers
802
self.assertEqual(25, packs._max_pack_count(112894))
804
def test_pack_distribution_zero(self):
805
packs = self.get_packs()
806
self.assertEqual([0], packs.pack_distribution(0))
808
def test_ensure_loaded_unlocked(self):
809
packs = self.get_packs()
810
self.assertRaises(errors.ObjectNotLocked,
813
def test_pack_distribution_one_to_nine(self):
814
packs = self.get_packs()
815
self.assertEqual([1],
816
packs.pack_distribution(1))
817
self.assertEqual([1, 1],
818
packs.pack_distribution(2))
819
self.assertEqual([1, 1, 1],
820
packs.pack_distribution(3))
821
self.assertEqual([1, 1, 1, 1],
822
packs.pack_distribution(4))
823
self.assertEqual([1, 1, 1, 1, 1],
824
packs.pack_distribution(5))
825
self.assertEqual([1, 1, 1, 1, 1, 1],
826
packs.pack_distribution(6))
827
self.assertEqual([1, 1, 1, 1, 1, 1, 1],
828
packs.pack_distribution(7))
829
self.assertEqual([1, 1, 1, 1, 1, 1, 1, 1],
830
packs.pack_distribution(8))
831
self.assertEqual([1, 1, 1, 1, 1, 1, 1, 1, 1],
832
packs.pack_distribution(9))
834
def test_pack_distribution_stable_at_boundaries(self):
835
"""When there are multi-rev packs the counts are stable."""
836
packs = self.get_packs()
838
self.assertEqual([10], packs.pack_distribution(10))
839
self.assertEqual([10, 1], packs.pack_distribution(11))
840
self.assertEqual([10, 10], packs.pack_distribution(20))
841
self.assertEqual([10, 10, 1], packs.pack_distribution(21))
843
self.assertEqual([100], packs.pack_distribution(100))
844
self.assertEqual([100, 1], packs.pack_distribution(101))
845
self.assertEqual([100, 10, 1], packs.pack_distribution(111))
846
self.assertEqual([100, 100], packs.pack_distribution(200))
847
self.assertEqual([100, 100, 1], packs.pack_distribution(201))
848
self.assertEqual([100, 100, 10, 1], packs.pack_distribution(211))
850
def test_plan_pack_operations_2009_revisions_skip_all_packs(self):
851
packs = self.get_packs()
852
existing_packs = [(2000, "big"), (9, "medium")]
853
# rev count - 2009 -> 2x1000 + 9x1
854
pack_operations = packs.plan_autopack_combinations(
855
existing_packs, [1000, 1000, 1, 1, 1, 1, 1, 1, 1, 1, 1])
856
self.assertEqual([], pack_operations)
858
def test_plan_pack_operations_2010_revisions_skip_all_packs(self):
859
packs = self.get_packs()
860
existing_packs = [(2000, "big"), (9, "medium"), (1, "single")]
861
# rev count - 2010 -> 2x1000 + 1x10
862
pack_operations = packs.plan_autopack_combinations(
863
existing_packs, [1000, 1000, 10])
864
self.assertEqual([], pack_operations)
866
def test_plan_pack_operations_2010_combines_smallest_two(self):
867
packs = self.get_packs()
868
existing_packs = [(1999, "big"), (9, "medium"), (1, "single2"),
870
# rev count - 2010 -> 2x1000 + 1x10 (3)
871
pack_operations = packs.plan_autopack_combinations(
872
existing_packs, [1000, 1000, 10])
873
self.assertEqual([[2, ["single2", "single1"]]], pack_operations)
875
def test_plan_pack_operations_creates_a_single_op(self):
876
packs = self.get_packs()
877
existing_packs = [(50, 'a'), (40, 'b'), (30, 'c'), (10, 'd'),
878
(10, 'e'), (6, 'f'), (4, 'g')]
879
# rev count 150 -> 1x100 and 5x10
880
# The two size 10 packs do not need to be touched. The 50, 40, 30 would
881
# be combined into a single 120 size pack, and the 6 & 4 would
882
# becombined into a size 10 pack. However, if we have to rewrite them,
883
# we save a pack file with no increased I/O by putting them into the
885
distribution = packs.pack_distribution(150)
886
pack_operations = packs.plan_autopack_combinations(existing_packs,
888
self.assertEqual([[130, ['a', 'b', 'c', 'f', 'g']]], pack_operations)
890
def test_all_packs_none(self):
891
format = self.get_format()
892
tree = self.make_branch_and_tree('.', format=format)
894
self.addCleanup(tree.unlock)
895
packs = tree.branch.repository._pack_collection
896
packs.ensure_loaded()
897
self.assertEqual([], packs.all_packs())
899
def test_all_packs_one(self):
900
format = self.get_format()
901
tree = self.make_branch_and_tree('.', format=format)
904
self.addCleanup(tree.unlock)
905
packs = tree.branch.repository._pack_collection
906
packs.ensure_loaded()
908
packs.get_pack_by_name(packs.names()[0])],
911
def test_all_packs_two(self):
912
format = self.get_format()
913
tree = self.make_branch_and_tree('.', format=format)
915
tree.commit('continue')
917
self.addCleanup(tree.unlock)
918
packs = tree.branch.repository._pack_collection
919
packs.ensure_loaded()
921
packs.get_pack_by_name(packs.names()[0]),
922
packs.get_pack_by_name(packs.names()[1]),
923
], packs.all_packs())
925
def test_get_pack_by_name(self):
926
format = self.get_format()
927
tree = self.make_branch_and_tree('.', format=format)
930
self.addCleanup(tree.unlock)
931
packs = tree.branch.repository._pack_collection
932
packs.ensure_loaded()
933
name = packs.names()[0]
934
pack_1 = packs.get_pack_by_name(name)
935
# the pack should be correctly initialised
936
sizes = packs._names[name]
937
rev_index = GraphIndex(packs._index_transport, name + '.rix', sizes[0])
938
inv_index = GraphIndex(packs._index_transport, name + '.iix', sizes[1])
939
txt_index = GraphIndex(packs._index_transport, name + '.tix', sizes[2])
940
sig_index = GraphIndex(packs._index_transport, name + '.six', sizes[3])
941
self.assertEqual(pack_repo.ExistingPack(packs._pack_transport,
942
name, rev_index, inv_index, txt_index, sig_index), pack_1)
943
# and the same instance should be returned on successive calls.
944
self.assertTrue(pack_1 is packs.get_pack_by_name(name))
946
def test_reload_pack_names_new_entry(self):
947
tree, r, packs, revs = self.make_packs_and_alt_repo()
948
names = packs.names()
949
# Add a new pack file into the repository
950
rev4 = tree.commit('four')
951
new_names = tree.branch.repository._pack_collection.names()
952
new_name = set(new_names).difference(names)
953
self.assertEqual(1, len(new_name))
954
new_name = new_name.pop()
955
# The old collection hasn't noticed yet
956
self.assertEqual(names, packs.names())
957
self.assertTrue(packs.reload_pack_names())
958
self.assertEqual(new_names, packs.names())
959
# And the repository can access the new revision
960
self.assertEqual({rev4:(revs[-1],)}, r.get_parent_map([rev4]))
961
self.assertFalse(packs.reload_pack_names())
963
def test_reload_pack_names_added_and_removed(self):
964
tree, r, packs, revs = self.make_packs_and_alt_repo()
965
names = packs.names()
966
# Now repack the whole thing
967
tree.branch.repository.pack()
968
new_names = tree.branch.repository._pack_collection.names()
969
# The other collection hasn't noticed yet
970
self.assertEqual(names, packs.names())
971
self.assertTrue(packs.reload_pack_names())
972
self.assertEqual(new_names, packs.names())
973
self.assertEqual({revs[-1]:(revs[-2],)}, r.get_parent_map([revs[-1]]))
974
self.assertFalse(packs.reload_pack_names())
976
def test_autopack_reloads_and_stops(self):
977
tree, r, packs, revs = self.make_packs_and_alt_repo(write_lock=True)
978
# After we have determined what needs to be autopacked, trigger a
979
# full-pack via the other repo which will cause us to re-evaluate and
980
# decide we don't need to do anything
981
orig_execute = packs._execute_pack_operations
982
def _munged_execute_pack_ops(*args, **kwargs):
983
tree.branch.repository.pack()
984
return orig_execute(*args, **kwargs)
985
packs._execute_pack_operations = _munged_execute_pack_ops
986
packs._max_pack_count = lambda x: 1
987
packs.pack_distribution = lambda x: [10]
988
self.assertFalse(packs.autopack())
989
self.assertEqual(1, len(packs.names()))
990
self.assertEqual(tree.branch.repository._pack_collection.names(),
994
class TestPack(TestCaseWithTransport):
995
"""Tests for the Pack object."""
997
def assertCurrentlyEqual(self, left, right):
998
self.assertTrue(left == right)
999
self.assertTrue(right == left)
1000
self.assertFalse(left != right)
1001
self.assertFalse(right != left)
1003
def assertCurrentlyNotEqual(self, left, right):
1004
self.assertFalse(left == right)
1005
self.assertFalse(right == left)
1006
self.assertTrue(left != right)
1007
self.assertTrue(right != left)
1009
def test___eq____ne__(self):
1010
left = pack_repo.ExistingPack('', '', '', '', '', '')
1011
right = pack_repo.ExistingPack('', '', '', '', '', '')
1012
self.assertCurrentlyEqual(left, right)
1013
# change all attributes and ensure equality changes as we do.
1014
left.revision_index = 'a'
1015
self.assertCurrentlyNotEqual(left, right)
1016
right.revision_index = 'a'
1017
self.assertCurrentlyEqual(left, right)
1018
left.inventory_index = 'a'
1019
self.assertCurrentlyNotEqual(left, right)
1020
right.inventory_index = 'a'
1021
self.assertCurrentlyEqual(left, right)
1022
left.text_index = 'a'
1023
self.assertCurrentlyNotEqual(left, right)
1024
right.text_index = 'a'
1025
self.assertCurrentlyEqual(left, right)
1026
left.signature_index = 'a'
1027
self.assertCurrentlyNotEqual(left, right)
1028
right.signature_index = 'a'
1029
self.assertCurrentlyEqual(left, right)
1031
self.assertCurrentlyNotEqual(left, right)
1033
self.assertCurrentlyEqual(left, right)
1034
left.transport = 'a'
1035
self.assertCurrentlyNotEqual(left, right)
1036
right.transport = 'a'
1037
self.assertCurrentlyEqual(left, right)
1039
def test_file_name(self):
1040
pack = pack_repo.ExistingPack('', 'a_name', '', '', '', '')
1041
self.assertEqual('a_name.pack', pack.file_name())
1044
class TestNewPack(TestCaseWithTransport):
1045
"""Tests for pack_repo.NewPack."""
1047
def test_new_instance_attributes(self):
1048
upload_transport = self.get_transport('upload')
1049
pack_transport = self.get_transport('pack')
1050
index_transport = self.get_transport('index')
1051
upload_transport.mkdir('.')
1052
collection = pack_repo.RepositoryPackCollection(repo=None,
1053
transport=self.get_transport('.'),
1054
index_transport=index_transport,
1055
upload_transport=upload_transport,
1056
pack_transport=pack_transport,
1057
index_builder_class=BTreeBuilder,
1058
index_class=BTreeGraphIndex)
1059
pack = pack_repo.NewPack(collection)
1060
self.assertIsInstance(pack.revision_index, BTreeBuilder)
1061
self.assertIsInstance(pack.inventory_index, BTreeBuilder)
1062
self.assertIsInstance(pack._hash, type(osutils.md5()))
1063
self.assertTrue(pack.upload_transport is upload_transport)
1064
self.assertTrue(pack.index_transport is index_transport)
1065
self.assertTrue(pack.pack_transport is pack_transport)
1066
self.assertEqual(None, pack.index_sizes)
1067
self.assertEqual(20, len(pack.random_name))
1068
self.assertIsInstance(pack.random_name, str)
1069
self.assertIsInstance(pack.start_time, float)
1072
class TestPacker(TestCaseWithTransport):
1073
"""Tests for the packs repository Packer class."""
1075
def test_pack_optimizes_pack_order(self):
1076
builder = self.make_branch_builder('.')
1077
builder.start_series()
1078
builder.build_snapshot('A', None, [
1079
('add', ('', 'root-id', 'directory', None)),
1080
('add', ('f', 'f-id', 'file', 'content\n'))])
1081
builder.build_snapshot('B', ['A'],
1082
[('modify', ('f-id', 'new-content\n'))])
1083
builder.build_snapshot('C', ['B'],
1084
[('modify', ('f-id', 'third-content\n'))])
1085
builder.build_snapshot('D', ['C'],
1086
[('modify', ('f-id', 'fourth-content\n'))])
1087
b = builder.get_branch()
1089
builder.finish_series()
1090
self.addCleanup(b.unlock)
1091
# At this point, we should have 4 pack files available
1092
# Because of how they were built, they correspond to
1093
# ['D', 'C', 'B', 'A']
1094
packs = b.repository._pack_collection.packs
1095
packer = pack_repo.Packer(b.repository._pack_collection,
1097
revision_ids=['B', 'C'])
1098
# Now, when we are copying the B & C revisions, their pack files should
1099
# be moved to the front of the stack
1100
# The new ordering moves B & C to the front of the .packs attribute,
1101
# and leaves the others in the original order.
1102
new_packs = [packs[1], packs[2], packs[0], packs[3]]
1103
new_pack = packer.pack()
1104
self.assertEqual(new_packs, packer.packs)
1107
class TestOptimisingPacker(TestCaseWithTransport):
1108
"""Tests for the OptimisingPacker class."""
1110
def get_pack_collection(self):
1111
repo = self.make_repository('.')
1112
return repo._pack_collection
1114
def test_open_pack_will_optimise(self):
1115
packer = pack_repo.OptimisingPacker(self.get_pack_collection(),
1117
new_pack = packer.open_pack()
1118
self.assertIsInstance(new_pack, pack_repo.NewPack)
1119
self.assertTrue(new_pack.revision_index._optimize_for_size)
1120
self.assertTrue(new_pack.inventory_index._optimize_for_size)
1121
self.assertTrue(new_pack.text_index._optimize_for_size)
1122
self.assertTrue(new_pack.signature_index._optimize_for_size)
1125
class TestInterDifferingSerializer(TestCaseWithTransport):
1127
def test_progress_bar(self):
1128
tree = self.make_branch_and_tree('tree')
1129
tree.commit('rev1', rev_id='rev-1')
1130
tree.commit('rev2', rev_id='rev-2')
1131
tree.commit('rev3', rev_id='rev-3')
1132
repo = self.make_repository('repo')
1133
inter_repo = repository.InterDifferingSerializer(
1134
tree.branch.repository, repo)
1135
pb = progress.InstrumentedProgress(to_file=StringIO())
1136
pb.never_throttle = True
1137
inter_repo.fetch('rev-1', pb)
1138
self.assertEqual('Transferring revisions', pb.last_msg)
1139
self.assertEqual(1, pb.last_cnt)
1140
self.assertEqual(1, pb.last_total)
1141
inter_repo.fetch('rev-3', pb)
1142
self.assertEqual(2, pb.last_cnt)
1143
self.assertEqual(2, pb.last_total)