195
194
tree = self.make_branch_and_tree('tree')
196
paths = ['a', 'b/', 'b/c', 'b/d/', 'b/d/e', 'b-c', 'f']
197
file_ids = ['a-id', 'b-id', 'c-id', 'd-id', 'e-id', 'b-c-id', 'f-id']
195
paths = ['a', 'b/', 'b/c', 'b/d/', 'b/d/e', 'f']
196
file_ids = ['a-id', 'b-id', 'c-id', 'd-id', 'e-id', 'f-id']
198
197
self.build_tree(['tree/' + p for p in paths])
199
198
tree.set_root_id('TREE_ROOT')
200
199
tree.add([p.rstrip('/') for p in paths], file_ids)
201
200
tree.commit('initial', rev_id='rev-1')
202
201
revision_id = 'rev-1'
203
202
# a_packed_stat = dirstate.pack_stat(os.stat('tree/a'))
204
t = self.get_transport('tree')
203
t = self.get_transport().clone('tree')
205
204
a_text = t.get_bytes('a')
206
205
a_sha = osutils.sha_string(a_text)
207
206
a_len = len(a_text)
248
244
('f', '', 0, False, null_stat),
249
245
('f', e_sha, e_len, False, revision_id),
251
'b-c':(('', 'b-c', 'b-c-id'), [
252
('f', '', 0, False, null_stat),
253
('f', b_c_sha, b_c_len, False, revision_id),
255
247
'f':(('', 'f', 'f-id'), [
256
248
('f', '', 0, False, null_stat),
257
249
('f', f_sha, f_len, False, revision_id),
284
276
tree, state, expected = self.create_basic_dirstate()
285
277
# Now we will just remove and add every file so we get an extra entry
286
278
# per entry. Unversion in reverse order so we handle subdirs
287
tree.unversion(['f-id', 'b-c-id', 'e-id', 'd-id', 'c-id', 'b-id', 'a-id'])
288
tree.add(['a', 'b', 'b/c', 'b/d', 'b/d/e', 'b-c', 'f'],
289
['a-id2', 'b-id2', 'c-id2', 'd-id2', 'e-id2', 'b-c-id2', 'f-id2'])
279
tree.unversion(['f-id', 'e-id', 'd-id', 'c-id', 'b-id', 'a-id'])
280
tree.add(['a', 'b', 'b/c', 'b/d', 'b/d/e', 'f'],
281
['a-id2', 'b-id2', 'c-id2', 'd-id2', 'e-id2', 'f-id2'])
291
283
# Update the expected dictionary.
292
for path in ['a', 'b', 'b/c', 'b/d', 'b/d/e', 'b-c', 'f']:
284
for path in ['a', 'b', 'b/c', 'b/d', 'b/d/e', 'f']:
293
285
orig = expected[path]
294
286
path2 = path + '2'
295
287
# This record was deleted in the current tree
1837
1829
(dir, name) tuples, and sorted according to how _bisect
1840
result = state._bisect(paths)
1832
dir_names = sorted(osutils.split(p) for p in paths)
1833
result = state._bisect(dir_names)
1841
1834
# For now, results are just returned in whatever order we read them.
1842
1835
# We could sort by (dir, name, file_id) or something like that, but in
1843
1836
# the end it would still be fairly arbitrary, and we don't want the
1844
1837
# extra overhead if we can avoid it. So sort everything to make sure
1845
1838
# equality is true
1846
assert len(map_keys) == len(paths)
1839
assert len(map_keys) == len(dir_names)
1848
for path, keys in zip(paths, map_keys):
1841
for dir_name, keys in zip(dir_names, map_keys):
1849
1842
if keys is None:
1850
1843
# This should not be present in the output
1852
expected[path] = sorted(expected_map[k] for k in keys)
1845
expected[dir_name] = sorted(expected_map[k] for k in keys)
1854
# The returned values are just arranged randomly based on when they
1855
# were read, for testing, make sure it is properly sorted.
1847
for dir_name in result:
1848
result[dir_name].sort()
1859
1850
self.assertEqual(expected, result)
1921
1912
# Bisect should be capable of finding multiple entries at the same time
1922
1913
self.assertBisect(expected, [['a'], ['b'], ['f']],
1923
1914
state, ['a', 'b', 'f'])
1915
# ('', 'f') sorts before the others
1924
1916
self.assertBisect(expected, [['f'], ['b/d'], ['b/d/e']],
1925
state, ['f', 'b/d', 'b/d/e'])
1926
self.assertBisect(expected, [['b'], ['b-c'], ['b/c']],
1927
state, ['b', 'b-c', 'b/c'])
1917
state, ['b/d', 'b/d/e', 'f'])
1929
1919
def test_bisect_one_page(self):
1930
1920
"""Test bisect when there is only 1 page to read"""
1936
1926
self.assertBisect(expected,[['b/c']], state, ['b/c'])
1937
1927
self.assertBisect(expected,[['b/d']], state, ['b/d'])
1938
1928
self.assertBisect(expected,[['b/d/e']], state, ['b/d/e'])
1939
self.assertBisect(expected,[['b-c']], state, ['b-c'])
1940
1929
self.assertBisect(expected,[['f']], state, ['f'])
1941
1930
self.assertBisect(expected,[['a'], ['b'], ['f']],
1942
1931
state, ['a', 'b', 'f'])
1943
self.assertBisect(expected, [['b/d'], ['b/d/e'], ['f']],
1932
# ('', 'f') sorts before the others
1933
self.assertBisect(expected, [['f'], ['b/d'], ['b/d/e']],
1944
1934
state, ['b/d', 'b/d/e', 'f'])
1945
self.assertBisect(expected, [['b'], ['b/c'], ['b-c']],
1946
state, ['b', 'b/c', 'b-c'])
1948
1936
def test_bisect_duplicate_paths(self):
1949
1937
"""When bisecting for a path, handle multiple entries."""
2001
1986
def test_bisect_dirblocks(self):
2002
1987
tree, state, expected = self.create_duplicated_dirstate()
2003
1988
self.assertBisectDirBlocks(expected,
2004
[['', 'a', 'a2', 'b', 'b2', 'b-c', 'b-c2', 'f', 'f2']],
1989
[['', 'a', 'a2', 'b', 'b2', 'f', 'f2']], state, [''])
2006
1990
self.assertBisectDirBlocks(expected,
2007
1991
[['b/c', 'b/c2', 'b/d', 'b/d2']], state, ['b'])
2008
1992
self.assertBisectDirBlocks(expected,
2009
1993
[['b/d/e', 'b/d/e2']], state, ['b/d'])
2010
1994
self.assertBisectDirBlocks(expected,
2011
[['', 'a', 'a2', 'b', 'b2', 'b-c', 'b-c2', 'f', 'f2'],
1995
[['', 'a', 'a2', 'b', 'b2', 'f', 'f2'],
2012
1996
['b/c', 'b/c2', 'b/d', 'b/d2'],
2013
1997
['b/d/e', 'b/d/e2'],
2014
1998
], state, ['', 'b', 'b/d'])
2029
2013
self.assertBisectRecursive(expected, ['a'], state, ['a'])
2030
2014
self.assertBisectRecursive(expected, ['b/c'], state, ['b/c'])
2031
2015
self.assertBisectRecursive(expected, ['b/d/e'], state, ['b/d/e'])
2032
self.assertBisectRecursive(expected, ['b-c'], state, ['b-c'])
2033
2016
self.assertBisectRecursive(expected, ['b/d', 'b/d/e'],
2034
2017
state, ['b/d'])
2035
2018
self.assertBisectRecursive(expected, ['b', 'b/c', 'b/d', 'b/d/e'],
2037
self.assertBisectRecursive(expected, ['', 'a', 'b', 'b-c', 'f', 'b/c',
2020
self.assertBisectRecursive(expected, ['', 'a', 'b', 'f', 'b/c',
2038
2021
'b/d', 'b/d/e'],
2050
class TestBisectDirblock(TestCase):
2051
"""Test that bisect_dirblock() returns the expected values.
2053
bisect_dirblock is intended to work like bisect.bisect_left() except it
2054
knows it is working on dirblocks and that dirblocks are sorted by ('path',
2055
'to', 'foo') chunks rather than by raw 'path/to/foo'.
2058
def assertBisect(self, dirblocks, split_dirblocks, path, *args, **kwargs):
2059
"""Assert that bisect_split works like bisect_left on the split paths.
2061
:param dirblocks: A list of (path, [info]) pairs.
2062
:param split_dirblocks: A list of ((split, path), [info]) pairs.
2063
:param path: The path we are indexing.
2065
All other arguments will be passed along.
2067
bisect_split_idx = dirstate.bisect_dirblock(dirblocks, path,
2069
split_dirblock = (path.split('/'), [])
2070
bisect_left_idx = bisect.bisect_left(split_dirblocks, split_dirblock,
2072
self.assertEqual(bisect_left_idx, bisect_split_idx,
2073
'bisect_split disagreed. %s != %s'
2075
% (bisect_left_idx, bisect_split_idx, path)
2078
def paths_to_dirblocks(self, paths):
2079
"""Convert a list of paths into dirblock form.
2081
Also, ensure that the paths are in proper sorted order.
2083
dirblocks = [(path, []) for path in paths]
2084
split_dirblocks = [(path.split('/'), []) for path in paths]
2085
self.assertEqual(sorted(split_dirblocks), split_dirblocks)
2086
return dirblocks, split_dirblocks
2088
def test_simple(self):
2089
"""In the simple case it works just like bisect_left"""
2090
paths = ['', 'a', 'b', 'c', 'd']
2091
dirblocks, split_dirblocks = self.paths_to_dirblocks(paths)
2093
self.assertBisect(dirblocks, split_dirblocks, path)
2094
self.assertBisect(dirblocks, split_dirblocks, '_')
2095
self.assertBisect(dirblocks, split_dirblocks, 'aa')
2096
self.assertBisect(dirblocks, split_dirblocks, 'bb')
2097
self.assertBisect(dirblocks, split_dirblocks, 'cc')
2098
self.assertBisect(dirblocks, split_dirblocks, 'dd')
2099
self.assertBisect(dirblocks, split_dirblocks, 'a/a')
2100
self.assertBisect(dirblocks, split_dirblocks, 'b/b')
2101
self.assertBisect(dirblocks, split_dirblocks, 'c/c')
2102
self.assertBisect(dirblocks, split_dirblocks, 'd/d')
2104
def test_involved(self):
2105
"""This is where bisect_left diverges slightly."""
2107
'a/a', 'a/a/a', 'a/a/z', 'a/a-a', 'a/a-z',
2108
'a/z', 'a/z/a', 'a/z/z', 'a/z-a', 'a/z-z',
2110
'z', 'z/a/a', 'z/a/z', 'z/a-a', 'z/a-z',
2111
'z/z', 'z/z/a', 'z/z/z', 'z/z-a', 'z/z-z',
2114
dirblocks, split_dirblocks = self.paths_to_dirblocks(paths)
2116
self.assertBisect(dirblocks, split_dirblocks, path)
2118
def test_involved_cached(self):
2119
"""This is where bisect_left diverges slightly."""
2121
'a/a', 'a/a/a', 'a/a/z', 'a/a-a', 'a/a-z',
2122
'a/z', 'a/z/a', 'a/z/z', 'a/z-a', 'a/z-z',
2124
'z', 'z/a/a', 'z/a/z', 'z/a-a', 'z/a-z',
2125
'z/z', 'z/z/a', 'z/z/z', 'z/z-a', 'z/z-z',
2129
dirblocks, split_dirblocks = self.paths_to_dirblocks(paths)
2131
self.assertBisect(dirblocks, split_dirblocks, path, cache=cache)
2067
2134
class TestDirstateValidation(TestCaseWithDirState):
2069
2136
def test_validate_correct_dirstate(self):