~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_repository.py

1st cut merge of bzr.dev r3907

Show diffs side-by-side

added added

removed removed

Lines of Context:
16
16
 
17
17
"""Tests for the Repository facility that are not interface tests.
18
18
 
19
 
For interface tests see tests/repository_implementations/*.py.
 
19
For interface tests see tests/per_repository/*.py.
20
20
 
21
21
For concrete class tests see this file, and for storage formats tests
22
22
also see this file.
23
23
"""
24
24
 
25
 
import md5
26
25
from stat import S_ISDIR
27
26
from StringIO import StringIO
28
27
 
33
32
                           UnsupportedFormatError,
34
33
                           )
35
34
from bzrlib import graph
 
35
from bzrlib.btree_index import BTreeBuilder, BTreeGraphIndex
36
36
from bzrlib.index import GraphIndex, InMemoryGraphIndex
37
37
from bzrlib.repository import RepositoryFormat
38
38
from bzrlib.smart import server
52
52
    bzrdir,
53
53
    errors,
54
54
    inventory,
 
55
    osutils,
55
56
    progress,
56
57
    repository,
57
58
    revision as _mod_revision,
472
473
class DummyRepository(object):
473
474
    """A dummy repository for testing."""
474
475
 
 
476
    _format = None
475
477
    _serializer = None
476
478
 
477
479
    def supports_rich_root(self):
478
480
        return False
479
481
 
 
482
    def get_graph(self):
 
483
        raise NotImplementedError
 
484
 
 
485
    def get_parent_map(self, revision_ids):
 
486
        raise NotImplementedError
 
487
 
480
488
 
481
489
class InterDummy(repository.InterRepository):
482
490
    """An inter-repository optimised code path for DummyRepository.
736
744
        """
737
745
        broken_repo = self.make_broken_repository()
738
746
        empty_repo = self.make_repository('empty-repo')
739
 
        self.assertRaises(errors.RevisionNotPresent, empty_repo.fetch, broken_repo)
 
747
        self.assertRaises((errors.RevisionNotPresent, errors.BzrCheckError),
 
748
                          empty_repo.fetch, broken_repo)
740
749
 
741
750
 
742
751
class TestRepositoryPackCollection(TestCaseWithTransport):
744
753
    def get_format(self):
745
754
        return bzrdir.format_registry.make_bzrdir('pack-0.92')
746
755
 
 
756
    def get_packs(self):
 
757
        format = self.get_format()
 
758
        repo = self.make_repository('.', format=format)
 
759
        return repo._pack_collection
 
760
 
747
761
    def test__max_pack_count(self):
748
762
        """The maximum pack count is a function of the number of revisions."""
749
 
        format = self.get_format()
750
 
        repo = self.make_repository('.', format=format)
751
 
        packs = repo._pack_collection
752
763
        # no revisions - one pack, so that we can have a revision free repo
753
764
        # without it blowing up
 
765
        packs = self.get_packs()
754
766
        self.assertEqual(1, packs._max_pack_count(0))
755
767
        # after that the sum of the digits, - check the first 1-9
756
768
        self.assertEqual(1, packs._max_pack_count(1))
772
784
        self.assertEqual(25, packs._max_pack_count(112894))
773
785
 
774
786
    def test_pack_distribution_zero(self):
775
 
        format = self.get_format()
776
 
        repo = self.make_repository('.', format=format)
777
 
        packs = repo._pack_collection
 
787
        packs = self.get_packs()
778
788
        self.assertEqual([0], packs.pack_distribution(0))
779
789
 
780
790
    def test_ensure_loaded_unlocked(self):
781
 
        format = self.get_format()
782
 
        repo = self.make_repository('.', format=format)
 
791
        packs = self.get_packs()
783
792
        self.assertRaises(errors.ObjectNotLocked,
784
 
                          repo._pack_collection.ensure_loaded)
 
793
                          packs.ensure_loaded)
785
794
 
786
795
    def test_pack_distribution_one_to_nine(self):
787
 
        format = self.get_format()
788
 
        repo = self.make_repository('.', format=format)
789
 
        packs = repo._pack_collection
 
796
        packs = self.get_packs()
790
797
        self.assertEqual([1],
791
798
            packs.pack_distribution(1))
