769
750
from bzrlib._dirstate_helpers_py import _read_dirblocks_py
770
751
self.assertIs(_read_dirblocks_py, dirstate._read_dirblocks)
772
def test_update_entry(self):
773
if CompiledDirstateHelpersFeature.available():
774
from bzrlib._dirstate_helpers_c import update_entry
775
self.assertIs(update_entry, dirstate.update_entry)
777
from bzrlib.dirstate import py_update_entry
778
self.assertIs(py_update_entry, dirstate.py_update_entry)
780
def test_process_entry(self):
781
if CompiledDirstateHelpersFeature.available():
782
from bzrlib._dirstate_helpers_c import ProcessEntryC
783
self.assertIs(ProcessEntryC, dirstate._process_entry)
785
from bzrlib.dirstate import ProcessEntryPython
786
self.assertIs(ProcessEntryPython, dirstate._process_entry)
789
class TestUpdateEntry(test_dirstate.TestCaseWithDirState):
790
"""Test the DirState.update_entry functions"""
792
def get_state_with_a(self):
793
"""Create a DirState tracking a single object named 'a'"""
794
state = test_dirstate.InstrumentedDirState.initialize('dirstate')
795
self.addCleanup(state.unlock)
796
state.add('a', 'a-id', 'file', None, '')
797
entry = state._get_entry(0, path_utf8='a')
798
self.set_update_entry()
801
def set_update_entry(self):
802
self.update_entry = dirstate.py_update_entry
804
def test_observed_sha1_cachable(self):
805
state, entry = self.get_state_with_a()
806
atime = time.time() - 10
807
self.build_tree(['a'])
808
statvalue = os.lstat('a')
809
statvalue = test_dirstate._FakeStat(statvalue.st_size, atime, atime,
810
statvalue.st_dev, statvalue.st_ino, statvalue.st_mode)
811
state._observed_sha1(entry, "foo", statvalue)
812
self.assertEqual('foo', entry[1][0][1])
813
packed_stat = dirstate.pack_stat(statvalue)
814
self.assertEqual(packed_stat, entry[1][0][4])
816
def test_observed_sha1_not_cachable(self):
817
state, entry = self.get_state_with_a()
818
oldval = entry[1][0][1]
819
oldstat = entry[1][0][4]
820
self.build_tree(['a'])
821
statvalue = os.lstat('a')
822
state._observed_sha1(entry, "foo", statvalue)
823
self.assertEqual(oldval, entry[1][0][1])
824
self.assertEqual(oldstat, entry[1][0][4])
826
def test_update_entry(self):
827
state, _ = self.get_state_with_a()
828
tree = self.make_branch_and_tree('tree')
830
empty_revid = tree.commit('empty')
831
self.build_tree(['tree/a'])
832
tree.add(['a'], ['a-id'])
833
with_a_id = tree.commit('with_a')
834
self.addCleanup(tree.unlock)
835
state.set_parent_trees(
836
[(empty_revid, tree.branch.repository.revision_tree(empty_revid))],
838
entry = state._get_entry(0, path_utf8='a')
839
self.build_tree(['a'])
840
# Add one where we don't provide the stat or sha already
841
self.assertEqual(('', 'a', 'a-id'), entry[0])
842
self.assertEqual(('f', '', 0, False, dirstate.DirState.NULLSTAT),
844
# Flush the buffers to disk
846
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
847
state._dirblock_state)
849
stat_value = os.lstat('a')
850
packed_stat = dirstate.pack_stat(stat_value)
851
link_or_sha1 = self.update_entry(state, entry, abspath='a',
852
stat_value=stat_value)
853
self.assertEqual(None, link_or_sha1)
855
# The dirblock entry should not have cached the file's sha1 (too new)
856
self.assertEqual(('f', '', 14, False, dirstate.DirState.NULLSTAT),
858
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
859
state._dirblock_state)
860
mode = stat_value.st_mode
861
self.assertEqual([('is_exec', mode, False)], state._log)
864
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
865
state._dirblock_state)
867
# If we do it again right away, we don't know if the file has changed
868
# so we will re-read the file. Roll the clock back so the file is
869
# guaranteed to look too new.
870
state.adjust_time(-10)
873
link_or_sha1 = self.update_entry(state, entry, abspath='a',
874
stat_value=stat_value)
875
self.assertEqual([('is_exec', mode, False)], state._log)
876
self.assertEqual(None, link_or_sha1)
877
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
878
state._dirblock_state)
879
self.assertEqual(('f', '', 14, False, dirstate.DirState.NULLSTAT),
883
# If it is cachable (the clock has moved forward) but new it still
884
# won't calculate the sha or cache it.
885
state.adjust_time(+20)
887
link_or_sha1 = dirstate.update_entry(state, entry, abspath='a',
888
stat_value=stat_value)
889
self.assertEqual(None, link_or_sha1)
890
self.assertEqual([('is_exec', mode, False)], state._log)
891
self.assertEqual(('f', '', 14, False, dirstate.DirState.NULLSTAT),
894
# If the file is no longer new, and the clock has been moved forward
895
# sufficiently, it will cache the sha.
897
state.set_parent_trees(
898
[(with_a_id, tree.branch.repository.revision_tree(with_a_id))],
900
entry = state._get_entry(0, path_utf8='a')
902
link_or_sha1 = self.update_entry(state, entry, abspath='a',
903
stat_value=stat_value)
904
self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
906
self.assertEqual([('is_exec', mode, False), ('sha1', 'a')],
908
self.assertEqual(('f', link_or_sha1, 14, False, packed_stat),
911
# Subsequent calls will just return the cached value
913
link_or_sha1 = self.update_entry(state, entry, abspath='a',
914
stat_value=stat_value)
915
self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
917
self.assertEqual([], state._log)
918
self.assertEqual(('f', link_or_sha1, 14, False, packed_stat),
921
def test_update_entry_symlink(self):
922
"""Update entry should read symlinks."""
923
self.requireFeature(SymlinkFeature)
924
state, entry = self.get_state_with_a()
926
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
927
state._dirblock_state)
928
os.symlink('target', 'a')
930
state.adjust_time(-10) # Make the symlink look new
931
stat_value = os.lstat('a')
932
packed_stat = dirstate.pack_stat(stat_value)
933
link_or_sha1 = self.update_entry(state, entry, abspath='a',
934
stat_value=stat_value)
935
self.assertEqual('target', link_or_sha1)
936
self.assertEqual([('read_link', 'a', '')], state._log)
937
# Dirblock is not updated (the link is too new)
938
self.assertEqual([('l', '', 6, False, dirstate.DirState.NULLSTAT)],
940
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
941
state._dirblock_state)
943
# Because the stat_value looks new, we should re-read the target
944
link_or_sha1 = self.update_entry(state, entry, abspath='a',
945
stat_value=stat_value)
946
self.assertEqual('target', link_or_sha1)
947
self.assertEqual([('read_link', 'a', ''),
948
('read_link', 'a', ''),
950
self.assertEqual([('l', '', 6, False, dirstate.DirState.NULLSTAT)],
952
state.adjust_time(+20) # Skip into the future, all files look old
953
link_or_sha1 = self.update_entry(state, entry, abspath='a',
954
stat_value=stat_value)
955
self.assertEqual('target', link_or_sha1)
956
# We need to re-read the link because only now can we cache it
957
self.assertEqual([('read_link', 'a', ''),
958
('read_link', 'a', ''),
959
('read_link', 'a', ''),
961
self.assertEqual([('l', 'target', 6, False, packed_stat)],
964
# Another call won't re-read the link
965
self.assertEqual([('read_link', 'a', ''),
966
('read_link', 'a', ''),
967
('read_link', 'a', ''),
969
link_or_sha1 = self.update_entry(state, entry, abspath='a',
970
stat_value=stat_value)
971
self.assertEqual('target', link_or_sha1)
972
self.assertEqual([('l', 'target', 6, False, packed_stat)],
975
def do_update_entry(self, state, entry, abspath):
976
stat_value = os.lstat(abspath)
977
return self.update_entry(state, entry, abspath, stat_value)
979
def test_update_entry_dir(self):
980
state, entry = self.get_state_with_a()
981
self.build_tree(['a/'])
982
self.assertIs(None, self.do_update_entry(state, entry, 'a'))
984
def test_update_entry_dir_unchanged(self):
985
state, entry = self.get_state_with_a()
986
self.build_tree(['a/'])
987
state.adjust_time(+20)
988
self.assertIs(None, self.do_update_entry(state, entry, 'a'))
989
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
990
state._dirblock_state)
992
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
993
state._dirblock_state)
994
self.assertIs(None, self.do_update_entry(state, entry, 'a'))
995
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
996
state._dirblock_state)
998
def test_update_entry_file_unchanged(self):
999
state, _ = self.get_state_with_a()
1000
tree = self.make_branch_and_tree('tree')
1002
self.build_tree(['tree/a'])
1003
tree.add(['a'], ['a-id'])
1004
with_a_id = tree.commit('witha')
1005
self.addCleanup(tree.unlock)
1006
state.set_parent_trees(
1007
[(with_a_id, tree.branch.repository.revision_tree(with_a_id))],
1009
entry = state._get_entry(0, path_utf8='a')
1010
self.build_tree(['a'])
1011
sha1sum = 'b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6'
1012
state.adjust_time(+20)
1013
self.assertEqual(sha1sum, self.do_update_entry(state, entry, 'a'))
1014
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
1015
state._dirblock_state)
1017
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1018
state._dirblock_state)
1019
self.assertEqual(sha1sum, self.do_update_entry(state, entry, 'a'))
1020
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1021
state._dirblock_state)
1023
def create_and_test_file(self, state, entry):
1024
"""Create a file at 'a' and verify the state finds it during update.
1026
The state should already be versioning *something* at 'a'. This makes
1027
sure that state.update_entry recognizes it as a file.
1029
self.build_tree(['a'])
1030
stat_value = os.lstat('a')
1031
packed_stat = dirstate.pack_stat(stat_value)
1033
link_or_sha1 = self.do_update_entry(state, entry, abspath='a')
1034
self.assertEqual(None, link_or_sha1)
1035
self.assertEqual([('f', '', 14, False, dirstate.DirState.NULLSTAT)],
1039
def create_and_test_dir(self, state, entry):
1040
"""Create a directory at 'a' and verify the state finds it.
1042
The state should already be versioning *something* at 'a'. This makes
1043
sure that state.update_entry recognizes it as a directory.
1045
self.build_tree(['a/'])
1046
stat_value = os.lstat('a')
1047
packed_stat = dirstate.pack_stat(stat_value)
1049
link_or_sha1 = self.do_update_entry(state, entry, abspath='a')
1050
self.assertIs(None, link_or_sha1)
1051
self.assertEqual([('d', '', 0, False, packed_stat)], entry[1])
1055
def create_and_test_symlink(self, state, entry):
1056
"""Create a symlink at 'a' and verify the state finds it.
1058
The state should already be versioning *something* at 'a'. This makes
1059
sure that state.update_entry recognizes it as a symlink.
1061
This should not be called if this platform does not have symlink
1064
# caller should care about skipping test on platforms without symlinks
1065
os.symlink('path/to/foo', 'a')
1067
stat_value = os.lstat('a')
1068
packed_stat = dirstate.pack_stat(stat_value)
1070
link_or_sha1 = self.do_update_entry(state, entry, abspath='a')
1071
self.assertEqual('path/to/foo', link_or_sha1)
1072
self.assertEqual([('l', 'path/to/foo', 11, False, packed_stat)],
1076
def test_update_file_to_dir(self):
1077
"""If a file changes to a directory we return None for the sha.
1078
We also update the inventory record.
1080
state, entry = self.get_state_with_a()
1081
# The file sha1 won't be cached unless the file is old
1082
state.adjust_time(+10)
1083
self.create_and_test_file(state, entry)
1085
self.create_and_test_dir(state, entry)
1087
def test_update_file_to_symlink(self):
1088
"""File becomes a symlink"""
1089
self.requireFeature(SymlinkFeature)
1090
state, entry = self.get_state_with_a()
1091
# The file sha1 won't be cached unless the file is old
1092
state.adjust_time(+10)
1093
self.create_and_test_file(state, entry)
1095
self.create_and_test_symlink(state, entry)
1097
def test_update_dir_to_file(self):
1098
"""Directory becoming a file updates the entry."""
1099
state, entry = self.get_state_with_a()
1100
# The file sha1 won't be cached unless the file is old
1101
state.adjust_time(+10)
1102
self.create_and_test_dir(state, entry)
1104
self.create_and_test_file(state, entry)
1106
def test_update_dir_to_symlink(self):
1107
"""Directory becomes a symlink"""
1108
self.requireFeature(SymlinkFeature)
1109
state, entry = self.get_state_with_a()
1110
# The symlink target won't be cached if it isn't old
1111
state.adjust_time(+10)
1112
self.create_and_test_dir(state, entry)
1114
self.create_and_test_symlink(state, entry)
1116
def test_update_symlink_to_file(self):
1117
"""Symlink becomes a file"""
1118
self.requireFeature(SymlinkFeature)
1119
state, entry = self.get_state_with_a()
1120
# The symlink and file info won't be cached unless old
1121
state.adjust_time(+10)
1122
self.create_and_test_symlink(state, entry)
1124
self.create_and_test_file(state, entry)
1126
def test_update_symlink_to_dir(self):
1127
"""Symlink becomes a directory"""
1128
self.requireFeature(SymlinkFeature)
1129
state, entry = self.get_state_with_a()
1130
# The symlink target won't be cached if it isn't old
1131
state.adjust_time(+10)
1132
self.create_and_test_symlink(state, entry)
1134
self.create_and_test_dir(state, entry)
1136
def test__is_executable_win32(self):
1137
state, entry = self.get_state_with_a()
1138
self.build_tree(['a'])
1140
# Make sure we are using the win32 implementation of _is_executable
1141
state._is_executable = state._is_executable_win32
1143
# The file on disk is not executable, but we are marking it as though
1144
# it is. With _is_executable_win32 we ignore what is on disk.
1145
entry[1][0] = ('f', '', 0, True, dirstate.DirState.NULLSTAT)
1147
stat_value = os.lstat('a')
1148
packed_stat = dirstate.pack_stat(stat_value)
1150
state.adjust_time(-10) # Make sure everything is new
1151
self.update_entry(state, entry, abspath='a', stat_value=stat_value)
1153
# The row is updated, but the executable bit stays set.
1154
self.assertEqual([('f', '', 14, True, dirstate.DirState.NULLSTAT)],
1157
# Make the disk object look old enough to cache (but it won't cache the sha
1158
# as it is a new file).
1159
state.adjust_time(+20)
1160
digest = 'b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6'
1161
self.update_entry(state, entry, abspath='a', stat_value=stat_value)
1162
self.assertEqual([('f', '', 14, True, dirstate.DirState.NULLSTAT)],
1166
class TestCompiledUpdateEntry(TestUpdateEntry):
1167
"""Test the pyrex implementation of _read_dirblocks"""
1169
_test_needs_features = [CompiledDirstateHelpersFeature]
1171
def set_update_entry(self):
1172
from bzrlib._dirstate_helpers_c import update_entry
1173
self.update_entry = update_entry