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
989
def create_duplicated_dirstate(self):
990
"""Create a dirstate with a deleted and added entries.
992
This grabs a basic_dirstate, and then removes and re adds every entry
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.
1005
new_state = dirstate.DirState.from_tree(tree, 'dirstate')
1011
# But we need to leave state in a read-lock because we already have
1012
# a cleanup scheduled
1014
state._bisect_page_size = 150
982
1015
return tree, state, expected
984
1017
def assertBisect(self, expected, state, paths):
985
1018
"""Assert that bisecting for paths returns the right result.
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
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))
997
1040
def test_bisect_each(self):
998
1041
"""Find a single record using bisect."""
999
1042
tree, state, expected = self.create_basic_dirstate()
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'])
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'])
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'])
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'])
1082
def test_bisect_duplicate_paths(self):
1083
"""When bisecting for a path, handle multiple entries."""
1084
tree, state, expected = self.create_duplicated_dirstate()
1086
# Update the expected dictionary.
1087
for path in ['a', 'b', 'b/c', 'b/d', 'b/d/e', 'f']:
1088
orig = expected[path]
1090
# This record was deleted in the current tree
1091
expected[path] = (orig[0], [dirstate.DirState.NULL_PARENT_DETAILS,
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])
1098
# Now make sure that both records are properly returned.
1099
self.assertBisect([[expected['a'], expected['a2']]], state, 'a')
1101
def test_bisect_page_size_too_small(self):
1102
"""We should raise an error if we detect a field longer than page_size.
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.
1107
tree, state, expected = self.create_basic_dirstate()
1108
state._bisect_page_size = 50
1109
self.assertRaises(errors.BisectPageSizeTooSmall,
1110
state._bisect, [('b', 'e')])
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'])
1119
self.assertBisect([[expected['a']], None, [expected['b/d']]],
1120
state, ['a', 'foo', 'b/d'])