~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_dirstate.py

Switch the bisect code to support the fact that we can have
repeated (dir, name) pairs (dir, name, file_id) is the unique key.
Also, fix some edge cases when the page size is small so that we don't get clean
divisions of records per page.

Show diffs side-by-side

added added

removed removed

Lines of Context:
979
979
        self.addCleanup(state.unlock)
980
980
        self.assertEqual(dirstate.DirState.NOT_IN_MEMORY,
981
981
                         state._dirblock_state)
 
982
        # This is code is only really tested if we actually have to make more
 
983
        # than one read, so set the page size to something smaller.
 
984
        # We want it to contain about 2.2 records, so that we have a couple
 
985
        # records that we can read per attempt
 
986
        state._bisect_page_size = 200
 
987
        return tree, state, expected
 
988
 
 
989
    def create_duplicated_dirstate(self):
 
990
        """Create a dirstate with a deleted and added entries.
 
991
 
 
992
        This grabs a basic_dirstate, and then removes and re adds every entry
 
993
        with a new file id.
 
994
        """
 
995
        tree, state, expected = self.create_basic_dirstate()
 
996
        # Now we will just remove and add every file so we get an extra entry
 
997
        # per entry. Unversion in reverse order so we handle subdirs
 
998
        tree.unversion(['f-id', 'e-id', 'd-id', 'c-id', 'b-id', 'a-id'])
 
999
        tree.add(['a', 'b', 'b/c', 'b/d', 'b/d/e', 'f'],
 
1000
                 ['a-id2', 'b-id2', 'c-id2', 'd-id2', 'e-id2', 'f-id2'])
 
1001
        # We will replace the 'dirstate' file underneath 'state', but that is
 
1002
        # okay as lock as we unlock 'state' first.
 
1003
        state.unlock()
 
1004
        try:
 
1005
            new_state = dirstate.DirState.from_tree(tree, 'dirstate')
 
1006
            try:
 
1007
                new_state.save()
 
1008
            finally:
 
1009
                new_state.unlock()
 
1010
        finally:
 
1011
            # But we need to leave state in a read-lock because we already have
 
1012
            # a cleanup scheduled
 
1013
            state.lock_read()
 
1014
        state._bisect_page_size = 150
982
1015
        return tree, state, expected
983
1016
 
984
1017
    def assertBisect(self, expected, state, paths):
985
1018
        """Assert that bisecting for paths returns the right result.
986
1019
 
987
 
        :param expected: The list of entries we expect.
 
1020
        :param expected: The list of extracted entries.
988
1021
        :param state: The DirState object.
989
1022
        :param paths: A list of paths, these will automatically be split into
990
1023
                      (dir, name) tuples, and sorted according to how _bisect
992
1025
        """
993
1026
        dir_names = sorted(osutils.split(p) for p in paths)
994
1027
        result = state._bisect(dir_names)
995
 
        self.assertEqual(expected, result)
 
1028
        # For now, results are just returned in whatever order we read them.
 
1029
        # We could sort by (dir, name, file_id) or something like that, but in
 
1030
        # the end it would still be fairly arbitrary, and we don't want the
 
1031
        # extra overhead if we can avoid it.
 
1032
        for expected_entries, actual_entries in zip(expected, result):
 
1033
            if expected_entries is not None:
 
1034
                expected_entries.sort()
 
1035
            if actual_entries is not None:
 
1036
                actual_entries.sort()
 
1037
            self.assertEqual(expected_entries, actual_entries)
 
1038
        self.assertEqual(len(expected), len(result))
996
1039
 
997
1040
    def test_bisect_each(self):
998
1041
        """Find a single record using bisect."""
999
1042
        tree, state, expected = self.create_basic_dirstate()
1000
1043
 
1001
1044
        # Bisect should return the rows for the specified files.
1002
 
        self.assertBisect([expected['']], state, [''])
1003
 
        self.assertBisect([expected['a']], state, ['a'])
1004
 
        self.assertBisect([expected['b']], state, ['b'])
1005
 
        self.assertBisect([expected['b/c']], state, ['b/c'])
1006
 
        self.assertBisect([expected['b/d']], state, ['b/d'])
1007
 
        self.assertBisect([expected['b/d/e']], state, ['b/d/e'])
1008
 
        self.assertBisect([expected['f']], state, ['f'])
 
1045
        self.assertBisect([[expected['']]], state, [''])
 
1046
        self.assertBisect([[expected['a']]], state, ['a'])
 
1047
        self.assertBisect([[expected['b']]], state, ['b'])
 
1048
        self.assertBisect([[expected['b/c']]], state, ['b/c'])
 
1049
        self.assertBisect([[expected['b/d']]], state, ['b/d'])
 
1050
        self.assertBisect([[expected['b/d/e']]], state, ['b/d/e'])
 
1051
        self.assertBisect([[expected['f']]], state, ['f'])
1009
1052
 
1010
1053
    def test_bisect_multi(self):
1011
1054
        """Bisect can be used to find multiple records at the same time."""
1012
1055
        tree, state, expected = self.create_basic_dirstate()
