~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_repository.py

  • Committer: Andrew Bennetts
  • Date: 2007-11-10 15:09:09 UTC
  • mfrom: (2916.2.17 streamable-containers)
  • mto: This revision was merged to the branch mainline in revision 3174.
  • Revision ID: andrew.bennetts@canonical.com-20071110150909-ik5254kgn930th10
Merge streamable-containers.

Show diffs side-by-side

added added

removed removed

Lines of Context:
22
22
also see this file.
23
23
"""
24
24
 
 
25
import md5
25
26
from stat import S_ISDIR
26
27
from StringIO import StringIO
27
28
 
31
32
                           UnknownFormatError,
32
33
                           UnsupportedFormatError,
33
34
                           )
 
35
from bzrlib.index import GraphIndex, InMemoryGraphIndex
34
36
from bzrlib.repository import RepositoryFormat
35
37
from bzrlib.smart import server
36
38
from bzrlib.tests import (
51
53
    upgrade,
52
54
    workingtree,
53
55
    )
54
 
from bzrlib.repofmt import knitrepo, weaverepo
 
56
from bzrlib.repofmt import knitrepo, weaverepo, pack_repo
55
57
 
56
58
 
57
59
class TestDefaultFormat(TestCase):
373
375
        self.assertRaises(errors.OutSideTransaction,
374
376
            inv.add_lines, 'foo', [], [])
375
377
 
 
378
    def test_deserialise_sets_root_revision(self):
 
379
        """We must have a inventory.root.revision
 
380
 
 
381
        Old versions of the XML5 serializer did not set the revision_id for
 
382
        the whole inventory. So we grab the one from the expected text. Which
 
383
        is valid when the api is not being abused.
 
