29
29
from bzrlib.tests import (
32
from bzrlib.tests.test_osutils import dir_reader_scenarios
33
from bzrlib.tests.scenarios import (
34
load_tests_apply_scenarios,
37
from bzrlib.tests import (
42
load_tests = load_tests_apply_scenarios
45
compiled_dirstate_helpers_feature = features.ModuleAvailableFeature(
46
'bzrlib._dirstate_helpers_pyx')
49
# FIXME: we should also parametrize against SHA1Provider !
51
ue_scenarios = [('dirstate_Python',
52
{'update_entry': dirstate.py_update_entry})]
53
if compiled_dirstate_helpers_feature.available():
54
update_entry = compiled_dirstate_helpers_feature.module.update_entry
55
ue_scenarios.append(('dirstate_Pyrex', {'update_entry': update_entry}))
57
pe_scenarios = [('dirstate_Python',
58
{'_process_entry': dirstate.ProcessEntryPython})]
59
if compiled_dirstate_helpers_feature.available():
60
process_entry = compiled_dirstate_helpers_feature.module.ProcessEntryC
61
pe_scenarios.append(('dirstate_Pyrex', {'_process_entry': process_entry}))
35
from bzrlib import _dirstate_helpers_pyx
36
has_dirstate_helpers_pyx = True
38
has_dirstate_helpers_pyx = False
41
compiled_dirstate_helpers_feature = tests.ModuleAvailableFeature(
42
'bzrlib._dirstate_helpers_pyx')
45
def load_tests(basic_tests, module, loader):
46
# FIXME: we should also parametrize against SHA1Provider !
47
suite = loader.suiteClass()
48
remaining_tests = basic_tests
50
dir_reader_scenarios = test_osutils.dir_reader_scenarios()
52
ue_scenarios = [('dirstate_Python',
53
{'update_entry': dirstate.py_update_entry})]
54
if compiled_dirstate_helpers_feature.available():
55
update_entry = compiled_dirstate_helpers_feature.module.update_entry
56
pyrex_scenario = ('dirstate_Pyrex', {'update_entry': update_entry})
57
ue_scenarios.append(pyrex_scenario)
58
process_entry_tests, remaining_tests = tests.split_suite_by_condition(
59
remaining_tests, tests.condition_isinstance(TestUpdateEntry))
60
tests.multiply_tests(process_entry_tests,
61
tests.multiply_scenarios(dir_reader_scenarios,
65
pe_scenarios = [('dirstate_Python',
66
{'_process_entry': dirstate.ProcessEntryPython})]
67
if compiled_dirstate_helpers_feature.available():
68
process_entry = compiled_dirstate_helpers_feature.module.ProcessEntryC
69
pyrex_scenario = ('dirstate_Pyrex', {'_process_entry': process_entry})
70
pe_scenarios.append(pyrex_scenario)
71
process_entry_tests, remaining_tests = tests.split_suite_by_condition(
72
remaining_tests, tests.condition_isinstance(TestProcessEntry))
73
tests.multiply_tests(process_entry_tests,
74
tests.multiply_scenarios(dir_reader_scenarios,
78
dir_reader_tests, remaining_tests = tests.split_suite_by_condition(
79
remaining_tests, tests.condition_isinstance(
80
test_dirstate.TestCaseWithDirState))
81
tests.multiply_tests(dir_reader_tests, dir_reader_scenarios, suite)
82
suite.addTest(remaining_tests)
64
87
class TestBisectPathMixin(object):
805
826
class TestUpdateEntry(test_dirstate.TestCaseWithDirState):
806
827
"""Test the DirState.update_entry functions"""
808
scenarios = multiply_scenarios(
809
dir_reader_scenarios(), ue_scenarios)
811
829
# Set by load_tests
812
830
update_entry = None
815
833
super(TestUpdateEntry, self).setUp()
816
self.overrideAttr(dirstate, 'update_entry', self.update_entry)
834
orig = dirstate.update_entry
836
dirstate.update_entry = orig
837
self.addCleanup(cleanup)
838
dirstate.update_entry = self.update_entry
818
840
def get_state_with_a(self):
819
841
"""Create a DirState tracking a single object named 'a'"""
826
848
def test_observed_sha1_cachable(self):
827
849
state, entry = self.get_state_with_a()
829
850
atime = time.time() - 10
830
851
self.build_tree(['a'])
831
statvalue = test_dirstate._FakeStat.from_stat(os.lstat('a'))
832
statvalue.st_mtime = statvalue.st_ctime = atime
833
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
834
state._dirblock_state)
852
statvalue = os.lstat('a')
853
statvalue = test_dirstate._FakeStat(statvalue.st_size, atime, atime,
854
statvalue.st_dev, statvalue.st_ino, statvalue.st_mode)
835
855
state._observed_sha1(entry, "foo", statvalue)
836
856
self.assertEqual('foo', entry[1][0][1])
837
857
packed_stat = dirstate.pack_stat(statvalue)
838
858
self.assertEqual(packed_stat, entry[1][0][4])
839
self.assertEqual(dirstate.DirState.IN_MEMORY_HASH_MODIFIED,
840
state._dirblock_state)
842
860
def test_observed_sha1_not_cachable(self):
843
861
state, entry = self.get_state_with_a()
845
862
oldval = entry[1][0][1]
846
863
oldstat = entry[1][0][4]
847
864
self.build_tree(['a'])
848
865
statvalue = os.lstat('a')
849
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
850
state._dirblock_state)
851
866
state._observed_sha1(entry, "foo", statvalue)
852
867
self.assertEqual(oldval, entry[1][0][1])
853
868
self.assertEqual(oldstat, entry[1][0][4])
854
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
855
state._dirblock_state)
857
870
def test_update_entry(self):
858
871
state, _ = self.get_state_with_a()
883
896
stat_value=stat_value)
884
897
self.assertEqual(None, link_or_sha1)
886
# The dirblock entry should not have computed or cached the file's
887
# sha1, but it did update the files' st_size. However, this is not
888
# worth writing a dirstate file for, so we leave the state UNMODIFIED
899
# The dirblock entry should not have cached the file's sha1 (too new)
889
900
self.assertEqual(('f', '', 14, False, dirstate.DirState.NULLSTAT),
891
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
902
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
892
903
state._dirblock_state)
893
904
mode = stat_value.st_mode
894
905
self.assertEqual([('is_exec', mode, False)], state._log)
971
981
# Dirblock is not updated (the link is too new)
972
982
self.assertEqual([('l', '', 6, False, dirstate.DirState.NULLSTAT)],
974
# The file entry turned into a symlink, that is considered
975
# HASH modified worthy.
976
self.assertEqual(dirstate.DirState.IN_MEMORY_HASH_MODIFIED,
984
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
977
985
state._dirblock_state)
979
987
# Because the stat_value looks new, we should re-read the target
981
988
link_or_sha1 = self.update_entry(state, entry, abspath='a',
982
989
stat_value=stat_value)
983
990
self.assertEqual('target', link_or_sha1)
984
self.assertEqual([('read_link', 'a', '')], state._log)
991
self.assertEqual([('read_link', 'a', ''),
992
('read_link', 'a', ''),
985
994
self.assertEqual([('l', '', 6, False, dirstate.DirState.NULLSTAT)],
988
996
state.adjust_time(+20) # Skip into the future, all files look old
990
997
link_or_sha1 = self.update_entry(state, entry, abspath='a',
991
998
stat_value=stat_value)
992
# The symlink stayed a symlink. So while it is new enough to cache, we
993
# don't bother setting the flag, because it is not really worth saving
994
# (when we stat the symlink, we'll have paged in the target.)
995
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
996
state._dirblock_state)
997
999
self.assertEqual('target', link_or_sha1)
998
1000
# We need to re-read the link because only now can we cache it
999
self.assertEqual([('read_link', 'a', '')], state._log)
1001
self.assertEqual([('read_link', 'a', ''),
1002
('read_link', 'a', ''),
1003
('read_link', 'a', ''),
1000
1005
self.assertEqual([('l', 'target', 6, False, packed_stat)],
1004
1008
# Another call won't re-read the link
1005
self.assertEqual([], state._log)
1009
self.assertEqual([('read_link', 'a', ''),
1010
('read_link', 'a', ''),
1011
('read_link', 'a', ''),
1006
1013
link_or_sha1 = self.update_entry(state, entry, abspath='a',
1007
1014
stat_value=stat_value)
1008
1015
self.assertEqual('target', link_or_sha1)
1023
1030
self.build_tree(['a/'])
1024
1031
state.adjust_time(+20)
1025
1032
self.assertIs(None, self.do_update_entry(state, entry, 'a'))
1026
# a/ used to be a file, but is now a directory, worth saving
1027
1033
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
1028
1034
state._dirblock_state)
1030
1036
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1031
1037
state._dirblock_state)
1032
# No changes to a/ means not worth saving.
1033
self.assertIs(None, self.do_update_entry(state, entry, 'a'))
1034
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1035
state._dirblock_state)
1036
# Change the last-modified time for the directory
1037
t = time.time() - 100.0
1039
os.utime('a', (t, t))
1041
# It looks like Win32 + FAT doesn't allow to change times on a dir.
1042
raise tests.TestSkipped("can't update mtime of a dir on FAT")
1043
saved_packed_stat = entry[1][0][-1]
1044
self.assertIs(None, self.do_update_entry(state, entry, 'a'))
1045
# We *do* go ahead and update the information in the dirblocks, but we
1046
# don't bother setting IN_MEMORY_MODIFIED because it is trivial to
1048
self.assertNotEqual(saved_packed_stat, entry[1][0][-1])
1038
self.assertIs(None, self.do_update_entry(state, entry, 'a'))
1049
1039
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1050
1040
state._dirblock_state)
1284
1274
class TestProcessEntry(test_dirstate.TestCaseWithDirState):
1286
scenarios = multiply_scenarios(dir_reader_scenarios(), pe_scenarios)
1288
1276
# Set by load_tests
1289
1277
_process_entry = None
1291
1279
def setUp(self):
1292
1280
super(TestProcessEntry, self).setUp()
1293
self.overrideAttr(dirstate, '_process_entry', self._process_entry)
1281
orig = dirstate._process_entry
1283
dirstate._process_entry = orig
1284
self.addCleanup(cleanup)
1285
dirstate._process_entry = self._process_entry
1295
1287
def assertChangedFileIds(self, expected, tree):
1296
1288
tree.lock_read()
1305
1297
# This is a direct test of bug #495023, it relies on osutils.is_inside
1306
1298
# getting called in an inner function. Which makes it a bit brittle,
1307
1299
# but at least it does reproduce the bug.
1300
def is_inside_raises(*args, **kwargs):
1301
raise RuntimeError('stop this')
1308
1302
tree = self.make_branch_and_tree('tree')
1309
1303
self.build_tree(['tree/file', 'tree/dir/', 'tree/dir/sub',
1310
1304
'tree/dir2/', 'tree/dir2/sub2'])
1313
1307
tree.lock_read()
1314
1308
self.addCleanup(tree.unlock)
1315
1309
basis_tree = tree.basis_tree()
1316
def is_inside_raises(*args, **kwargs):
1317
raise RuntimeError('stop this')
1318
self.overrideAttr(osutils, 'is_inside', is_inside_raises)
1310
orig = osutils.is_inside
1311
self.addCleanup(setattr, osutils, 'is_inside', orig)
1312
osutils.is_inside = is_inside_raises
1319
1313
self.assertListRaises(RuntimeError, tree.iter_changes, basis_tree)
1321
1315
def test_simple_changes(self):