1013
1056
        # Bisect should be capable of finding multiple entries at the same time
1014
 
        self.assertBisect([expected['a'], expected['b'], expected['f']],
1015
 
                         state, ['a', 'b', 'f'])
1016
 
        # ('', 'f') sorts before the others
1017
 
        self.assertBisect([expected['f'], expected['b/d'], expected['b/d/e']],
1018
 
                         state, ['b/d', 'b/d/e', 'f'])
1019
 
 
1020
 
    def test_bisect_split_pages(self):
1021
 
        """Test bisect when crossing page boundaries"""
1022
 
        tree, state, expected = self.create_basic_dirstate()
1023
 
        # Make sure we can't read all the records in a single page, but also
1024
 
        # that we *can* read a singe entry in the page size.
1025
 
        state._bisect_page_size = 100
1026
 
        # Bisect should be capable of finding multiple entries at the same time
1027
 
        self.assertBisect([expected['a'], expected['b'], expected['f']],
1028
 
                         state, ['a', 'b', 'f'])
1029
 
        # ('', 'f') sorts before the others
1030
 
        self.assertBisect([expected['f'], expected['b/d'], expected['b/d/e']],
1031
 
                         state, ['b/d', 'b/d/e', 'f'])
 
1057
        self.assertBisect([[expected['a']], [expected['b']], [expected['f']]],
 
1058
                          state, ['a', 'b', 'f'])
 
1059
        # ('', 'f') sorts before the others
 
1060
        self.assertBisect([[expected['f']], [expected['b/d']],
 
1061
                           [expected['b/d/e']]],
 
1062
                          state, ['b/d', 'b/d/e', 'f'])
 
1063
 
 
1064
    def test_bisect_one_page(self):
 
1065
        """Test bisect when there is only 1 page to read"""
 
1066
        tree, state, expected = self.create_basic_dirstate()
 
1067
        state._bisect_page_size = 5000
 
1068
        self.assertBisect([[expected['']]], state, [''])
 
1069
        self.assertBisect([[expected['a']]], state, ['a'])
 
1070
        self.assertBisect([[expected['b']]], state, ['b'])
 
1071
        self.assertBisect([[expected['b/c']]], state, ['b/c'])
 
1072
        self.assertBisect([[expected['b/d']]], state, ['b/d'])
 
1073
        self.assertBisect([[expected['b/d/e']]], state, ['b/d/e'])
 
1074
        self.assertBisect([[expected['f']]], state, ['f'])
 
1075
        self.assertBisect([[expected['a']], [expected['b']], [expected['f']]],
 
1076
                          state, ['a', 'b', 'f'])
 
1077
        # ('', 'f') sorts before the others
 
1078
        self.assertBisect([[expected['f']], [expected['b/d']],
 
1079
                           [expected['b/d/e']]],
 
1080
                          state, ['b/d', 'b/d/e', 'f'])
 
1081
 
 
1082
    def test_bisect_duplicate_paths(self):
 
1083
        """When bisecting for a path, handle multiple entries."""
 
1084
        tree, state, expected = self.create_duplicated_dirstate()
 
1085
 
 
1086
        # Update the expected dictionary.
 
1087
        for path in ['a', 'b', 'b/c', 'b/d', 'b/d/e', 'f']:
 
1088
            orig = expected[path]
 
1089
            path2 = path + '2'
 
1090
            # This record was deleted in the current tree
 
1091
            expected[path] = (orig[0], [dirstate.DirState.NULL_PARENT_DETAILS,
 
1092
                                        orig[1][1]])
 
1093
            new_key = (orig[0][0], orig[0][1], orig[0][2]+'2')
 
1094
            # And didn't exist in the basis tree
 
1095
            expected[path2] = (new_key, [orig[1][0],
 
1096
                                         dirstate.DirState.NULL_PARENT_DETAILS])
 
1097
 
 
1098
        # Now make sure that both records are properly returned.
 
1099
        self.assertBisect([[expected['a'], expected['a2']]], state, 'a')
 
1100
 
 
1101
    def test_bisect_page_size_too_small(self):
 
1102
        """We should raise an error if we detect a field longer than page_size.
 
1103
 
 
1104
        This is a safety check, since we know we are reading in pages, and we
 
1105
        expect to fit at least slightly more than 1 record per page.
 
1106
        """
 
1107
        tree, state, expected = self.create_basic_dirstate()
 
1108
        state._bisect_page_size = 50
 
1109
        self.assertRaises(errors.BisectPageSizeTooSmall,
 
1110
                          state._bisect, [('b', 'e')])
 
1111
 
 
1112
    def test_bisect_missing(self):
 
1113
        """Test that bisect return None if it cannot find a path."""
 
1114
        tree, state, expected = self.create_basic_dirstate()
 
1115
        self.assertBisect([None], state, ['foo'])
 
1116
        self.assertBisect([None], state, ['b/foo'])
 
1117
        self.assertBisect([None], state, ['bar/foo'])
 
1118
 
 
1119
        self.assertBisect([[expected['a']], None, [expected['b/d']]],
 
1120
                          state, ['a', 'foo', 'b/d'])