384
        """
 
385
        repo = self.make_repository('.',
 
386
                format=bzrdir.format_registry.get('knit')())
 
387
        inv_xml = '<inventory format="5">\n</inventory>\n'
 
388
        inv = repo.deserialise_inventory('test-rev-id', inv_xml)
 
389
        self.assertEqual('test-rev-id', inv.root.revision)
 
390
 
 
391
    def test_deserialise_uses_global_revision_id(self):
 
392
        """If it is set, then we re-use the global revision id"""
 
393
        repo = self.make_repository('.',
 
394
                format=bzrdir.format_registry.get('knit')())
 
395
        inv_xml = ('<inventory format="5" revision_id="other-rev-id">\n'
 
396
                   '</inventory>\n')
 
397
        # Arguably, the deserialise_inventory should detect a mismatch, and
 
398
        # raise an error, rather than silently using one revision_id over the
 
399
        # other.
 
400
        inv = repo.deserialise_inventory('test-rev-id', inv_xml)
 
401
        self.assertEqual('other-rev-id', inv.root.revision)
 
402
 
376
403
 
377
404
class KnitRepositoryStreamTests(test_knit.KnitTests):
378
405
    """Tests for knitrepo._get_stream_as_bytes."""
645
672
 
646
673
 
647
674
class TestWithBrokenRepo(TestCaseWithTransport):
 
675
    """These tests seem to be more appropriate as interface tests?"""
648
676
 
649
677
    def make_broken_repository(self):
650
678
        # XXX: This function is borrowed from Aaron's "Reconcile can fix bad
724
752
        broken_repo = self.make_broken_repository()
725
753
        empty_repo = self.make_repository('empty-repo')
726
754
        stream = broken_repo.get_data_stream(['rev1a', 'rev2', 'rev3'])
727
 
        self.assertRaises(
728
 
            errors.KnitCorrupt, empty_repo.insert_data_stream, stream)
729
 
 
 
755
        empty_repo.lock_write()
 
756
        self.addCleanup(empty_repo.unlock)
 
757
        empty_repo.start_write_group()
 
758
        try:
 
759
            self.assertRaises(
 
760
                errors.KnitCorrupt, empty_repo.insert_data_stream, stream)
 
761
        finally:
 
762
            empty_repo.abort_write_group()
 
763
 
 
764
 
 
765
class TestKnitPackNoSubtrees(TestCaseWithTransport):
 
766
 
 
767
    def get_format(self):
 
768
        return bzrdir.format_registry.make_bzrdir('knitpack-experimental')
 
769
 
 
770
    def test_disk_layout(self):
 
771
        format = self.get_format()
 
772
        repo = self.make_repository('.', format=format)
 
773
        # in case of side effects of locking.
 
774
        repo.lock_write()
 
775
        repo.unlock()
 
776
        t = repo.bzrdir.get_repository_transport(None)
 
777
        self.check_format(t)
 
778
        # XXX: no locks left when unlocked at the moment
 
779
        # self.assertEqualDiff('', t.get('lock').read())
 
780
        self.check_databases(t)
 
781
 
 
782
    def check_format(self, t):
 
783
        self.assertEqualDiff(
 
784
            "Bazaar pack repository format 1 (needs bzr 0.92)\n",
 
785
                             t.get('format').read())
 
786
 
 
787
    def assertHasKndx(self, t, knit_name):
 
788
        """Assert that knit_name exists on t."""
 
789
        self.assertEqualDiff('# bzr knit index 8\n',
 
790
                             t.get(knit_name + '.kndx').read())
 
791
 
 
792
    def assertHasNoKndx(self, t, knit_name):
 
793
        """Assert that knit_name has no index on t."""
 
794
        self.assertFalse(t.has(knit_name + '.kndx'))
 
795
 
 
796
    def assertHasNoKnit(self, t, knit_name):
 
797
        """Assert that knit_name exists on t."""
 
798
        # no default content
 
799
        self.assertFalse(t.has(knit_name + '.knit'))
 
800
 
 
801
    def check_databases(self, t):
 
802
        """check knit content for a repository."""
 
803
        # check conversion worked
 
804
        self.assertHasNoKndx(t, 'inventory')
 
805
        self.assertHasNoKnit(t, 'inventory')
 
806
        self.assertHasNoKndx(t, 'revisions')
 
807
        self.assertHasNoKnit(t, 'revisions')
 
808
        self.assertHasNoKndx(t, 'signatures')
 
809
        self.assertHasNoKnit(t, 'signatures')
 
810
        self.assertFalse(t.has('knits'))
 
811
        # revision-indexes file-container directory
 
812
        self.assertEqual([],
 
813
            list(GraphIndex(t, 'pack-names', None).iter_all_entries()))
 
814
        self.assertTrue(S_ISDIR(t.stat('packs').st_mode))
 
815
        self.assertTrue(S_ISDIR(t.stat('upload').st_mode))
 
816
        self.assertTrue(S_ISDIR(t.stat('indices').st_mode))
 
817
        self.assertTrue(S_ISDIR(t.stat('obsolete_packs').st_mode))
 
818
 
 
819
    def test_shared_disk_layout(self):
 
820
        format = self.get_format()
 
821
        repo = self.make_repository('.', shared=True, format=format)
 
822
        # we want:
 
823
        t = repo.bzrdir.get_repository_transport(None)
 
824
        self.check_format(t)
 
825
        # XXX: no locks left when unlocked at the moment
 
826
        # self.assertEqualDiff('', t.get('lock').read())
 
827
        # We should have a 'shared-storage' marker file.
 
828
        self.assertEqualDiff('', t.get('shared-storage').read())
 
829
        self.check_databases(t)
 
830
 
 
831
    def test_shared_no_tree_disk_layout(self):
 
832
        format = self.get_format()
 
833
        repo = self.make_repository('.', shared=True, format=format)
 
834
        repo.set_make_working_trees(False)
 
835
        # we want:
 
836
        t = repo.bzrdir.get_repository_transport(None)
 
837
        self.check_format(t)
 
838
        # XXX: no locks left when unlocked at the moment
 
839
        # self.assertEqualDiff('', t.get('lock').read())
 
840
        # We should have a 'shared-storage' marker file.
 
841
        self.assertEqualDiff('', t.get('shared-storage').read())
 
842
        # We should have a marker for the no-working-trees flag.
 
843
        self.assertEqualDiff('', t.get('no-working-trees').read())
 
844
        # The marker should go when we toggle the setting.
 
845
        repo.set_make_working_trees(True)
 
846
        self.assertFalse(t.has('no-working-trees'))
 
847
        self.check_databases(t)
 
848
 
 
849
    def test_adding_revision_creates_pack_indices(self):
 
850
        format = self.get_format()
 
851
        tree = self.make_branch_and_tree('.', format=format)
 
852
        trans = tree.branch.repository.bzrdir.get_repository_transport(None)
 
853
        self.assertEqual([],
 
854
            list(GraphIndex(trans, 'pack-names', None).iter_all_entries()))
 
855
        tree.commit('foobarbaz')
 
856
        index = GraphIndex(trans, 'pack-names', None)
 
857
        index_nodes = list(index.iter_all_entries())
 
858
        self.assertEqual(1, len(index_nodes))
 
859
        node = index_nodes[0]
 
860
        name = node[1][0]
 
861
        # the pack sizes should be listed in the index
 
862
        pack_value = node[2]
 
863
        sizes = [int(digits) for digits in pack_value.split(' ')]
 
864
        for size, suffix in zip(sizes, ['.rix', '.iix', '.tix', '.six']):
 
865
            stat = trans.stat('indices/%s%s' % (name, suffix))
 
866
            self.assertEqual(size, stat.st_size)
 
867
 
 
868
    def test_pulling_nothing_leads_to_no_new_names(self):
 
869
        format = self.get_format()
 
870
        tree1 = self.make_branch_and_tree('1', format=format)
 
871
        tree2 = self.make_branch_and_tree('2', format=format)
 
872
        tree1.branch.repository.fetch(tree2.branch.repository)
 
873
        trans = tree1.branch.repository.bzrdir.get_repository_transport(None)
 
874
        self.assertEqual([],
 
875
            list(GraphIndex(trans, 'pack-names', None).iter_all_entries()))
 
876
 
 
877
    def test_commit_across_pack_shape_boundary_autopacks(self):
 
878
        format = self.get_format()
 
879
        tree = self.make_branch_and_tree('.', format=format)
 
880
        trans = tree.branch.repository.bzrdir.get_repository_transport(None)
 
881
        # This test could be a little cheaper by replacing the packs
 
882
        # attribute on the repository to allow a different pack distribution
 
883
        # and max packs policy - so we are checking the policy is honoured
 
884
        # in the test. But for now 11 commits is not a big deal in a single
 
885
        # test.
 
886
        for x in range(9):
 
887
            tree.commit('commit %s' % x)
 
888
        # there should be 9 packs:
 
889
        index = GraphIndex(trans, 'pack-names', None)
 
890
        self.assertEqual(9, len(list(index.iter_all_entries())))
 
891
        # insert some files in obsolete_packs which should be removed by pack.
 
892
        trans.put_bytes('obsolete_packs/foo', '123')
 
893
        trans.put_bytes('obsolete_packs/bar', '321')
 
894
        # committing one more should coalesce to 1 of 10.
 
895
        tree.commit('commit triggering pack')
 
896
        index = GraphIndex(trans, 'pack-names', None)
 
897
        self.assertEqual(1, len(list(index.iter_all_entries())))
 
898
        # packing should not damage data
 
899
        tree = tree.bzrdir.open_workingtree()
 
900
        check_result = tree.branch.repository.check(
 
901
            [tree.branch.last_revision()])
 
902
        # We should have 50 (10x5) files in the obsolete_packs directory.
 
903
        obsolete_files = list(trans.list_dir('obsolete_packs'))
 
904
        self.assertFalse('foo' in obsolete_files)
 
905
        self.assertFalse('bar' in obsolete_files)
 
906
        self.assertEqual(50, len(obsolete_files))
 
907
        # XXX: Todo check packs obsoleted correctly - old packs and indices
 
908
        # in the obsolete_packs directory.
 
909
        large_pack_name = list(index.iter_all_entries())[0][1][0]
 
910
        # finally, committing again should not touch the large pack.
 
911
        tree.commit('commit not triggering pack')
 
912
        index = GraphIndex(trans, 'pack-names', None)
 
913
        self.assertEqual(2, len(list(index.iter_all_entries())))
 
914
        pack_names = [node[1][0] for node in index.iter_all_entries()]
 
915
        self.assertTrue(large_pack_name in pack_names)
 
916
 
 
917
    def test_pack_after_two_commits_packs_everything(self):
 
918
        format = self.get_format()
 
919
        tree = self.make_branch_and_tree('.', format=format)
 
920
        trans = tree.branch.repository.bzrdir.get_repository_transport(None)
 
921
        tree.commit('start')
 
922
        tree.commit('more work')
 
923
        tree.branch.repository.pack()
 
924
        # there should be 1 pack:
 
925
        index = GraphIndex(trans, 'pack-names', None)
 
926
        self.assertEqual(1, len(list(index.iter_all_entries())))
 
927
        self.assertEqual(2, len(tree.branch.repository.all_revision_ids()))
 
928
 
 
929
    def test_pack_repositories_support_multiple_write_locks(self):
 
930
        format = self.get_format()
 
931
        self.make_repository('.', shared=True, format=format)
 
932
        r1 = repository.Repository.open('.')
 
933
        r2 = repository.Repository.open('.')
 
934
        r1.lock_write()
 
935
        self.addCleanup(r1.unlock)
 
936
        r2.lock_write()
 
937
        r2.unlock()
 
938
 
 
939
    def _add_text(self, repo, fileid):
 
940
        """Add a text to the repository within a write group."""
 
941
        vf =repo.weave_store.get_weave(fileid, repo.get_transaction())
 
942
        vf.add_lines('samplerev+' + fileid, [], [])
 
943
 
 
944
    def test_concurrent_writers_merge_new_packs(self):
 
945
        format = self.get_format()
 
946
        self.make_repository('.', shared=True, format=format)
 
947
        r1 = repository.Repository.open('.')
 
948
        r2 = repository.Repository.open('.')
 
949
        r1.lock_write()
 
950
        try:
 
951
            # access enough data to load the names list
 
952
            list(r1.all_revision_ids())
 
953
            r2.lock_write()
 
954
            try:
 
955
                # access enough data to load the names list
 
956
                list(r2.all_revision_ids())
 
957
                r1.start_write_group()
 
958
                try:
 
959
                    r2.start_write_group()
 
960
                    try:
 
961
                        self._add_text(r1, 'fileidr1')
 
962
                        self._add_text(r2, 'fileidr2')
 
963
                    except:
 
964
                        r2.abort_write_group()
 
965
                        raise
 
966
                except:
 
967
                    r1.abort_write_group()
 
968
                    raise
 
969
                # both r1 and r2 have open write groups with data in them
 
970
                # created while the other's write group was open.
 
971
                # Commit both which requires a merge to the pack-names.
 
972
                try:
 
973
                    r1.commit_write_group()
 
974
                except:
 
975
                    r1.abort_write_group()
 
976
                    r2.abort_write_group()
 
977
                    raise
 
978
                r2.commit_write_group()
 
979
                # tell r1 to reload from disk
 
980
                r1._pack_collection.reset()
 
981
                # Now both repositories should know about both names
 
982
                r1._pack_collection.ensure_loaded()
 
983
                r2._pack_collection.ensure_loaded()
 
984
                self.assertEqual(r1._pack_collection.names(), r2._pack_collection.names())
 
985
                self.assertEqual(2, len(r1._pack_collection.names()))
 
986
            finally:
 
987
                r2.unlock()
 
988
        finally:
 
989
            r1.unlock()
 
990
 
 
991
    def test_concurrent_writer_second_preserves_dropping_a_pack(self):
 
992
        format = self.get_format()
 
993
        self.make_repository('.', shared=True, format=format)
 
994
        r1 = repository.Repository.open('.')
 
995
        r2 = repository.Repository.open('.')
 
996
        # add a pack to drop
 
997
        r1.lock_write()
 
998
        try:
 
999
            r1.start_write_group()
 
1000
            try:
 
1001
                self._add_text(r1, 'fileidr1')
 
1002
            except:
 
1003
                r1.abort_write_group()
 
1004
                raise
 
1005
            else:
 
1006
                r1.commit_write_group()
 
1007
            r1._pack_collection.ensure_loaded()
 
1008
            name_to_drop = r1._pack_collection.all_packs()[0].name
 
1009
        finally:
 
1010
            r1.unlock()
 
1011
        r1.lock_write()
 
1012
        try:
 
1013
            # access enough data to load the names list
 
1014
            list(r1.all_revision_ids())
 
1015
            r2.lock_write()
 
1016
            try:
 
1017
                # access enough data to load the names list
 
1018
                list(r2.all_revision_ids())
 
1019
                r1._pack_collection.ensure_loaded()
 
1020
                try:
 
1021
                    r2.start_write_group()
 
1022
                    try:
 
1023
                        # in r1, drop the pack
 
1024
                        r1._pack_collection._remove_pack_from_memory(
 
1025
                            r1._pack_collection.get_pack_by_name(name_to_drop))
 
1026
                        # in r2, add a pack
 
1027
                        self._add_text(r2, 'fileidr2')
 
1028
                    except:
 
1029
                        r2.abort_write_group()
 
1030
                        raise
 
1031
                except:
 
1032
                    r1._pack_collection.reset()
 
1033
                    raise
 
1034
                # r1 has a changed names list, and r2 an open write groups with
 
1035
                # changes.
 
1036
                # save r1, and then commit the r2 write group, which requires a
 
1037
                # merge to the pack-names, which should not reinstate
 
1038
                # name_to_drop
 
1039
                try:
 
1040
                    r1._pack_collection._save_pack_names()
 
1041
                    r1._pack_collection.reset()
 
1042
                except:
 
1043
                    r2.abort_write_group()
 
1044
                    raise
 
1045
                try:
 
1046
                    r2.commit_write_group()
 
1047
                except:
 
1048
                    r2.abort_write_group()
 
1049
                    raise
 
1050
                # Now both repositories should now about just one name.
 
1051
                r1._pack_collection.ensure_loaded()
 
1052
                r2._pack_collection.ensure_loaded()
 
1053
                self.assertEqual(r1._pack_collection.names(), r2._pack_collection.names())
 
1054
                self.assertEqual(1, len(r1._pack_collection.names()))
 
1055
                self.assertFalse(name_to_drop in r1._pack_collection.names())
 
1056
            finally:
 
1057
                r2.unlock()
 
1058
        finally:
 
1059
            r1.unlock()
 
1060
 
 
1061
    def test_lock_write_does_not_physically_lock(self):
 
1062
        repo = self.make_repository('.', format=self.get_format())
 
1063
        repo.lock_write()
 
1064
        self.addCleanup(repo.unlock)
 
1065
        self.assertFalse(repo.get_physical_lock_status())
 
1066
 
 
1067
    def prepare_for_break_lock(self):
 
1068
        # Setup the global ui factory state so that a break-lock method call
 
1069
        # will find usable input in the input stream.
 
1070
        old_factory = bzrlib.ui.ui_factory
 
1071
        def restoreFactory():
 
1072
            bzrlib.ui.ui_factory = old_factory
 
1073
        self.addCleanup(restoreFactory)
 
1074
        bzrlib.ui.ui_factory = bzrlib.ui.SilentUIFactory()
 
1075
        bzrlib.ui.ui_factory.stdin = StringIO("y\n")
 
1076
 
 
1077
    def test_break_lock_breaks_physical_lock(self):
 
1078
        repo = self.make_repository('.', format=self.get_format())
 
1079
        repo._pack_collection.lock_names()
 
1080
        repo2 = repository.Repository.open('.')
 
1081
        self.assertTrue(repo.get_physical_lock_status())
 
1082
        self.prepare_for_break_lock()
 
1083
        repo2.break_lock()
 
1084
        self.assertFalse(repo.get_physical_lock_status())
 
1085
 
 
1086
    def test_broken_physical_locks_error_on__unlock_names_lock(self):
 
1087
        repo = self.make_repository('.', format=self.get_format())
 
1088
        repo._pack_collection.lock_names()
 
1089
        self.assertTrue(repo.get_physical_lock_status())
 
1090
        repo2 = repository.Repository.open('.')
 
1091
        self.prepare_for_break_lock()
 
1092
        repo2.break_lock()
 
1093
        self.assertRaises(errors.LockBroken, repo._pack_collection._unlock_names)
 
1094
 
 
1095
    def test_fetch_without_find_ghosts_ignores_ghosts(self):
 
1096
        # we want two repositories at this point:
 
1097
        # one with a revision that is a ghost in the other
 
1098
        # repository.
 
1099
        # 'ghost' is present in has_ghost, 'ghost' is absent in 'missing_ghost'.
 
1100
        # 'references' is present in both repositories, and 'tip' is present
 
1101
        # just in has_ghost.
 
1102
        # has_ghost       missing_ghost
 
1103
        #------------------------------
 
1104
        # 'ghost'             -
 
1105
        # 'references'    'references'
 
1106
        # 'tip'               -
 
1107
        # In this test we fetch 'tip' which should not fetch 'ghost'
 
1108
        has_ghost = self.make_repository('has_ghost', format=self.get_format())
 
1109
        missing_ghost = self.make_repository('missing_ghost',
 
1110
            format=self.get_format())
 
1111
 
 
1112
        def add_commit(repo, revision_id, parent_ids):
 
1113
            repo.lock_write()
 
1114
            repo.start_write_group()
 
1115
            inv = inventory.Inventory(revision_id=revision_id)
 
1116
            inv.root.revision = revision_id
 
1117
            root_id = inv.root.file_id
 
1118
            sha1 = repo.add_inventory(revision_id, inv, [])
 
1119
            vf = repo.weave_store.get_weave_or_empty(root_id,
 
1120
                repo.get_transaction())
 
1121
            vf.add_lines(revision_id, [], [])
 
1122
            rev = bzrlib.revision.Revision(timestamp=0,
 
1123
                                           timezone=None,
 
1124
                                           committer="Foo Bar <foo@example.com>",
 
1125
                                           message="Message",
 
1126
                                           inventory_sha1=sha1,
 
1127
                                           revision_id=revision_id)
 
1128
            rev.parent_ids = parent_ids
 
1129
            repo.add_revision(revision_id, rev)
 
1130
            repo.commit_write_group()
 
1131
            repo.unlock()
 
1132
        add_commit(has_ghost, 'ghost', [])
 
1133
        add_commit(has_ghost, 'references', ['ghost'])
 
1134
        add_commit(missing_ghost, 'references', ['ghost'])
 
1135
        add_commit(has_ghost, 'tip', ['references'])
 
1136
        missing_ghost.fetch(has_ghost, 'tip')
 
1137
        # missing ghost now has tip and not ghost.
 
1138
        rev = missing_ghost.get_revision('tip')
 
1139
        inv = missing_ghost.get_inventory('tip')
 
1140
        self.assertRaises(errors.NoSuchRevision,
 
1141
            missing_ghost.get_revision, 'ghost')
 
1142
        self.assertRaises(errors.RevisionNotPresent,
 
1143
            missing_ghost.get_inventory, 'ghost')
 
1144
 
 
1145
 
 
1146
class TestKnitPackSubtrees(TestKnitPackNoSubtrees):
 
1147
 
 
1148
    def get_format(self):
 
1149
        return bzrdir.format_registry.make_bzrdir(
 
1150
            'knitpack-subtree-experimental')
 
1151
 
 
1152
    def check_format(self, t):
 
1153
        self.assertEqualDiff(
 
1154
            "Bazaar pack repository format 1 with subtree support (needs bzr 0.92)\n",
 
1155
            t.get('format').read())
 
1156
 
 
1157
 
 
1158
class TestRepositoryPackCollection(TestCaseWithTransport):
 
1159
 
 
1160
    def get_format(self):
 
1161
        return bzrdir.format_registry.make_bzrdir('knitpack-experimental')
 
1162
 
 
1163
    def test__max_pack_count(self):
 
1164
        """The maximum pack count is a function of the number of revisions."""
 
1165
        format = self.get_format()
 
1166
        repo = self.make_repository('.', format=format)
 
1167
        packs = repo._pack_collection
 
1168
        # no revisions - one pack, so that we can have a revision free repo
 
1169
        # without it blowing up
 
1170
        self.assertEqual(1, packs._max_pack_count(0))
 
1171
        # after that the sum of the digits, - check the first 1-9
 
1172
        self.assertEqual(1, packs._max_pack_count(1))
 
1173
        self.assertEqual(2, packs._max_pack_count(2))
 
1174
        self.assertEqual(3, packs._max_pack_count(3))
 
1175
        self.assertEqual(4, packs._max_pack_count(4))
 
1176
        self.assertEqual(5, packs._max_pack_count(5))
 
1177
        self.assertEqual(6, packs._max_pack_count(6))
 
1178
        self.assertEqual(7, packs._max_pack_count(7))
 
1179
        self.assertEqual(8, packs._max_pack_count(8))
 
1180
        self.assertEqual(9, packs._max_pack_count(9))
 
1181
        # check the boundary cases with two digits for the next decade
 
1182
        self.assertEqual(1, packs._max_pack_count(10))
 
1183
        self.assertEqual(2, packs._max_pack_count(11))
 
1184
        self.assertEqual(10, packs._max_pack_count(19))
 
1185
        self.assertEqual(2, packs._max_pack_count(20))
 
1186
        self.assertEqual(3, packs._max_pack_count(21))
 
1187
        # check some arbitrary big numbers
 
1188
        self.assertEqual(25, packs._max_pack_count(112894))
 
1189
 
 
1190
    def test_pack_distribution_zero(self):
 
1191
        format = self.get_format()
 
1192
        repo = self.make_repository('.', format=format)
 
1193
        packs = repo._pack_collection
 
1194
        self.assertEqual([0], packs.pack_distribution(0))
 
1195
        
 
1196
    def test_pack_distribution_one_to_nine(self):
 
1197
        format = self.get_format()
 
1198
        repo = self.make_repository('.', format=format)
 
1199
        packs = repo._pack_collection
 
1200
        self.assertEqual([1],
 
1201
            packs.pack_distribution(1))
 
1202
        self.assertEqual([1, 1],
 
1203
            packs.pack_distribution(2))
 
1204
        self.assertEqual([1, 1, 1],
 
1205
            packs.pack_distribution(3))
 
1206
        self.assertEqual([1, 1, 1, 1],
 
1207
            packs.pack_distribution(4))
 
1208
        self.assertEqual([1, 1, 1, 1, 1],
 
1209
            packs.pack_distribution(5))
 
1210
        self.assertEqual([1, 1, 1, 1, 1, 1],
 
1211
            packs.pack_distribution(6))
 
1212
        self.assertEqual([1, 1, 1, 1, 1, 1, 1],
 
1213
            packs.pack_distribution(7))
 
1214
        self.assertEqual([1, 1, 1, 1, 1, 1, 1, 1],
 
1215
            packs.pack_distribution(8))
 
1216
        self.assertEqual([1, 1, 1, 1, 1, 1, 1, 1, 1],
 
1217
            packs.pack_distribution(9))
 
1218
 
 
1219
    def test_pack_distribution_stable_at_boundaries(self):
 
1220
        """When there are multi-rev packs the counts are stable."""
 
1221
        format = self.get_format()
 
1222
        repo = self.make_repository('.', format=format)
 
1223
        packs = repo._pack_collection
 
1224
        # in 10s:
 
1225
        self.assertEqual([10], packs.pack_distribution(10))
 
1226
        self.assertEqual([10, 1], packs.pack_distribution(11))
 
1227
        self.assertEqual([10, 10], packs.pack_distribution(20))
 
1228
        self.assertEqual([10, 10, 1], packs.pack_distribution(21))
 
1229
        # 100s
 
1230
        self.assertEqual([100], packs.pack_distribution(100))
 
1231
        self.assertEqual([100, 1], packs.pack_distribution(101))
 
1232
        self.assertEqual([100, 10, 1], packs.pack_distribution(111))
 
1233
        self.assertEqual([100, 100], packs.pack_distribution(200))
 
1234
        self.assertEqual([100, 100, 1], packs.pack_distribution(201))
 
1235
        self.assertEqual([100, 100, 10, 1], packs.pack_distribution(211))
 
1236
 
 
1237
    def test_plan_pack_operations_2009_revisions_skip_all_packs(self):
 
1238
        format = self.get_format()
 
1239
        repo = self.make_repository('.', format=format)
 
1240
        packs = repo._pack_collection
 
1241
        existing_packs = [(2000, "big"), (9, "medium")]
 
1242
        # rev count - 2009 -> 2x1000 + 9x1
 
1243
        pack_operations = packs.plan_autopack_combinations(
 
1244
            existing_packs, [1000, 1000, 1, 1, 1, 1, 1, 1, 1, 1, 1])
 
1245
        self.assertEqual([], pack_operations)
 
1246
 
 
1247
    def test_plan_pack_operations_2010_revisions_skip_all_packs(self):
 
1248
        format = self.get_format()
 
1249
        repo = self.make_repository('.', format=format)
 
1250
        packs = repo._pack_collection
 
1251
        existing_packs = [(2000, "big"), (9, "medium"), (1, "single")]
 
1252
        # rev count - 2010 -> 2x1000 + 1x10
 
1253
        pack_operations = packs.plan_autopack_combinations(
 
1254
            existing_packs, [1000, 1000, 10])
 
1255
        self.assertEqual([], pack_operations)
 
1256
 
 
1257
    def test_plan_pack_operations_2010_combines_smallest_two(self):
 
1258
        format = self.get_format()
 
1259
        repo = self.make_repository('.', format=format)
 
1260
        packs = repo._pack_collection
 
1261
        existing_packs = [(1999, "big"), (9, "medium"), (1, "single2"),
 
1262
            (1, "single1")]
 
1263
        # rev count - 2010 -> 2x1000 + 1x10 (3)
 
1264
        pack_operations = packs.plan_autopack_combinations(
 
1265
            existing_packs, [1000, 1000, 10])
 
1266
        self.assertEqual([[2, ["single2", "single1"]], [0, []]], pack_operations)
 
1267
 
 
1268
    def test_all_packs_none(self):
 
1269
        format = self.get_format()
 
1270
        tree = self.make_branch_and_tree('.', format=format)
 
1271
        tree.lock_read()
 
1272
        self.addCleanup(tree.unlock)
 
1273
        packs = tree.branch.repository._pack_collection
 
1274
        packs.ensure_loaded()
 
1275
        self.assertEqual([], packs.all_packs())
 
1276
 
 
1277
    def test_all_packs_one(self):
 
1278
        format = self.get_format()
 
1279
        tree = self.make_branch_and_tree('.', format=format)
 
1280
        tree.commit('start')
 
1281
        tree.lock_read()
 
1282
        self.addCleanup(tree.unlock)
 
1283
        packs = tree.branch.repository._pack_collection
 
1284
        packs.ensure_loaded()
 
1285
        self.assertEqual([
 
1286
            packs.get_pack_by_name(packs.names()[0])],
 
1287
            packs.all_packs())
 
1288
 
 
1289
    def test_all_packs_two(self):
 
1290
        format = self.get_format()
 
1291
        tree = self.make_branch_and_tree('.', format=format)
 
1292
        tree.commit('start')
 
1293
        tree.commit('continue')
 
1294
        tree.lock_read()
 
1295
        self.addCleanup(tree.unlock)
 
1296
        packs = tree.branch.repository._pack_collection
 
1297
        packs.ensure_loaded()
 
1298
        self.assertEqual([
 
1299
            packs.get_pack_by_name(packs.names()[0]),
 
1300
            packs.get_pack_by_name(packs.names()[1]),
 
1301
            ], packs.all_packs())
 
1302
 
 
1303
    def test_get_pack_by_name(self):
 
1304
        format = self.get_format()
 
1305
        tree = self.make_branch_and_tree('.', format=format)
 
1306
        tree.commit('start')
 
1307
        tree.lock_read()
 
1308
        self.addCleanup(tree.unlock)
 
1309
        packs = tree.branch.repository._pack_collection
 
1310
        packs.ensure_loaded()
 
1311
        name = packs.names()[0]
 
1312
        pack_1 = packs.get_pack_by_name(name)
 
1313
        # the pack should be correctly initialised
 
1314
        rev_index = GraphIndex(packs._index_transport, name + '.rix',
 
1315
            packs._names[name][0])
 
1316
        inv_index = GraphIndex(packs._index_transport, name + '.iix',
 
1317
            packs._names[name][1])
 
1318
        txt_index = GraphIndex(packs._index_transport, name + '.tix',
 
1319
            packs._names[name][2])
 
1320
        sig_index = GraphIndex(packs._index_transport, name + '.six',
 
1321
            packs._names[name][3])
 
1322
        self.assertEqual(pack_repo.ExistingPack(packs._pack_transport,
 
1323
            name, rev_index, inv_index, txt_index, sig_index), pack_1)
 
1324
        # and the same instance should be returned on successive calls.
 
1325
        self.assertTrue(pack_1 is packs.get_pack_by_name(name))
 
1326
 
 
1327
 
 
1328
class TestPack(TestCaseWithTransport):
 
1329
    """Tests for the Pack object."""
 
1330
 
 
1331
    def assertCurrentlyEqual(self, left, right):
 
1332
        self.assertTrue(left == right)
 
1333
        self.assertTrue(right == left)
 
1334
        self.assertFalse(left != right)
 
1335
        self.assertFalse(right != left)
 
1336
 
 
1337
    def assertCurrentlyNotEqual(self, left, right):
 
1338
        self.assertFalse(left == right)
 
1339
        self.assertFalse(right == left)
 
1340
        self.assertTrue(left != right)
 
1341
        self.assertTrue(right != left)
 
1342
 
 
1343
    def test___eq____ne__(self):
 
1344
        left = pack_repo.ExistingPack('', '', '', '', '', '')
 
1345
        right = pack_repo.ExistingPack('', '', '', '', '', '')
 
1346
        self.assertCurrentlyEqual(left, right)
 
1347
        # change all attributes and ensure equality changes as we do.
 
1348
        left.revision_index = 'a'
 
1349
        self.assertCurrentlyNotEqual(left, right)
 
1350
        right.revision_index = 'a'
 
1351
        self.assertCurrentlyEqual(left, right)
 
1352
        left.inventory_index = 'a'
 
1353
        self.assertCurrentlyNotEqual(left, right)
 
1354
        right.inventory_index = 'a'
 
1355
        self.assertCurrentlyEqual(left, right)
 
1356
        left.text_index = 'a'
 
1357
        self.assertCurrentlyNotEqual(left, right)
 
1358
        right.text_index = 'a'
 
1359
        self.assertCurrentlyEqual(left, right)
 
1360
        left.signature_index = 'a'
 
1361
        self.assertCurrentlyNotEqual(left, right)
 
1362
        right.signature_index = 'a'
 
1363
        self.assertCurrentlyEqual(left, right)
 
1364
        left.name = 'a'
 
1365
        self.assertCurrentlyNotEqual(left, right)
 
1366
        right.name = 'a'
 
1367
        self.assertCurrentlyEqual(left, right)
 
1368
        left.transport = 'a'
 
1369
        self.assertCurrentlyNotEqual(left, right)
 
1370
        right.transport = 'a'
 
1371
        self.assertCurrentlyEqual(left, right)
 
1372
 
 
1373
    def test_file_name(self):
 
1374
        pack = pack_repo.ExistingPack('', 'a_name', '', '', '', '')
 
1375
        self.assertEqual('a_name.pack', pack.file_name())
 
1376
 
 
1377
 
 
1378
class TestNewPack(TestCaseWithTransport):
 
1379
    """Tests for pack_repo.NewPack."""
 
1380
 
 
1381
    def test_new_instance_attributes(self):
 
1382
        upload_transport = self.get_transport('upload')
 
1383
        pack_transport = self.get_transport('pack')
 
1384
        index_transport = self.get_transport('index')
 
1385
        upload_transport.mkdir('.')
 
1386
        pack = pack_repo.NewPack(upload_transport, index_transport,
 
1387
            pack_transport)
 
1388
        self.assertIsInstance(pack.revision_index, InMemoryGraphIndex)
 
1389
        self.assertIsInstance(pack.inventory_index, InMemoryGraphIndex)
 
1390
        self.assertIsInstance(pack._hash, type(md5.new()))
 
1391
        self.assertTrue(pack.upload_transport is upload_transport)
 
1392
        self.assertTrue(pack.index_transport is index_transport)
 
1393
        self.assertTrue(pack.pack_transport is pack_transport)
 
1394
        self.assertEqual(None, pack.index_sizes)
 
1395
        self.assertEqual(20, len(pack.random_name))
 
1396
        self.assertIsInstance(pack.random_name, str)
 
1397
        self.assertIsInstance(pack.start_time, float)
 
1398
 
 
1399
 
 
1400
class TestPacker(TestCaseWithTransport):
 
1401
    """Tests for the packs repository Packer class."""
 
1402
 
 
1403
    # To date, this class has been factored out and nothing new added to it;
 
1404
    # thus there are not yet any tests.