23
23
from bzrlib import (
29
28
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}))
31
from bzrlib.tests import test_dirstate
34
class _CompiledDirstateHelpersFeature(tests.Feature):
37
import bzrlib._dirstate_helpers_c
42
def feature_name(self):
43
return 'bzrlib._dirstate_helpers_c'
45
CompiledDirstateHelpersFeature = _CompiledDirstateHelpersFeature()
64
48
class TestBisectPathMixin(object):
219
203
class TestBisectPathLeft(tests.TestCase, TestBisectPathMixin):
220
"""Run all Bisect Path tests against _bisect_path_left."""
204
"""Run all Bisect Path tests against _bisect_path_left_py."""
222
206
def get_bisect_path(self):
223
from bzrlib._dirstate_helpers_py import _bisect_path_left
224
return _bisect_path_left
207
from bzrlib._dirstate_helpers_py import _bisect_path_left_py
208
return _bisect_path_left_py
226
210
def get_bisect(self):
227
211
return bisect.bisect_left, 0
230
214
class TestCompiledBisectPathLeft(TestBisectPathLeft):
231
"""Run all Bisect Path tests against _bisect_path_lect"""
215
"""Run all Bisect Path tests against _bisect_path_right_c"""
233
_test_needs_features = [compiled_dirstate_helpers_feature]
217
_test_needs_features = [CompiledDirstateHelpersFeature]
235
219
def get_bisect_path(self):
236
from bzrlib._dirstate_helpers_pyx import _bisect_path_left
237
return _bisect_path_left
220
from bzrlib._dirstate_helpers_c import _bisect_path_left_c
221
return _bisect_path_left_c
240
224
class TestBisectPathRight(tests.TestCase, TestBisectPathMixin):
241
"""Run all Bisect Path tests against _bisect_path_right"""
225
"""Run all Bisect Path tests against _bisect_path_right_py"""
243
227
def get_bisect_path(self):
244
from bzrlib._dirstate_helpers_py import _bisect_path_right
245
return _bisect_path_right
228
from bzrlib._dirstate_helpers_py import _bisect_path_right_py
229
return _bisect_path_right_py
247
231
def get_bisect(self):
248
232
return bisect.bisect_right, -1
251
235
class TestCompiledBisectPathRight(TestBisectPathRight):
252
"""Run all Bisect Path tests against _bisect_path_right"""
236
"""Run all Bisect Path tests against _bisect_path_right_c"""
254
_test_needs_features = [compiled_dirstate_helpers_feature]
238
_test_needs_features = [CompiledDirstateHelpersFeature]
256
240
def get_bisect_path(self):
257
from bzrlib._dirstate_helpers_pyx import _bisect_path_right
258
return _bisect_path_right
241
from bzrlib._dirstate_helpers_c import _bisect_path_right_c
242
return _bisect_path_right_c
261
245
class TestBisectDirblock(tests.TestCase):
636
620
class TestCompiledCmpPathByDirblock(TestCmpPathByDirblock):
637
621
"""Test the pyrex implementation of _cmp_path_by_dirblock"""
639
_test_needs_features = [compiled_dirstate_helpers_feature]
623
_test_needs_features = [CompiledDirstateHelpersFeature]
641
625
def get_cmp_by_dirs(self):
642
from bzrlib._dirstate_helpers_pyx import _cmp_path_by_dirblock
643
return _cmp_path_by_dirblock
626
from bzrlib._dirstate_helpers_c import _cmp_path_by_dirblock_c
627
return _cmp_path_by_dirblock_c
646
630
class TestMemRChr(tests.TestCase):
647
631
"""Test memrchr functionality"""
649
_test_needs_features = [compiled_dirstate_helpers_feature]
633
_test_needs_features = [CompiledDirstateHelpersFeature]
651
635
def assertMemRChr(self, expected, s, c):
652
from bzrlib._dirstate_helpers_pyx import _py_memrchr
636
from bzrlib._dirstate_helpers_c import _py_memrchr
653
637
self.assertEqual(expected, _py_memrchr(s, c))
655
639
def test_missing(self):
736
714
class TestCompiledReadDirblocks(TestReadDirblocks):
737
715
"""Test the pyrex implementation of _read_dirblocks"""
739
_test_needs_features = [compiled_dirstate_helpers_feature]
717
_test_needs_features = [CompiledDirstateHelpersFeature]
741
719
def get_read_dirblocks(self):
742
from bzrlib._dirstate_helpers_pyx import _read_dirblocks
743
return _read_dirblocks
720
from bzrlib._dirstate_helpers_c import _read_dirblocks_c
721
return _read_dirblocks_c
746
724
class TestUsingCompiledIfAvailable(tests.TestCase):
747
725
"""Check that any compiled functions that are available are the default.
749
727
It is possible to have typos, etc in the import line, such that
750
_dirstate_helpers_pyx is actually available, but the compiled functions are
728
_dirstate_helpers_c is actually available, but the compiled functions are
754
732
def test_bisect_dirblock(self):
755
if compiled_dirstate_helpers_feature.available():
756
from bzrlib._dirstate_helpers_pyx import bisect_dirblock
733
if CompiledDirstateHelpersFeature.available():
734
from bzrlib._dirstate_helpers_c import bisect_dirblock_c
735
self.assertIs(bisect_dirblock_c, dirstate.bisect_dirblock)
758
from bzrlib._dirstate_helpers_py import bisect_dirblock
759
self.assertIs(bisect_dirblock, dirstate.bisect_dirblock)
737
from bzrlib._dirstate_helpers_py import bisect_dirblock_py
738
self.assertIs(bisect_dirblock_py, dirstate.bisect_dirblock)
761
740
def test__bisect_path_left(self):
762
if compiled_dirstate_helpers_feature.available():
763
from bzrlib._dirstate_helpers_pyx import _bisect_path_left
741
if CompiledDirstateHelpersFeature.available():
742
from bzrlib._dirstate_helpers_c import _bisect_path_left_c
743
self.assertIs(_bisect_path_left_c, dirstate._bisect_path_left)
765
from bzrlib._dirstate_helpers_py import _bisect_path_left
766
self.assertIs(_bisect_path_left, dirstate._bisect_path_left)
745
from bzrlib._dirstate_helpers_py import _bisect_path_left_py
746
self.assertIs(_bisect_path_left_py, dirstate._bisect_path_left)
768
748
def test__bisect_path_right(self):
769
if compiled_dirstate_helpers_feature.available():
770
from bzrlib._dirstate_helpers_pyx import _bisect_path_right
749
if CompiledDirstateHelpersFeature.available():
750
from bzrlib._dirstate_helpers_c import _bisect_path_right_c
751
self.assertIs(_bisect_path_right_c, dirstate._bisect_path_right)
772
from bzrlib._dirstate_helpers_py import _bisect_path_right
773
self.assertIs(_bisect_path_right, dirstate._bisect_path_right)
753
from bzrlib._dirstate_helpers_py import _bisect_path_right_py
754
self.assertIs(_bisect_path_right_py, dirstate._bisect_path_right)
775
756
def test_cmp_by_dirs(self):
776
if compiled_dirstate_helpers_feature.available():
777
from bzrlib._dirstate_helpers_pyx import cmp_by_dirs
757
if CompiledDirstateHelpersFeature.available():
758
from bzrlib._dirstate_helpers_c import cmp_by_dirs_c
759
self.assertIs(cmp_by_dirs_c, dirstate.cmp_by_dirs)
779
from bzrlib._dirstate_helpers_py import cmp_by_dirs
780
self.assertIs(cmp_by_dirs, dirstate.cmp_by_dirs)
761
from bzrlib._dirstate_helpers_py import cmp_by_dirs_py
762
self.assertIs(cmp_by_dirs_py, dirstate.cmp_by_dirs)
782
764
def test__read_dirblocks(self):
783
if compiled_dirstate_helpers_feature.available():
784
from bzrlib._dirstate_helpers_pyx import _read_dirblocks
765
if CompiledDirstateHelpersFeature.available():
766
from bzrlib._dirstate_helpers_c import _read_dirblocks_c
767
self.assertIs(_read_dirblocks_c, dirstate._read_dirblocks)
786
from bzrlib._dirstate_helpers_py import _read_dirblocks
787
self.assertIs(_read_dirblocks, dirstate._read_dirblocks)
769
from bzrlib._dirstate_helpers_py import _read_dirblocks_py
770
self.assertIs(_read_dirblocks_py, dirstate._read_dirblocks)
789
772
def test_update_entry(self):
790
if compiled_dirstate_helpers_feature.available():
791
from bzrlib._dirstate_helpers_pyx import update_entry
773
if CompiledDirstateHelpersFeature.available():
774
from bzrlib._dirstate_helpers_c import update_entry
775
self.assertIs(update_entry, dirstate.update_entry)
793
from bzrlib.dirstate import update_entry
794
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)
796
780
def test_process_entry(self):
797
if compiled_dirstate_helpers_feature.available():
798
from bzrlib._dirstate_helpers_pyx import ProcessEntryC
781
if CompiledDirstateHelpersFeature.available():
782
from bzrlib._dirstate_helpers_c import ProcessEntryC
799
783
self.assertIs(ProcessEntryC, dirstate._process_entry)
801
785
from bzrlib.dirstate import ProcessEntryPython
805
789
class TestUpdateEntry(test_dirstate.TestCaseWithDirState):
806
790
"""Test the DirState.update_entry functions"""
808
scenarios = multiply_scenarios(
809
dir_reader_scenarios(), ue_scenarios)
815
super(TestUpdateEntry, self).setUp()
816
self.overrideAttr(dirstate, 'update_entry', self.update_entry)
818
792
def get_state_with_a(self):
819
793
"""Create a DirState tracking a single object named 'a'"""
820
794
state = test_dirstate.InstrumentedDirState.initialize('dirstate')
821
795
self.addCleanup(state.unlock)
822
796
state.add('a', 'a-id', 'file', None, '')
823
797
entry = state._get_entry(0, path_utf8='a')
798
self.set_update_entry()
824
799
return state, entry
801
def set_update_entry(self):
802
self.update_entry = dirstate.py_update_entry
826
804
def test_observed_sha1_cachable(self):
827
805
state, entry = self.get_state_with_a()
829
806
atime = time.time() - 10
830
807
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)
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)
835
811
state._observed_sha1(entry, "foo", statvalue)
836
812
self.assertEqual('foo', entry[1][0][1])
837
813
packed_stat = dirstate.pack_stat(statvalue)
838
814
self.assertEqual(packed_stat, entry[1][0][4])
839
self.assertEqual(dirstate.DirState.IN_MEMORY_HASH_MODIFIED,
840
state._dirblock_state)
842
816
def test_observed_sha1_not_cachable(self):
843
817
state, entry = self.get_state_with_a()
845
818
oldval = entry[1][0][1]
846
819
oldstat = entry[1][0][4]
847
820
self.build_tree(['a'])
848
821
statvalue = os.lstat('a')
849
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
850
state._dirblock_state)
851
822
state._observed_sha1(entry, "foo", statvalue)
852
823
self.assertEqual(oldval, entry[1][0][1])
853
824
self.assertEqual(oldstat, entry[1][0][4])
854
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
855
state._dirblock_state)
857
826
def test_update_entry(self):
858
827
state, _ = self.get_state_with_a()
971
937
# Dirblock is not updated (the link is too new)
972
938
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,
940
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
977
941
state._dirblock_state)
979
943
# Because the stat_value looks new, we should re-read the target
981
944
link_or_sha1 = self.update_entry(state, entry, abspath='a',
982
945
stat_value=stat_value)
983
946
self.assertEqual('target', link_or_sha1)
984
self.assertEqual([('read_link', 'a', '')], state._log)
947
self.assertEqual([('read_link', 'a', ''),
948
('read_link', 'a', ''),
985
950
self.assertEqual([('l', '', 6, False, dirstate.DirState.NULLSTAT)],
988
952
state.adjust_time(+20) # Skip into the future, all files look old
990
953
link_or_sha1 = self.update_entry(state, entry, abspath='a',
991
954
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
955
self.assertEqual('target', link_or_sha1)
998
956
# We need to re-read the link because only now can we cache it
999
self.assertEqual([('read_link', 'a', '')], state._log)
957
self.assertEqual([('read_link', 'a', ''),
958
('read_link', 'a', ''),
959
('read_link', 'a', ''),
1000
961
self.assertEqual([('l', 'target', 6, False, packed_stat)],
1004
964
# Another call won't re-read the link
1005
self.assertEqual([], state._log)
965
self.assertEqual([('read_link', 'a', ''),
966
('read_link', 'a', ''),
967
('read_link', 'a', ''),
1006
969
link_or_sha1 = self.update_entry(state, entry, abspath='a',
1007
970
stat_value=stat_value)
1008
971
self.assertEqual('target', link_or_sha1)
1023
986
self.build_tree(['a/'])
1024
987
state.adjust_time(+20)
1025
988
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
989
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
1028
990
state._dirblock_state)
1030
992
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1031
993
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])
994
self.assertIs(None, self.do_update_entry(state, entry, 'a'))
1049
995
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1050
996
state._dirblock_state)
1219
1154
self.assertEqual([('f', '', 14, True, dirstate.DirState.NULLSTAT)],
1222
# Make the disk object look old enough to cache (but it won't cache the
1223
# sha as it is a new file).
1157
# Make the disk object look old enough to cache (but it won't cache the sha
1158
# as it is a new file).
1224
1159
state.adjust_time(+20)
1225
1160
digest = 'b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6'
1226
1161
self.update_entry(state, entry, abspath='a', stat_value=stat_value)
1227
1162
self.assertEqual([('f', '', 14, True, dirstate.DirState.NULLSTAT)],
1230
def _prepare_tree(self):
1232
text = 'Hello World\n'
1233
tree = self.make_branch_and_tree('tree')
1234
self.build_tree_contents([('tree/a file', text)])
1235
tree.add('a file', 'a-file-id')
1236
# Note: dirstate does not sha prior to the first commit
1237
# so commit now in order for the test to work
1238
tree.commit('first')
1241
def test_sha1provider_sha1_used(self):
1242
tree, text = self._prepare_tree()
1243
state = dirstate.DirState.from_tree(tree, 'dirstate',
1244
UppercaseSHA1Provider())
1245
self.addCleanup(state.unlock)
1246
expected_sha = osutils.sha_string(text.upper() + "foo")
1247
entry = state._get_entry(0, path_utf8='a file')
1248
state._sha_cutoff_time()
1249
state._cutoff_time += 10
1250
sha1 = self.update_entry(state, entry, 'tree/a file',
1251
os.lstat('tree/a file'))
1252
self.assertEqual(expected_sha, sha1)
1254
def test_sha1provider_stat_and_sha1_used(self):
1255
tree, text = self._prepare_tree()
1257
self.addCleanup(tree.unlock)
1258
state = tree._current_dirstate()
1259
state._sha1_provider = UppercaseSHA1Provider()
1260
# If we used the standard provider, it would look like nothing has
1262
file_ids_changed = [change[0] for change
1263
in tree.iter_changes(tree.basis_tree())]
1264
self.assertEqual(['a-file-id'], file_ids_changed)
1267
class UppercaseSHA1Provider(dirstate.SHA1Provider):
1268
"""A custom SHA1Provider."""
1270
def sha1(self, abspath):
1271
return self.stat_and_sha1(abspath)[1]
1273
def stat_and_sha1(self, abspath):
1274
file_obj = file(abspath, 'rb')
1276
statvalue = os.fstat(file_obj.fileno())
1277
text = ''.join(file_obj.readlines())
1278
sha1 = osutils.sha_string(text.upper() + "foo")
1281
return statvalue, sha1
1284
class TestProcessEntry(test_dirstate.TestCaseWithDirState):
1286
scenarios = multiply_scenarios(dir_reader_scenarios(), pe_scenarios)
1289
_process_entry = None
1292
super(TestProcessEntry, self).setUp()
1293
self.overrideAttr(dirstate, '_process_entry', self._process_entry)
1295
def assertChangedFileIds(self, expected, tree):
1298
file_ids = [info[0] for info
1299
in tree.iter_changes(tree.basis_tree())]
1302
self.assertEqual(sorted(expected), sorted(file_ids))
1304
def test_exceptions_raised(self):
1305
# This is a direct test of bug #495023, it relies on osutils.is_inside
1306
# getting called in an inner function. Which makes it a bit brittle,
1307
# but at least it does reproduce the bug.
1308
tree = self.make_branch_and_tree('tree')
1309
self.build_tree(['tree/file', 'tree/dir/', 'tree/dir/sub',
1310
'tree/dir2/', 'tree/dir2/sub2'])
1311
tree.add(['file', 'dir', 'dir/sub', 'dir2', 'dir2/sub2'])
1312
tree.commit('first commit')
1314
self.addCleanup(tree.unlock)
1315
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)
1319
self.assertListRaises(RuntimeError, tree.iter_changes, basis_tree)
1321
def test_simple_changes(self):
1322
tree = self.make_branch_and_tree('tree')
1323
self.build_tree(['tree/file'])
1324
tree.add(['file'], ['file-id'])
1325
self.assertChangedFileIds([tree.get_root_id(), 'file-id'], tree)
1327
self.assertChangedFileIds([], tree)
1329
def test_sha1provider_stat_and_sha1_used(self):
1330
tree = self.make_branch_and_tree('tree')
1331
self.build_tree(['tree/file'])
1332
tree.add(['file'], ['file-id'])
1335
self.addCleanup(tree.unlock)
1336
state = tree._current_dirstate()
1337
state._sha1_provider = UppercaseSHA1Provider()
1338
self.assertChangedFileIds(['file-id'], tree)
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