194
195
tree = self.make_branch_and_tree('tree')
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']
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']
197
198
self.build_tree(['tree/' + p for p in paths])
198
199
tree.set_root_id('TREE_ROOT')
199
200
tree.add([p.rstrip('/') for p in paths], file_ids)
200
201
tree.commit('initial', rev_id='rev-1')
201
202
revision_id = 'rev-1'
202
203
# a_packed_stat = dirstate.pack_stat(os.stat('tree/a'))
203
t = self.get_transport().clone('tree')
204
t = self.get_transport('tree')
204
205
a_text = t.get_bytes('a')
205
206
a_sha = osutils.sha_string(a_text)
206
207
a_len = len(a_text)
244
248
('f', '', 0, False, null_stat),
245
249
('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),
247
255
'f':(('', 'f', 'f-id'), [
248
256
('f', '', 0, False, null_stat),
249
257
('f', f_sha, f_len, False, revision_id),
276
284
tree, state, expected = self.create_basic_dirstate()
277
285
# Now we will just remove and add every file so we get an extra entry
278
286
# per entry. Unversion in reverse order so we handle subdirs
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'])
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'])
283
291
# Update the expected dictionary.
284
for path in ['a', 'b', 'b/c', 'b/d', 'b/d/e', 'f']:
292
for path in ['a', 'b', 'b/c', 'b/d', 'b/d/e', 'b-c', 'f']:
285
293
orig = expected[path]
286
294
path2 = path + '2'
287
295
# This record was deleted in the current tree
688
700
# This will unlock it
689
701
self.check_state_with_reopen(expected_result, state)
703
def test_set_state_from_inventory_mixed_paths(self):
704
tree1 = self.make_branch_and_tree('tree1')
705
self.build_tree(['tree1/a/', 'tree1/a/b/', 'tree1/a-b/',
706
'tree1/a/b/foo', 'tree1/a-b/bar'])
709
tree1.add(['a', 'a/b', 'a-b', 'a/b/foo', 'a-b/bar'],
710
['a-id', 'b-id', 'a-b-id', 'foo-id', 'bar-id'])
711
tree1.commit('rev1', rev_id='rev1')
712
root_id = tree1.get_root_id()
713
inv = tree1.inventory
716
expected_result1 = [('', '', root_id, 'd'),
717
('', 'a', 'a-id', 'd'),
718
('', 'a-b', 'a-b-id', 'd'),
719
('a', 'b', 'b-id', 'd'),
720
('a/b', 'foo', 'foo-id', 'f'),
721
('a-b', 'bar', 'bar-id', 'f'),
723
expected_result2 = [('', '', root_id, 'd'),
724
('', 'a', 'a-id', 'd'),
725
('', 'a-b', 'a-b-id', 'd'),
726
('a-b', 'bar', 'bar-id', 'f'),
728
state = dirstate.DirState.initialize('dirstate')
730
state.set_state_from_inventory(inv)
732
for entry in state._iter_entries():
733
values.append(entry[0] + entry[1][0][:1])
734
self.assertEqual(expected_result1, values)
736
state.set_state_from_inventory(inv)
738
for entry in state._iter_entries():
739
values.append(entry[0] + entry[1][0][:1])
740
self.assertEqual(expected_result2, values)
691
744
def test_set_path_id_no_parents(self):
692
745
"""The id of a path can be changed trivally with no parents."""
693
746
state = dirstate.DirState.initialize('dirstate')
1448
1501
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
1449
1502
state._dirblock_state)
1503
self.assertEqual([('f', '', 14, False, dirstate.DirState.NULLSTAT)],
1452
1507
# However, if we move the clock forward so the file is considered
1453
# "stable", it should just returned the cached value.
1454
state.adjust_time(20)
1508
# "stable", it should just cache the value.
1509
state.adjust_time(+20)
1455
1510
link_or_sha1 = state.update_entry(entry, abspath='a',
1456
1511
stat_value=stat_value)
1457
1512
self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
1459
1514
self.assertEqual([('sha1', 'a'), ('is_exec', mode, False),
1460
1515
('sha1', 'a'), ('is_exec', mode, False),
1516
('sha1', 'a'), ('is_exec', mode, False),
1518
self.assertEqual([('f', link_or_sha1, 14, False, packed_stat)],
1463
def test_update_entry_no_stat_value(self):
1464
"""Passing the stat_value is optional."""
1465
state, entry = self.get_state_with_a()
1466
state.adjust_time(-10) # Make sure the file looks new
1467
self.build_tree(['a'])
1468
# Add one where we don't provide the stat or sha already
1469
link_or_sha1 = state.update_entry(entry, abspath='a')
1521
# Subsequent calls will just return the cached value
1522
link_or_sha1 = state.update_entry(entry, abspath='a',
1523
stat_value=stat_value)
1470
1524
self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
1472
stat_value = os.lstat('a')
1473
self.assertEqual([('lstat', 'a'), ('sha1', 'a'),
1474
('is_exec', stat_value.st_mode, False),
1526
self.assertEqual([('sha1', 'a'), ('is_exec', mode, False),
1527
('sha1', 'a'), ('is_exec', mode, False),
1528
('sha1', 'a'), ('is_exec', mode, False),
1530
self.assertEqual([('f', link_or_sha1, 14, False, packed_stat)],
1477
1533
def test_update_entry_symlink(self):
1478
1534
"""Update entry should read symlinks."""
1503
1559
stat_value=stat_value)
1504
1560
self.assertEqual('target', link_or_sha1)
1505
1561
self.assertEqual([('read_link', 'a', ''),
1506
('read_link', 'a', 'target'),
1562
('read_link', 'a', ''),
1564
self.assertEqual([('l', '', 6, False, dirstate.DirState.NULLSTAT)],
1508
1566
state.adjust_time(+20) # Skip into the future, all files look old
1509
1567
link_or_sha1 = state.update_entry(entry, abspath='a',
1510
1568
stat_value=stat_value)
1511
1569
self.assertEqual('target', link_or_sha1)
1512
# There should not be a new read_link call.
1513
# (this is a weak assertion, because read_link is fairly inexpensive,
1514
# versus the number of symlinks that we would have)
1515
self.assertEqual([('read_link', 'a', ''),
1516
('read_link', 'a', 'target'),
1570
# We need to re-read the link because only now can we cache it
1571
self.assertEqual([('read_link', 'a', ''),
1572
('read_link', 'a', ''),
1573
('read_link', 'a', ''),
1575
self.assertEqual([('l', 'target', 6, False, packed_stat)],
1578
# Another call won't re-read the link
1579
self.assertEqual([('read_link', 'a', ''),
1580
('read_link', 'a', ''),
1581
('read_link', 'a', ''),
1583
link_or_sha1 = state.update_entry(entry, abspath='a',
1584
stat_value=stat_value)
1585
self.assertEqual('target', link_or_sha1)
1586
self.assertEqual([('l', 'target', 6, False, packed_stat)],
1589
def do_update_entry(self, state, entry, abspath):
1590
stat_value = os.lstat(abspath)
1591
return state.update_entry(entry, abspath, stat_value)
1519
1593
def test_update_entry_dir(self):
1520
1594
state, entry = self.get_state_with_a()
1521
1595
self.build_tree(['a/'])
1522
self.assertIs(None, state.update_entry(entry, 'a'))
1596
self.assertIs(None, self.do_update_entry(state, entry, 'a'))
1598
def test_update_entry_dir_unchanged(self):
1599
state, entry = self.get_state_with_a()
1600
self.build_tree(['a/'])
1601
state.adjust_time(+20)
1602
self.assertIs(None, self.do_update_entry(state, entry, 'a'))
1603
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
1604
state._dirblock_state)
1606
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1607
state._dirblock_state)
1608
self.assertIs(None, self.do_update_entry(state, entry, 'a'))
1609
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1610
state._dirblock_state)
1612
def test_update_entry_file_unchanged(self):
1613
state, entry = self.get_state_with_a()
1614
self.build_tree(['a'])
1615
sha1sum = 'b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6'
1616
state.adjust_time(+20)
1617
self.assertEqual(sha1sum, self.do_update_entry(state, entry, 'a'))
1618
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
1619
state._dirblock_state)
1621
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1622
state._dirblock_state)
1623
self.assertEqual(sha1sum, self.do_update_entry(state, entry, 'a'))
1624
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1625
state._dirblock_state)
1524
1627
def create_and_test_file(self, state, entry):
1525
1628
"""Create a file at 'a' and verify the state finds it.
1569
1672
stat_value = os.lstat('a')
1570
1673
packed_stat = dirstate.pack_stat(stat_value)
1572
link_or_sha1 = state.update_entry(entry, abspath='a')
1675
link_or_sha1 = self.do_update_entry(state, entry, abspath='a')
1573
1676
self.assertEqual('path/to/foo', link_or_sha1)
1574
1677
self.assertEqual([('l', 'path/to/foo', 11, False, packed_stat)],
1576
1679
return packed_stat
1578
def test_update_missing_file(self):
1579
state, entry = self.get_state_with_a()
1580
packed_stat = self.create_and_test_file(state, entry)
1581
# Now if we delete the file, update_entry should recover and
1584
self.assertIs(None, state.update_entry(entry, abspath='a'))
1585
# And the record shouldn't be changed.
1586
digest = 'b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6'
1587
self.assertEqual([('f', digest, 14, False, packed_stat)],
1590
def test_update_missing_dir(self):
1591
state, entry = self.get_state_with_a()
1592
packed_stat = self.create_and_test_dir(state, entry)
1593
# Now if we delete the directory, update_entry should recover and
1596
self.assertIs(None, state.update_entry(entry, abspath='a'))
1597
self.assertEqual([('d', '', 0, False, packed_stat)], entry[1])
1599
def test_update_missing_symlink(self):
1600
if not osutils.has_symlinks():
1601
# PlatformDeficiency / TestSkipped
1602
raise TestSkipped("No symlink support")
1603
state, entry = self.get_state_with_a()
1604
packed_stat = self.create_and_test_symlink(state, entry)
1606
self.assertIs(None, state.update_entry(entry, abspath='a'))
1607
# And the record shouldn't be changed.
1608
self.assertEqual([('l', 'path/to/foo', 11, False, packed_stat)],
1611
1681
def test_update_file_to_dir(self):
1612
1682
"""If a file changes to a directory we return None for the sha.
1613
1683
We also update the inventory record.
1615
1685
state, entry = self.get_state_with_a()
1686
# The file sha1 won't be cached unless the file is old
1687
state.adjust_time(+10)
1616
1688
self.create_and_test_file(state, entry)
1618
1690
self.create_and_test_dir(state, entry)
1677
1759
packed_stat = dirstate.pack_stat(stat_value)
1679
1761
state.adjust_time(-10) # Make sure everything is new
1680
# Make sure it wants to kkkkkkkk
1681
1762
state.update_entry(entry, abspath='a', stat_value=stat_value)
1683
1764
# The row is updated, but the executable bit stays set.
1765
self.assertEqual([('f', '', 14, True, dirstate.DirState.NULLSTAT)],
1768
# Make the disk object look old enough to cache
1769
state.adjust_time(+20)
1684
1770
digest = 'b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6'
1771
state.update_entry(entry, abspath='a', stat_value=stat_value)
1685
1772
self.assertEqual([('f', digest, 14, True, packed_stat)], entry[1])
1751
1837
(dir, name) tuples, and sorted according to how _bisect
1754
dir_names = sorted(osutils.split(p) for p in paths)
1755
result = state._bisect(dir_names)
1840
result = state._bisect(paths)
1756
1841
# For now, results are just returned in whatever order we read them.
1757
1842
# We could sort by (dir, name, file_id) or something like that, but in
1758
1843
# the end it would still be fairly arbitrary, and we don't want the
1759
1844
# extra overhead if we can avoid it. So sort everything to make sure
1760
1845
# equality is true
1761
assert len(map_keys) == len(dir_names)
1846
assert len(map_keys) == len(paths)
1763
for dir_name, keys in zip(dir_names, map_keys):
1848
for path, keys in zip(paths, map_keys):
1764
1849
if keys is None:
1765
1850
# This should not be present in the output
1767
expected[dir_name] = sorted(expected_map[k] for k in keys)
1852
expected[path] = sorted(expected_map[k] for k in keys)
1769
for dir_name in result:
1770
result[dir_name].sort()
1854
# The returned values are just arranged randomly based on when they
1855
# were read, for testing, make sure it is properly sorted.
1772
1859
self.assertEqual(expected, result)
1834
1921
# Bisect should be capable of finding multiple entries at the same time
1835
1922
self.assertBisect(expected, [['a'], ['b'], ['f']],
1836
1923
state, ['a', 'b', 'f'])
1837
# ('', 'f') sorts before the others
1838
1924
self.assertBisect(expected, [['f'], ['b/d'], ['b/d/e']],
1839
state, ['b/d', 'b/d/e', 'f'])
1925
state, ['f', 'b/d', 'b/d/e'])
1926
self.assertBisect(expected, [['b'], ['b-c'], ['b/c']],
1927
state, ['b', 'b-c', 'b/c'])
1841
1929
def test_bisect_one_page(self):
1842
1930
"""Test bisect when there is only 1 page to read"""
1848
1936
self.assertBisect(expected,[['b/c']], state, ['b/c'])
1849
1937
self.assertBisect(expected,[['b/d']], state, ['b/d'])
1850
1938
self.assertBisect(expected,[['b/d/e']], state, ['b/d/e'])
1939
self.assertBisect(expected,[['b-c']], state, ['b-c'])
1851
1940
self.assertBisect(expected,[['f']], state, ['f'])
1852
1941
self.assertBisect(expected,[['a'], ['b'], ['f']],
1853
1942
state, ['a', 'b', 'f'])
1854
# ('', 'f') sorts before the others
1855
self.assertBisect(expected, [['f'], ['b/d'], ['b/d/e']],
1943
self.assertBisect(expected, [['b/d'], ['b/d/e'], ['f']],
1856
1944
state, ['b/d', 'b/d/e', 'f'])
1945
self.assertBisect(expected, [['b'], ['b/c'], ['b-c']],
1946
state, ['b', 'b/c', 'b-c'])
1858
1948
def test_bisect_duplicate_paths(self):
1859
1949
"""When bisecting for a path, handle multiple entries."""
1908
2001
def test_bisect_dirblocks(self):
1909
2002
tree, state, expected = self.create_duplicated_dirstate()
1910
2003
self.assertBisectDirBlocks(expected,
1911
[['', 'a', 'a2', 'b', 'b2', 'f', 'f2']], state, [''])
2004
[['', 'a', 'a2', 'b', 'b2', 'b-c', 'b-c2', 'f', 'f2']],
1912
2006
self.assertBisectDirBlocks(expected,
1913
2007
[['b/c', 'b/c2', 'b/d', 'b/d2']], state, ['b'])
1914
2008
self.assertBisectDirBlocks(expected,
1915
2009
[['b/d/e', 'b/d/e2']], state, ['b/d'])
1916
2010
self.assertBisectDirBlocks(expected,
1917
[['', 'a', 'a2', 'b', 'b2', 'f', 'f2'],
2011
[['', 'a', 'a2', 'b', 'b2', 'b-c', 'b-c2', 'f', 'f2'],
1918
2012
['b/c', 'b/c2', 'b/d', 'b/d2'],
1919
2013
['b/d/e', 'b/d/e2'],
1920
2014
], state, ['', 'b', 'b/d'])
1935
2029
self.assertBisectRecursive(expected, ['a'], state, ['a'])
1936
2030
self.assertBisectRecursive(expected, ['b/c'], state, ['b/c'])
1937
2031
self.assertBisectRecursive(expected, ['b/d/e'], state, ['b/d/e'])
2032
self.assertBisectRecursive(expected, ['b-c'], state, ['b-c'])
1938
2033
self.assertBisectRecursive(expected, ['b/d', 'b/d/e'],
1939
2034
state, ['b/d'])
1940
2035
self.assertBisectRecursive(expected, ['b', 'b/c', 'b/d', 'b/d/e'],
1942
self.assertBisectRecursive(expected, ['', 'a', 'b', 'f', 'b/c',
2037
self.assertBisectRecursive(expected, ['', 'a', 'b', 'b-c', 'f', 'b/c',
1943
2038
'b/d', 'b/d/e'],
1972
class TestBisectDirblock(TestCase):
1973
"""Test that bisect_dirblock() returns the expected values.
1975
bisect_dirblock is intended to work like bisect.bisect_left() except it
1976
knows it is working on dirblocks and that dirblocks are sorted by ('path',
1977
'to', 'foo') chunks rather than by raw 'path/to/foo'.
1980
def assertBisect(self, dirblocks, split_dirblocks, path, *args, **kwargs):
1981
"""Assert that bisect_split works like bisect_left on the split paths.
1983
:param dirblocks: A list of (path, [info]) pairs.
1984
:param split_dirblocks: A list of ((split, path), [info]) pairs.
1985
:param path: The path we are indexing.
1987
All other arguments will be passed along.
1989
bisect_split_idx = dirstate.bisect_dirblock(dirblocks, path,
1991
split_dirblock = (path.split('/'), [])
1992
bisect_left_idx = bisect.bisect_left(split_dirblocks, split_dirblock,
1994
self.assertEqual(bisect_left_idx, bisect_split_idx,
1995
'bisect_split disagreed. %s != %s'
1997
% (bisect_left_idx, bisect_split_idx, path)
2000
def paths_to_dirblocks(self, paths):
2001
"""Convert a list of paths into dirblock form.
2003
Also, ensure that the paths are in proper sorted order.
2005
dirblocks = [(path, []) for path in paths]
2006
split_dirblocks = [(path.split('/'), []) for path in paths]
2007
self.assertEqual(sorted(split_dirblocks), split_dirblocks)
2008
return dirblocks, split_dirblocks
2010
def test_simple(self):
2011
"""In the simple case it works just like bisect_left"""
2012
paths = ['', 'a', 'b', 'c', 'd']
2013
dirblocks, split_dirblocks = self.paths_to_dirblocks(paths)
2015
self.assertBisect(dirblocks, split_dirblocks, path)
2016
self.assertBisect(dirblocks, split_dirblocks, '_')
2017
self.assertBisect(dirblocks, split_dirblocks, 'aa')
2018
self.assertBisect(dirblocks, split_dirblocks, 'bb')
2019
self.assertBisect(dirblocks, split_dirblocks, 'cc')
2020
self.assertBisect(dirblocks, split_dirblocks, 'dd')
2021
self.assertBisect(dirblocks, split_dirblocks, 'a/a')
2022
self.assertBisect(dirblocks, split_dirblocks, 'b/b')
2023
self.assertBisect(dirblocks, split_dirblocks, 'c/c')
2024
self.assertBisect(dirblocks, split_dirblocks, 'd/d')
2026
def test_involved(self):
2027
"""This is where bisect_left diverges slightly."""
2029
'a/a', 'a/a/a', 'a/a/z', 'a/a-a', 'a/a-z',
2030
'a/z', 'a/z/a', 'a/z/z', 'a/z-a', 'a/z-z',
2032
'z', 'z/a/a', 'z/a/z', 'z/a-a', 'z/a-z',
2033
'z/z', 'z/z/a', 'z/z/z', 'z/z-a', 'z/z-z',
2036
dirblocks, split_dirblocks = self.paths_to_dirblocks(paths)
2038
self.assertBisect(dirblocks, split_dirblocks, path)
2040
def test_involved_cached(self):
2041
"""This is where bisect_left diverges slightly."""
2043
'a/a', 'a/a/a', 'a/a/z', 'a/a-a', 'a/a-z',
2044
'a/z', 'a/z/a', 'a/z/z', 'a/z-a', 'a/z-z',
2046
'z', 'z/a/a', 'z/a/z', 'z/a-a', 'z/a-z',
2047
'z/z', 'z/z/a', 'z/z/z', 'z/z-a', 'z/z-z',
2051
dirblocks, split_dirblocks = self.paths_to_dirblocks(paths)
2053
self.assertBisect(dirblocks, split_dirblocks, path, cache=cache)
2056
2067
class TestDirstateValidation(TestCaseWithDirState):
2058
2069
def test_validate_correct_dirstate(self):