792
799
        self.assertEqual([1, 1],
808
815
 
809
816
    def test_pack_distribution_stable_at_boundaries(self):
810
817
        """When there are multi-rev packs the counts are stable."""
811
 
        format = self.get_format()
812
 
        repo = self.make_repository('.', format=format)
813
 
        packs = repo._pack_collection
 
818
        packs = self.get_packs()
814
819
        # in 10s:
815
820
        self.assertEqual([10], packs.pack_distribution(10))
816
821
        self.assertEqual([10, 1], packs.pack_distribution(11))
825
830
        self.assertEqual([100, 100, 10, 1], packs.pack_distribution(211))
826
831
 
827
832
    def test_plan_pack_operations_2009_revisions_skip_all_packs(self):
828
 
        format = self.get_format()
829
 
        repo = self.make_repository('.', format=format)
830
 
        packs = repo._pack_collection
 
833
        packs = self.get_packs()
831
834
        existing_packs = [(2000, "big"), (9, "medium")]
832
835
        # rev count - 2009 -> 2x1000 + 9x1
833
836
        pack_operations = packs.plan_autopack_combinations(
835
838
        self.assertEqual([], pack_operations)
836
839
 
837
840
    def test_plan_pack_operations_2010_revisions_skip_all_packs(self):
838
 
        format = self.get_format()
839
 
        repo = self.make_repository('.', format=format)
840
 
        packs = repo._pack_collection
 
841
        packs = self.get_packs()
841
842
        existing_packs = [(2000, "big"), (9, "medium"), (1, "single")]
842
843
        # rev count - 2010 -> 2x1000 + 1x10
843
844
        pack_operations = packs.plan_autopack_combinations(
845
846
        self.assertEqual([], pack_operations)
846
847
 
847
848
    def test_plan_pack_operations_2010_combines_smallest_two(self):
848
 
        format = self.get_format()
849
 
        repo = self.make_repository('.', format=format)
850
 
        packs = repo._pack_collection
 
849
        packs = self.get_packs()
851
850
        existing_packs = [(1999, "big"), (9, "medium"), (1, "single2"),
852
851
            (1, "single1")]
853
852
        # rev count - 2010 -> 2x1000 + 1x10 (3)
854
853
        pack_operations = packs.plan_autopack_combinations(
855
854
            existing_packs, [1000, 1000, 10])
856
 
        self.assertEqual([[2, ["single2", "single1"]], [0, []]], pack_operations)
 
855
        self.assertEqual([[2, ["single2", "single1"]]], pack_operations)
 
856
 
 
857
    def test_plan_pack_operations_creates_a_single_op(self):
 
858
        packs = self.get_packs()
 
859
        existing_packs = [(50, 'a'), (40, 'b'), (30, 'c'), (10, 'd'),
 
860
                          (10, 'e'), (6, 'f'), (4, 'g')]
 
861
        # rev count 150 -> 1x100 and 5x10
 
862
        # The two size 10 packs do not need to be touched. The 50, 40, 30 would
 
863
        # be combined into a single 120 size pack, and the 6 & 4 would
 
864
        # becombined into a size 10 pack. However, if we have to rewrite them,
 
865
        # we save a pack file with no increased I/O by putting them into the
 
866
        # same file.
 
867
        distribution = packs.pack_distribution(150)
 
868
        pack_operations = packs.plan_autopack_combinations(existing_packs,
 
869
                                                           distribution)
 
870
        self.assertEqual([[130, ['a', 'b', 'c', 'f', 'g']]], pack_operations)
857
871
 
858
872
    def test_all_packs_none(self):
859
873
        format = self.get_format()
911
925
        # and the same instance should be returned on successive calls.
912
926
        self.assertTrue(pack_1 is packs.get_pack_by_name(name))
913
927
 
 
928
    def test_reload_pack_names_new_entry(self):
 
929
        tree = self.make_branch_and_tree('.')
 
930
        tree.lock_write()
 
931
        self.addCleanup(tree.unlock)
 
932
        rev1 = tree.commit('one')
 
933
        rev2 = tree.commit('two')
 
934
        r = repository.Repository.open('.')
 
935
        r.lock_read()
 
936
        self.addCleanup(r.unlock)
 
937
        packs = r._pack_collection
 
938
        packs.ensure_loaded()
 
939
        names = packs.names()
 
940
        # Add a new pack file into the repository
 
941
        rev3 = tree.commit('three')
 
942
        new_names = tree.branch.repository._pack_collection.names()
 
943
        new_name = set(new_names).difference(names)
 
944
        self.assertEqual(1, len(new_name))
 
945
        new_name = new_name.pop()
 
946
        # The old collection hasn't noticed yet
 
947
        self.assertEqual(names, packs.names())
 
948
        self.assertTrue(packs.reload_pack_names())
 
949
        self.assertEqual(new_names, packs.names())
 
950
        # And the repository can access the new revision
 
951
        self.assertEqual({rev3:(rev2,)}, r.get_parent_map([rev3]))
 
952
        self.assertFalse(packs.reload_pack_names())
 
953
 
 
954
    def test_reload_pack_names_added_and_removed(self):
 
955
        tree = self.make_branch_and_tree('.')
 
956
        tree.lock_write()
 
957
        self.addCleanup(tree.unlock)
 
958
        rev1 = tree.commit('one')
 
959
        rev2 = tree.commit('two')
 
960
        r = repository.Repository.open('.')
 
961
        r.lock_read()
 
962
        self.addCleanup(r.unlock)
 
963
        packs = r._pack_collection
 
964
        packs.ensure_loaded()
 
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({rev2:(rev1,)}, r.get_parent_map([rev2]))
 
974
        self.assertFalse(packs.reload_pack_names())
 
975
 
914
976
 
915
977
class TestPack(TestCaseWithTransport):
916
978
    """Tests for the Pack object."""
970
1032
        pack_transport = self.get_transport('pack')
971
1033
        index_transport = self.get_transport('index')
972
1034
        upload_transport.mkdir('.')
973
 
        pack = pack_repo.NewPack(upload_transport, index_transport,
974
 
            pack_transport)
975
 
        self.assertIsInstance(pack.revision_index, InMemoryGraphIndex)
976
 
        self.assertIsInstance(pack.inventory_index, InMemoryGraphIndex)
977
 
        self.assertIsInstance(pack._hash, type(md5.new()))
 
1035
        collection = pack_repo.RepositoryPackCollection(repo=None,
 
1036
            transport=self.get_transport('.'),
 
1037
            index_transport=index_transport,
 
1038
            upload_transport=upload_transport,
 
1039
            pack_transport=pack_transport,
 
1040
            index_builder_class=BTreeBuilder,
 
1041
            index_class=BTreeGraphIndex)
 
1042
        pack = pack_repo.NewPack(collection)
 
1043
        self.assertIsInstance(pack.revision_index, BTreeBuilder)
 
1044
        self.assertIsInstance(pack.inventory_index, BTreeBuilder)
 
1045
        self.assertIsInstance(pack._hash, type(osutils.md5()))
978
1046
        self.assertTrue(pack.upload_transport is upload_transport)
979
1047
        self.assertTrue(pack.index_transport is index_transport)
980
1048
        self.assertTrue(pack.pack_transport is pack_transport)
987
1055
class TestPacker(TestCaseWithTransport):
988
1056
    """Tests for the packs repository Packer class."""
989
1057
 
990
 
    # To date, this class has been factored out and nothing new added to it;
991
 
    # thus there are not yet any tests.
 
1058
    def test_pack_optimizes_pack_order(self):
 
1059
        builder = self.make_branch_builder('.')
 
1060
        builder.start_series()
 
1061
        builder.build_snapshot('A', None, [
 
1062
            ('add', ('', 'root-id', 'directory', None)),
 
1063
            ('add', ('f', 'f-id', 'file', 'content\n'))])
 
1064
        builder.build_snapshot('B', ['A'],
 
1065
            [('modify', ('f-id', 'new-content\n'))])
 
1066
        builder.build_snapshot('C', ['B'],
 
1067
            [('modify', ('f-id', 'third-content\n'))])
 
1068
        builder.build_snapshot('D', ['C'],
 
1069
            [('modify', ('f-id', 'fourth-content\n'))])
 
1070
        b = builder.get_branch()
 
1071
        b.lock_read()
 
1072
        builder.finish_series()
 
1073
        self.addCleanup(b.unlock)
 
1074
        # At this point, we should have 4 pack files available
 
1075
        # Because of how they were built, they correspond to
 
1076
        # ['D', 'C', 'B', 'A']
 
1077
        packs = b.repository._pack_collection.packs
 
1078
        packer = pack_repo.Packer(b.repository._pack_collection,
 
1079
                                  packs, 'testing',
 
1080
                                  revision_ids=['B', 'C'])
 
1081
        # Now, when we are copying the B & C revisions, their pack files should
 
1082
        # be moved to the front of the stack
 
1083
        # The new ordering moves B & C to the front of the .packs attribute,
 
1084
        # and leaves the others in the original order.
 
1085
        new_packs = [packs[1], packs[2], packs[0], packs[3]]
 
1086
        new_pack = packer.pack()
 
1087
        self.assertEqual(new_packs, packer.packs)
 
1088
 
 
1089
 
 
1090
class TestOptimisingPacker(TestCaseWithTransport):
 
1091
    """Tests for the OptimisingPacker class."""
 
1092
 
 
1093
    def get_pack_collection(self):
 
1094
        repo = self.make_repository('.')
 
1095
        return repo._pack_collection
 
1096
 
 
1097
    def test_open_pack_will_optimise(self):
 
1098
        packer = pack_repo.OptimisingPacker(self.get_pack_collection(),
 
1099
                                            [], '.test')
 
1100
        new_pack = packer.open_pack()
 
1101
        self.assertIsInstance(new_pack, pack_repo.NewPack)
 
1102
        self.assertTrue(new_pack.revision_index._optimize_for_size)
 
1103
        self.assertTrue(new_pack.inventory_index._optimize_for_size)
 
1104
        self.assertTrue(new_pack.text_index._optimize_for_size)
 
1105
        self.assertTrue(new_pack.signature_index._optimize_for_size)
992
1106
 
993
1107
 
994
1108
class TestInterDifferingSerializer(TestCaseWithTransport):