841
907
state._validate()
843
909
state.set_parent_trees([('parent-revid', rt)], ghosts=[])
844
state.set_path_id('', 'foobarbaz')
910
root_entry = (('', '', 'TREE_ROOT'),
911
[('d', '', 0, False, 'x'*32),
912
('d', '', 0, False, 'parent-revid')])
913
self.assertEqual(root_entry, state._get_entry(0, path_utf8=''))
914
self.assertEqual(root_entry,
915
state._get_entry(0, fileid_utf8='TREE_ROOT'))
916
self.assertEqual((None, None),
917
state._get_entry(0, fileid_utf8='Asecond-root-id'))
918
state.set_path_id('', 'Asecond-root-id')
845
919
state._validate()
846
920
# now see that it is what we expected
848
(('', '', 'TREE_ROOT'),
849
[('a', '', 0, False, ''),
850
('d', '', 0, False, 'parent-revid'),
852
(('', '', 'foobarbaz'),
853
[('d', '', 0, False, ''),
854
('a', '', 0, False, ''),
921
old_root_entry = (('', '', 'TREE_ROOT'),
922
[('a', '', 0, False, ''),
923
('d', '', 0, False, 'parent-revid')])
924
new_root_entry = (('', '', 'Asecond-root-id'),
925
[('d', '', 0, False, ''),
926
('a', '', 0, False, '')])
927
expected_rows = [new_root_entry, old_root_entry]
857
928
state._validate()
858
929
self.assertEqual(expected_rows, list(state._iter_entries()))
930
self.assertEqual(new_root_entry, state._get_entry(0, path_utf8=''))
931
self.assertEqual(old_root_entry, state._get_entry(1, path_utf8=''))
932
self.assertEqual((None, None),
933
state._get_entry(0, fileid_utf8='TREE_ROOT'))
934
self.assertEqual(old_root_entry,
935
state._get_entry(1, fileid_utf8='TREE_ROOT'))
936
self.assertEqual(new_root_entry,
937
state._get_entry(0, fileid_utf8='Asecond-root-id'))
938
self.assertEqual((None, None),
939
state._get_entry(1, fileid_utf8='Asecond-root-id'))
859
940
# should work across save too
1623
1735
self.st_ino = ino
1624
1736
self.st_mode = mode
1627
class TestUpdateEntry(TestCaseWithDirState):
1628
"""Test the DirState.update_entry functions"""
1630
def get_state_with_a(self):
1631
"""Create a DirState tracking a single object named 'a'"""
1632
state = InstrumentedDirState.initialize('dirstate')
1633
self.addCleanup(state.unlock)
1634
state.add('a', 'a-id', 'file', None, '')
1635
entry = state._get_entry(0, path_utf8='a')
1638
def test_update_entry(self):
1639
state, entry = self.get_state_with_a()
1640
self.build_tree(['a'])
1641
# Add one where we don't provide the stat or sha already
1642
self.assertEqual(('', 'a', 'a-id'), entry[0])
1643
self.assertEqual([('f', '', 0, False, dirstate.DirState.NULLSTAT)],
1645
# Flush the buffers to disk
1647
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1648
state._dirblock_state)
1650
stat_value = os.lstat('a')
1651
packed_stat = dirstate.pack_stat(stat_value)
1652
link_or_sha1 = state.update_entry(entry, abspath='a',
1653
stat_value=stat_value)
1654
self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
1657
# The dirblock entry should not cache the file's sha1
1658
self.assertEqual([('f', '', 14, False, dirstate.DirState.NULLSTAT)],
1660
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
1661
state._dirblock_state)
1662
mode = stat_value.st_mode
1663
self.assertEqual([('sha1', 'a'), ('is_exec', mode, False)], state._log)
1666
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1667
state._dirblock_state)
1669
# If we do it again right away, we don't know if the file has changed
1670
# so we will re-read the file. Roll the clock back so the file is
1671
# guaranteed to look too new.
1672
state.adjust_time(-10)
1674
link_or_sha1 = state.update_entry(entry, abspath='a',
1675
stat_value=stat_value)
1676
self.assertEqual([('sha1', 'a'), ('is_exec', mode, False),
1677
('sha1', 'a'), ('is_exec', mode, False),
1679
self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
1681
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
1682
state._dirblock_state)
1683
self.assertEqual([('f', '', 14, False, dirstate.DirState.NULLSTAT)],
1687
# However, if we move the clock forward so the file is considered
1688
# "stable", it should just cache the value.
1689
state.adjust_time(+20)
1690
link_or_sha1 = state.update_entry(entry, abspath='a',
1691
stat_value=stat_value)
1692
self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
1694
self.assertEqual([('sha1', 'a'), ('is_exec', mode, False),
1695
('sha1', 'a'), ('is_exec', mode, False),
1696
('sha1', 'a'), ('is_exec', mode, False),
1698
self.assertEqual([('f', link_or_sha1, 14, False, packed_stat)],
1701
# Subsequent calls will just return the cached value
1702
link_or_sha1 = state.update_entry(entry, abspath='a',
1703
stat_value=stat_value)
1704
self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
1706
self.assertEqual([('sha1', 'a'), ('is_exec', mode, False),
1707
('sha1', 'a'), ('is_exec', mode, False),
1708
('sha1', 'a'), ('is_exec', mode, False),
1710
self.assertEqual([('f', link_or_sha1, 14, False, packed_stat)],
1713
def test_update_entry_symlink(self):
1714
"""Update entry should read symlinks."""
1715
self.requireFeature(SymlinkFeature)
1716
state, entry = self.get_state_with_a()
1718
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1719
state._dirblock_state)
1720
os.symlink('target', 'a')
1722
state.adjust_time(-10) # Make the symlink look new
1723
stat_value = os.lstat('a')
1724
packed_stat = dirstate.pack_stat(stat_value)
1725
link_or_sha1 = state.update_entry(entry, abspath='a',
1726
stat_value=stat_value)
1727
self.assertEqual('target', link_or_sha1)
1728
self.assertEqual([('read_link', 'a', '')], state._log)
1729
# Dirblock is not updated (the link is too new)
1730
self.assertEqual([('l', '', 6, False, dirstate.DirState.NULLSTAT)],
1732
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
1733
state._dirblock_state)
1735
# Because the stat_value looks new, we should re-read the target
1736
link_or_sha1 = state.update_entry(entry, abspath='a',
1737
stat_value=stat_value)
1738
self.assertEqual('target', link_or_sha1)
1739
self.assertEqual([('read_link', 'a', ''),
1740
('read_link', 'a', ''),
1742
self.assertEqual([('l', '', 6, False, dirstate.DirState.NULLSTAT)],
1744
state.adjust_time(+20) # Skip into the future, all files look old
1745
link_or_sha1 = state.update_entry(entry, abspath='a',
1746
stat_value=stat_value)
1747
self.assertEqual('target', link_or_sha1)
1748
# We need to re-read the link because only now can we cache it
1749
self.assertEqual([('read_link', 'a', ''),
1750
('read_link', 'a', ''),
1751
('read_link', 'a', ''),
1753
self.assertEqual([('l', 'target', 6, False, packed_stat)],
1756
# Another call won't re-read the link
1757
self.assertEqual([('read_link', 'a', ''),
1758
('read_link', 'a', ''),
1759
('read_link', 'a', ''),
1761
link_or_sha1 = state.update_entry(entry, abspath='a',
1762
stat_value=stat_value)
1763
self.assertEqual('target', link_or_sha1)
1764
self.assertEqual([('l', 'target', 6, False, packed_stat)],
1767
def do_update_entry(self, state, entry, abspath):
1768
stat_value = os.lstat(abspath)
1769
return state.update_entry(entry, abspath, stat_value)
1771
def test_update_entry_dir(self):
1772
state, entry = self.get_state_with_a()
1773
self.build_tree(['a/'])
1774
self.assertIs(None, self.do_update_entry(state, entry, 'a'))
1776
def test_update_entry_dir_unchanged(self):
1777
state, entry = self.get_state_with_a()
1778
self.build_tree(['a/'])
1779
state.adjust_time(+20)
1780
self.assertIs(None, self.do_update_entry(state, entry, 'a'))
1781
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
1782
state._dirblock_state)
1784
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1785
state._dirblock_state)
1786
self.assertIs(None, self.do_update_entry(state, entry, 'a'))
1787
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1788
state._dirblock_state)
1790
def test_update_entry_file_unchanged(self):
1791
state, entry = self.get_state_with_a()
1792
self.build_tree(['a'])
1793
sha1sum = 'b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6'
1794
state.adjust_time(+20)
1795
self.assertEqual(sha1sum, self.do_update_entry(state, entry, 'a'))
1796
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
1797
state._dirblock_state)
1799
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1800
state._dirblock_state)
1801
self.assertEqual(sha1sum, self.do_update_entry(state, entry, 'a'))
1802
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1803
state._dirblock_state)
1805
def create_and_test_file(self, state, entry):
1806
"""Create a file at 'a' and verify the state finds it.
1808
The state should already be versioning *something* at 'a'. This makes
1809
sure that state.update_entry recognizes it as a file.
1811
self.build_tree(['a'])
1812
stat_value = os.lstat('a')
1813
packed_stat = dirstate.pack_stat(stat_value)
1815
link_or_sha1 = self.do_update_entry(state, entry, abspath='a')
1816
self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
1818
self.assertEqual([('f', link_or_sha1, 14, False, packed_stat)],
1822
def create_and_test_dir(self, state, entry):
1823
"""Create a directory at 'a' and verify the state finds it.
1825
The state should already be versioning *something* at 'a'. This makes
1826
sure that state.update_entry recognizes it as a directory.
1828
self.build_tree(['a/'])
1829
stat_value = os.lstat('a')
1830
packed_stat = dirstate.pack_stat(stat_value)
1832
link_or_sha1 = self.do_update_entry(state, entry, abspath='a')
1833
self.assertIs(None, link_or_sha1)
1834
self.assertEqual([('d', '', 0, False, packed_stat)], entry[1])
1838
def create_and_test_symlink(self, state, entry):
1839
"""Create a symlink at 'a' and verify the state finds it.
1841
The state should already be versioning *something* at 'a'. This makes
1842
sure that state.update_entry recognizes it as a symlink.
1844
This should not be called if this platform does not have symlink
1847
# caller should care about skipping test on platforms without symlinks
1848
os.symlink('path/to/foo', 'a')
1850
stat_value = os.lstat('a')
1851
packed_stat = dirstate.pack_stat(stat_value)
1853
link_or_sha1 = self.do_update_entry(state, entry, abspath='a')
1854
self.assertEqual('path/to/foo', link_or_sha1)
1855
self.assertEqual([('l', 'path/to/foo', 11, False, packed_stat)],
1859
def test_update_file_to_dir(self):
1860
"""If a file changes to a directory we return None for the sha.
1861
We also update the inventory record.
1863
state, entry = self.get_state_with_a()
1864
# The file sha1 won't be cached unless the file is old
1865
state.adjust_time(+10)
1866
self.create_and_test_file(state, entry)
1868
self.create_and_test_dir(state, entry)
1870
def test_update_file_to_symlink(self):
1871
"""File becomes a symlink"""
1872
self.requireFeature(SymlinkFeature)
1873
state, entry = self.get_state_with_a()
1874
# The file sha1 won't be cached unless the file is old
1875
state.adjust_time(+10)
1876
self.create_and_test_file(state, entry)
1878
self.create_and_test_symlink(state, entry)
1880
def test_update_dir_to_file(self):
1881
"""Directory becoming a file updates the entry."""
1882
state, entry = self.get_state_with_a()
1883
# The file sha1 won't be cached unless the file is old
1884
state.adjust_time(+10)
1885
self.create_and_test_dir(state, entry)
1887
self.create_and_test_file(state, entry)
1889
def test_update_dir_to_symlink(self):
1890
"""Directory becomes a symlink"""
1891
self.requireFeature(SymlinkFeature)
1892
state, entry = self.get_state_with_a()
1893
# The symlink target won't be cached if it isn't old
1894
state.adjust_time(+10)
1895
self.create_and_test_dir(state, entry)
1897
self.create_and_test_symlink(state, entry)
1899
def test_update_symlink_to_file(self):
1900
"""Symlink becomes a file"""
1901
self.requireFeature(SymlinkFeature)
1902
state, entry = self.get_state_with_a()
1903
# The symlink and file info won't be cached unless old
1904
state.adjust_time(+10)
1905
self.create_and_test_symlink(state, entry)
1907
self.create_and_test_file(state, entry)
1909
def test_update_symlink_to_dir(self):
1910
"""Symlink becomes a directory"""
1911
self.requireFeature(SymlinkFeature)
1912
state, entry = self.get_state_with_a()
1913
# The symlink target won't be cached if it isn't old
1914
state.adjust_time(+10)
1915
self.create_and_test_symlink(state, entry)
1917
self.create_and_test_dir(state, entry)
1919
def test__is_executable_win32(self):
1920
state, entry = self.get_state_with_a()
1921
self.build_tree(['a'])
1923
# Make sure we are using the win32 implementation of _is_executable
1924
state._is_executable = state._is_executable_win32
1926
# The file on disk is not executable, but we are marking it as though
1927
# it is. With _is_executable_win32 we ignore what is on disk.
1928
entry[1][0] = ('f', '', 0, True, dirstate.DirState.NULLSTAT)
1930
stat_value = os.lstat('a')
1931
packed_stat = dirstate.pack_stat(stat_value)
1933
state.adjust_time(-10) # Make sure everything is new
1934
state.update_entry(entry, abspath='a', stat_value=stat_value)
1936
# The row is updated, but the executable bit stays set.
1937
self.assertEqual([('f', '', 14, True, dirstate.DirState.NULLSTAT)],
1940
# Make the disk object look old enough to cache
1941
state.adjust_time(+20)
1942
digest = 'b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6'
1943
state.update_entry(entry, abspath='a', stat_value=stat_value)
1944
self.assertEqual([('f', digest, 14, True, packed_stat)], entry[1])
1947
class TestPackStat(TestCaseWithTransport):
1740
return _FakeStat(st.st_size, st.st_mtime, st.st_ctime, st.st_dev,
1741
st.st_ino, st.st_mode)
1744
class TestPackStat(tests.TestCaseWithTransport):
1949
1746
def assertPackStat(self, expected, stat_value):
1950
1747
"""Check the packed and serialized form of a stat value."""