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,
39
load_tests = load_tests_apply_scenarios
42
compiled_dirstate_helpers_feature = tests.ModuleAvailableFeature(
43
'bzrlib._dirstate_helpers_pyx')
46
# FIXME: we should also parametrize against SHA1Provider !
48
ue_scenarios = [('dirstate_Python',
49
{'update_entry': dirstate.py_update_entry})]
50
if compiled_dirstate_helpers_feature.available():
51
update_entry = compiled_dirstate_helpers_feature.module.update_entry
52
ue_scenarios.append(('dirstate_Pyrex', {'update_entry': update_entry}))
54
pe_scenarios = [('dirstate_Python',
55
{'_process_entry': dirstate.ProcessEntryPython})]
56
if compiled_dirstate_helpers_feature.available():
57
process_entry = compiled_dirstate_helpers_feature.module.ProcessEntryC
58
pe_scenarios.append(('dirstate_Pyrex', {'_process_entry': process_entry}))
32
from bzrlib.tests import test_dirstate
35
class _CompiledDirstateHelpersFeature(tests.Feature):
38
import bzrlib._dirstate_helpers_c
43
def feature_name(self):
44
return 'bzrlib._dirstate_helpers_c'
46
CompiledDirstateHelpersFeature = _CompiledDirstateHelpersFeature()
61
49
class TestBisectPathMixin(object):
216
204
class TestBisectPathLeft(tests.TestCase, TestBisectPathMixin):
217
"""Run all Bisect Path tests against _bisect_path_left."""
205
"""Run all Bisect Path tests against _bisect_path_left_py."""
219
207
def get_bisect_path(self):
220
from bzrlib._dirstate_helpers_py import _bisect_path_left
221
return _bisect_path_left
208
from bzrlib._dirstate_helpers_py import _bisect_path_left_py
209
return _bisect_path_left_py
223
211
def get_bisect(self):
224
212
return bisect.bisect_left, 0
227
215
class TestCompiledBisectPathLeft(TestBisectPathLeft):
228
"""Run all Bisect Path tests against _bisect_path_lect"""
216
"""Run all Bisect Path tests against _bisect_path_right_c"""
230
_test_needs_features = [compiled_dirstate_helpers_feature]
218
_test_needs_features = [CompiledDirstateHelpersFeature]
232
220
def get_bisect_path(self):
233
from bzrlib._dirstate_helpers_pyx import _bisect_path_left
234
return _bisect_path_left
221
from bzrlib._dirstate_helpers_c import _bisect_path_left_c
222
return _bisect_path_left_c
237
225
class TestBisectPathRight(tests.TestCase, TestBisectPathMixin):
238
"""Run all Bisect Path tests against _bisect_path_right"""
226
"""Run all Bisect Path tests against _bisect_path_right_py"""
240
228
def get_bisect_path(self):
241
from bzrlib._dirstate_helpers_py import _bisect_path_right
242
return _bisect_path_right
229
from bzrlib._dirstate_helpers_py import _bisect_path_right_py
230
return _bisect_path_right_py
244
232
def get_bisect(self):
245
233
return bisect.bisect_right, -1
248
236
class TestCompiledBisectPathRight(TestBisectPathRight):
249
"""Run all Bisect Path tests against _bisect_path_right"""
237
"""Run all Bisect Path tests against _bisect_path_right_c"""
251
_test_needs_features = [compiled_dirstate_helpers_feature]
239
_test_needs_features = [CompiledDirstateHelpersFeature]
253
241
def get_bisect_path(self):
254
from bzrlib._dirstate_helpers_pyx import _bisect_path_right
255
return _bisect_path_right
242
from bzrlib._dirstate_helpers_c import _bisect_path_right_c
243
return _bisect_path_right_c
258
246
class TestBisectDirblock(tests.TestCase):
633
621
class TestCompiledCmpPathByDirblock(TestCmpPathByDirblock):
634
622
"""Test the pyrex implementation of _cmp_path_by_dirblock"""
636
_test_needs_features = [compiled_dirstate_helpers_feature]
624
_test_needs_features = [CompiledDirstateHelpersFeature]
638
626
def get_cmp_by_dirs(self):
639
from bzrlib._dirstate_helpers_pyx import _cmp_path_by_dirblock
640
return _cmp_path_by_dirblock
627
from bzrlib._dirstate_helpers_c import _cmp_path_by_dirblock_c
628
return _cmp_path_by_dirblock_c
643
631
class TestMemRChr(tests.TestCase):
644
632
"""Test memrchr functionality"""
646
_test_needs_features = [compiled_dirstate_helpers_feature]
634
_test_needs_features = [CompiledDirstateHelpersFeature]
648
636
def assertMemRChr(self, expected, s, c):
649
from bzrlib._dirstate_helpers_pyx import _py_memrchr
637
from bzrlib._dirstate_helpers_c import _py_memrchr
650
638
self.assertEqual(expected, _py_memrchr(s, c))
652
640
def test_missing(self):
733
715
class TestCompiledReadDirblocks(TestReadDirblocks):
734
716
"""Test the pyrex implementation of _read_dirblocks"""
736
_test_needs_features = [compiled_dirstate_helpers_feature]
718
_test_needs_features = [CompiledDirstateHelpersFeature]
738
720
def get_read_dirblocks(self):
739
from bzrlib._dirstate_helpers_pyx import _read_dirblocks
740
return _read_dirblocks
721
from bzrlib._dirstate_helpers_c import _read_dirblocks_c
722
return _read_dirblocks_c
743
725
class TestUsingCompiledIfAvailable(tests.TestCase):
744
726
"""Check that any compiled functions that are available are the default.
746
728
It is possible to have typos, etc in the import line, such that
747
_dirstate_helpers_pyx is actually available, but the compiled functions are
729
_dirstate_helpers_c is actually available, but the compiled functions are
751
733
def test_bisect_dirblock(self):
752
if compiled_dirstate_helpers_feature.available():
753
from bzrlib._dirstate_helpers_pyx import bisect_dirblock
734
if CompiledDirstateHelpersFeature.available():
735
from bzrlib._dirstate_helpers_c import bisect_dirblock_c
736
self.assertIs(bisect_dirblock_c, dirstate.bisect_dirblock)
755
from bzrlib._dirstate_helpers_py import bisect_dirblock
756
self.assertIs(bisect_dirblock, dirstate.bisect_dirblock)
738
from bzrlib._dirstate_helpers_py import bisect_dirblock_py
739
self.assertIs(bisect_dirblock_py, dirstate.bisect_dirblock)
758
741
def test__bisect_path_left(self):
759
if compiled_dirstate_helpers_feature.available():
760
from bzrlib._dirstate_helpers_pyx import _bisect_path_left
742
if CompiledDirstateHelpersFeature.available():
743
from bzrlib._dirstate_helpers_c import _bisect_path_left_c
744
self.assertIs(_bisect_path_left_c, dirstate._bisect_path_left)
762
from bzrlib._dirstate_helpers_py import _bisect_path_left
763
self.assertIs(_bisect_path_left, dirstate._bisect_path_left)
746
from bzrlib._dirstate_helpers_py import _bisect_path_left_py
747
self.assertIs(_bisect_path_left_py, dirstate._bisect_path_left)
765
749
def test__bisect_path_right(self):
766
if compiled_dirstate_helpers_feature.available():
767
from bzrlib._dirstate_helpers_pyx import _bisect_path_right
750
if CompiledDirstateHelpersFeature.available():
751
from bzrlib._dirstate_helpers_c import _bisect_path_right_c
752
self.assertIs(_bisect_path_right_c, dirstate._bisect_path_right)
769
from bzrlib._dirstate_helpers_py import _bisect_path_right
770
self.assertIs(_bisect_path_right, dirstate._bisect_path_right)
754
from bzrlib._dirstate_helpers_py import _bisect_path_right_py
755
self.assertIs(_bisect_path_right_py, dirstate._bisect_path_right)
772
757
def test_cmp_by_dirs(self):
773
if compiled_dirstate_helpers_feature.available():
774
from bzrlib._dirstate_helpers_pyx import cmp_by_dirs
758
if CompiledDirstateHelpersFeature.available():
759
from bzrlib._dirstate_helpers_c import cmp_by_dirs_c
760
self.assertIs(cmp_by_dirs_c, dirstate.cmp_by_dirs)
776
from bzrlib._dirstate_helpers_py import cmp_by_dirs
777
self.assertIs(cmp_by_dirs, dirstate.cmp_by_dirs)
762
from bzrlib._dirstate_helpers_py import cmp_by_dirs_py
763
self.assertIs(cmp_by_dirs_py, dirstate.cmp_by_dirs)
779
765
def test__read_dirblocks(self):
780
if compiled_dirstate_helpers_feature.available():
781
from bzrlib._dirstate_helpers_pyx import _read_dirblocks
766
if CompiledDirstateHelpersFeature.available():
767
from bzrlib._dirstate_helpers_c import _read_dirblocks_c
768
self.assertIs(_read_dirblocks_c, dirstate._read_dirblocks)
783
from bzrlib._dirstate_helpers_py import _read_dirblocks
784
self.assertIs(_read_dirblocks, dirstate._read_dirblocks)
770
from bzrlib._dirstate_helpers_py import _read_dirblocks_py
771
self.assertIs(_read_dirblocks_py, dirstate._read_dirblocks)
786
773
def test_update_entry(self):
787
if compiled_dirstate_helpers_feature.available():
788
from bzrlib._dirstate_helpers_pyx import update_entry
774
if CompiledDirstateHelpersFeature.available():
775
from bzrlib._dirstate_helpers_c import update_entry
776
self.assertIs(update_entry, dirstate.update_entry)
790
from bzrlib.dirstate import update_entry
791
self.assertIs(update_entry, dirstate.update_entry)
778
from bzrlib.dirstate import py_update_entry
779
self.assertIs(py_update_entry, dirstate.py_update_entry)
793
781
def test_process_entry(self):
794
if compiled_dirstate_helpers_feature.available():
795
from bzrlib._dirstate_helpers_pyx import ProcessEntryC
782
if CompiledDirstateHelpersFeature.available():
783
from bzrlib._dirstate_helpers_c import ProcessEntryC
796
784
self.assertIs(ProcessEntryC, dirstate._process_entry)
798
786
from bzrlib.dirstate import ProcessEntryPython
802
790
class TestUpdateEntry(test_dirstate.TestCaseWithDirState):
803
791
"""Test the DirState.update_entry functions"""
805
scenarios = multiply_scenarios(
806
dir_reader_scenarios(), ue_scenarios)
812
super(TestUpdateEntry, self).setUp()
813
self.overrideAttr(dirstate, 'update_entry', self.update_entry)
815
793
def get_state_with_a(self):
816
794
"""Create a DirState tracking a single object named 'a'"""
817
795
state = test_dirstate.InstrumentedDirState.initialize('dirstate')
818
796
self.addCleanup(state.unlock)
819
797
state.add('a', 'a-id', 'file', None, '')
820
798
entry = state._get_entry(0, path_utf8='a')
799
self.set_update_entry()
821
800
return state, entry
802
def set_update_entry(self):
803
self.update_entry = dirstate.py_update_entry
823
805
def test_observed_sha1_cachable(self):
824
806
state, entry = self.get_state_with_a()
826
807
atime = time.time() - 10
827
808
self.build_tree(['a'])
828
statvalue = test_dirstate._FakeStat.from_stat(os.lstat('a'))
829
statvalue.st_mtime = statvalue.st_ctime = atime
830
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
831
state._dirblock_state)
809
statvalue = os.lstat('a')
810
statvalue = test_dirstate._FakeStat(statvalue.st_size, atime, atime,
811
statvalue.st_dev, statvalue.st_ino, statvalue.st_mode)
832
812
state._observed_sha1(entry, "foo", statvalue)
833
813
self.assertEqual('foo', entry[1][0][1])
834
814
packed_stat = dirstate.pack_stat(statvalue)
835
815
self.assertEqual(packed_stat, entry[1][0][4])
836
self.assertEqual(dirstate.DirState.IN_MEMORY_HASH_MODIFIED,
837
state._dirblock_state)
839
817
def test_observed_sha1_not_cachable(self):
840
818
state, entry = self.get_state_with_a()
842
819
oldval = entry[1][0][1]
843
820
oldstat = entry[1][0][4]
844
821
self.build_tree(['a'])
845
822
statvalue = os.lstat('a')
846
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
847
state._dirblock_state)
848
823
state._observed_sha1(entry, "foo", statvalue)
849
824
self.assertEqual(oldval, entry[1][0][1])
850
825
self.assertEqual(oldstat, entry[1][0][4])
851
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
852
state._dirblock_state)
854
827
def test_update_entry(self):
855
828
state, _ = self.get_state_with_a()
880
853
stat_value=stat_value)
881
854
self.assertEqual(None, link_or_sha1)
883
# The dirblock entry should not have computed or cached the file's
884
# sha1, but it did update the files' st_size. However, this is not
885
# worth writing a dirstate file for, so we leave the state UNMODIFIED
856
# The dirblock entry should not have cached the file's sha1 (too new)
886
857
self.assertEqual(('f', '', 14, False, dirstate.DirState.NULLSTAT),
888
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
859
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
889
860
state._dirblock_state)
890
861
mode = stat_value.st_mode
891
862
self.assertEqual([('is_exec', mode, False)], state._log)
968
938
# Dirblock is not updated (the link is too new)
969
939
self.assertEqual([('l', '', 6, False, dirstate.DirState.NULLSTAT)],
971
# The file entry turned into a symlink, that is considered
972
# HASH modified worthy.
973
self.assertEqual(dirstate.DirState.IN_MEMORY_HASH_MODIFIED,
941
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
974
942
state._dirblock_state)
976
944
# Because the stat_value looks new, we should re-read the target
978
945
link_or_sha1 = self.update_entry(state, entry, abspath='a',
979
946
stat_value=stat_value)
980
947
self.assertEqual('target', link_or_sha1)
981
self.assertEqual([('read_link', 'a', '')], state._log)
948
self.assertEqual([('read_link', 'a', ''),
949
('read_link', 'a', ''),
982
951
self.assertEqual([('l', '', 6, False, dirstate.DirState.NULLSTAT)],
985
953
state.adjust_time(+20) # Skip into the future, all files look old
987
954
link_or_sha1 = self.update_entry(state, entry, abspath='a',
988
955
stat_value=stat_value)
989
# The symlink stayed a symlink. So while it is new enough to cache, we
990
# don't bother setting the flag, because it is not really worth saving
991
# (when we stat the symlink, we'll have paged in the target.)
992
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
993
state._dirblock_state)
994
956
self.assertEqual('target', link_or_sha1)
995
957
# We need to re-read the link because only now can we cache it
996
self.assertEqual([('read_link', 'a', '')], state._log)
958
self.assertEqual([('read_link', 'a', ''),
959
('read_link', 'a', ''),
960
('read_link', 'a', ''),
997
962
self.assertEqual([('l', 'target', 6, False, packed_stat)],
1001
965
# Another call won't re-read the link
1002
self.assertEqual([], state._log)
966
self.assertEqual([('read_link', 'a', ''),
967
('read_link', 'a', ''),
968
('read_link', 'a', ''),
1003
970
link_or_sha1 = self.update_entry(state, entry, abspath='a',
1004
971
stat_value=stat_value)
1005
972
self.assertEqual('target', link_or_sha1)
1020
987
self.build_tree(['a/'])
1021
988
state.adjust_time(+20)
1022
989
self.assertIs(None, self.do_update_entry(state, entry, 'a'))
1023
# a/ used to be a file, but is now a directory, worth saving
1024
990
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
1025
991
state._dirblock_state)
1027
993
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1028
994
state._dirblock_state)
1029
# No changes to a/ means not worth saving.
1030
self.assertIs(None, self.do_update_entry(state, entry, 'a'))
1031
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1032
state._dirblock_state)
1033
# Change the last-modified time for the directory
1034
t = time.time() - 100.0
1036
os.utime('a', (t, t))
1038
# It looks like Win32 + FAT doesn't allow to change times on a dir.
1039
raise tests.TestSkipped("can't update mtime of a dir on FAT")
1040
saved_packed_stat = entry[1][0][-1]
1041
self.assertIs(None, self.do_update_entry(state, entry, 'a'))
1042
# We *do* go ahead and update the information in the dirblocks, but we
1043
# don't bother setting IN_MEMORY_MODIFIED because it is trivial to
1045
self.assertNotEqual(saved_packed_stat, entry[1][0][-1])
995
self.assertIs(None, self.do_update_entry(state, entry, 'a'))
1046
996
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1047
997
state._dirblock_state)
1278
1228
return statvalue, sha1
1281
class TestProcessEntry(test_dirstate.TestCaseWithDirState):
1283
scenarios = multiply_scenarios(dir_reader_scenarios(), pe_scenarios)
1286
_process_entry = None
1231
class TestCompiledUpdateEntry(TestUpdateEntry):
1232
"""Test the pyrex implementation of _read_dirblocks"""
1234
_test_needs_features = [CompiledDirstateHelpersFeature]
1236
def set_update_entry(self):
1237
from bzrlib._dirstate_helpers_c import update_entry
1238
self.update_entry = update_entry
1241
class TestProcessEntryPython(test_dirstate.TestCaseWithDirState):
1288
1243
def setUp(self):
1289
super(TestProcessEntry, self).setUp()
1290
self.overrideAttr(dirstate, '_process_entry', self._process_entry)
1244
super(TestProcessEntryPython, self).setUp()
1245
self.setup_process_entry()
1247
def setup_process_entry(self):
1248
from bzrlib import dirstate
1249
orig = dirstate._process_entry
1251
dirstate._process_entry = orig
1252
self.addCleanup(cleanup)
1253
dirstate._process_entry = dirstate.ProcessEntryPython
1292
1255
def assertChangedFileIds(self, expected, tree):
1293
1256
tree.lock_read()
1299
1262
self.assertEqual(sorted(expected), sorted(file_ids))
1301
def test_exceptions_raised(self):
1302
# This is a direct test of bug #495023, it relies on osutils.is_inside
1303
# getting called in an inner function. Which makes it a bit brittle,
1304
# but at least it does reproduce the bug.
1305
tree = self.make_branch_and_tree('tree')
1306
self.build_tree(['tree/file', 'tree/dir/', 'tree/dir/sub',
1307
'tree/dir2/', 'tree/dir2/sub2'])
1308
tree.add(['file', 'dir', 'dir/sub', 'dir2', 'dir2/sub2'])
1309
tree.commit('first commit')
1311
self.addCleanup(tree.unlock)
1312
basis_tree = tree.basis_tree()
1313
def is_inside_raises(*args, **kwargs):
1314
raise RuntimeError('stop this')
1315
self.overrideAttr(osutils, 'is_inside', is_inside_raises)
1316
self.assertListRaises(RuntimeError, tree.iter_changes, basis_tree)
1318
1264
def test_simple_changes(self):
1319
1265
tree = self.make_branch_and_tree('tree')
1320
1266
self.build_tree(['tree/file'])