29
from bzrlib.tests import (
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()
30
from bzrlib.tests import (
33
from bzrlib.tests.test_osutils import dir_reader_scenarios
34
from bzrlib.tests.scenarios import (
35
load_tests_apply_scenarios,
38
from bzrlib.tests import (
43
load_tests = load_tests_apply_scenarios
46
compiled_dirstate_helpers_feature = features.ModuleAvailableFeature(
47
'bzrlib._dirstate_helpers_pyx')
50
# FIXME: we should also parametrize against SHA1Provider !
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
ue_scenarios.append(('dirstate_Pyrex', {'update_entry': update_entry}))
58
pe_scenarios = [('dirstate_Python',
59
{'_process_entry': dirstate.ProcessEntryPython})]
60
if compiled_dirstate_helpers_feature.available():
61
process_entry = compiled_dirstate_helpers_feature.module.ProcessEntryC
62
pe_scenarios.append(('dirstate_Pyrex', {'_process_entry': process_entry}))
64
helper_scenarios = [('dirstate_Python', {'helpers': _dirstate_helpers_py})]
65
if compiled_dirstate_helpers_feature.available():
66
helper_scenarios.append(('dirstate_Pyrex',
67
{'helpers': compiled_dirstate_helpers_feature.module}))
49
70
class TestBisectPathMixin(object):
204
225
class TestBisectPathLeft(tests.TestCase, TestBisectPathMixin):
205
"""Run all Bisect Path tests against _bisect_path_left_py."""
226
"""Run all Bisect Path tests against _bisect_path_left."""
207
228
def get_bisect_path(self):
208
from bzrlib._dirstate_helpers_py import _bisect_path_left_py
209
return _bisect_path_left_py
229
from bzrlib._dirstate_helpers_py import _bisect_path_left
230
return _bisect_path_left
211
232
def get_bisect(self):
212
233
return bisect.bisect_left, 0
215
236
class TestCompiledBisectPathLeft(TestBisectPathLeft):
216
"""Run all Bisect Path tests against _bisect_path_right_c"""
237
"""Run all Bisect Path tests against _bisect_path_lect"""
218
_test_needs_features = [CompiledDirstateHelpersFeature]
239
_test_needs_features = [compiled_dirstate_helpers_feature]
220
241
def get_bisect_path(self):
221
from bzrlib._dirstate_helpers_c import _bisect_path_left_c
222
return _bisect_path_left_c
242
from bzrlib._dirstate_helpers_pyx import _bisect_path_left
243
return _bisect_path_left
225
246
class TestBisectPathRight(tests.TestCase, TestBisectPathMixin):
226
"""Run all Bisect Path tests against _bisect_path_right_py"""
247
"""Run all Bisect Path tests against _bisect_path_right"""
228
249
def get_bisect_path(self):
229
from bzrlib._dirstate_helpers_py import _bisect_path_right_py
230
return _bisect_path_right_py
250
from bzrlib._dirstate_helpers_py import _bisect_path_right
251
return _bisect_path_right
232
253
def get_bisect(self):
233
254
return bisect.bisect_right, -1
236
257
class TestCompiledBisectPathRight(TestBisectPathRight):
237
"""Run all Bisect Path tests against _bisect_path_right_c"""
258
"""Run all Bisect Path tests against _bisect_path_right"""
239
_test_needs_features = [CompiledDirstateHelpersFeature]
260
_test_needs_features = [compiled_dirstate_helpers_feature]
241
262
def get_bisect_path(self):
242
from bzrlib._dirstate_helpers_c import _bisect_path_right_c
243
return _bisect_path_right_c
263
from bzrlib._dirstate_helpers_pyx import _bisect_path_right
264
return _bisect_path_right
246
267
class TestBisectDirblock(tests.TestCase):
621
642
class TestCompiledCmpPathByDirblock(TestCmpPathByDirblock):
622
643
"""Test the pyrex implementation of _cmp_path_by_dirblock"""
624
_test_needs_features = [CompiledDirstateHelpersFeature]
645
_test_needs_features = [compiled_dirstate_helpers_feature]
626
647
def get_cmp_by_dirs(self):
627
from bzrlib._dirstate_helpers_c import _cmp_path_by_dirblock_c
628
return _cmp_path_by_dirblock_c
648
from bzrlib._dirstate_helpers_pyx import _cmp_path_by_dirblock
649
return _cmp_path_by_dirblock
631
652
class TestMemRChr(tests.TestCase):
632
653
"""Test memrchr functionality"""
634
_test_needs_features = [CompiledDirstateHelpersFeature]
655
_test_needs_features = [compiled_dirstate_helpers_feature]
636
657
def assertMemRChr(self, expected, s, c):
637
from bzrlib._dirstate_helpers_c import _py_memrchr
658
from bzrlib._dirstate_helpers_pyx import _py_memrchr
638
659
self.assertEqual(expected, _py_memrchr(s, c))
640
661
def test_missing(self):
715
742
class TestCompiledReadDirblocks(TestReadDirblocks):
716
743
"""Test the pyrex implementation of _read_dirblocks"""
718
_test_needs_features = [CompiledDirstateHelpersFeature]
745
_test_needs_features = [compiled_dirstate_helpers_feature]
720
747
def get_read_dirblocks(self):
721
from bzrlib._dirstate_helpers_c import _read_dirblocks_c
722
return _read_dirblocks_c
748
from bzrlib._dirstate_helpers_pyx import _read_dirblocks
749
return _read_dirblocks
725
752
class TestUsingCompiledIfAvailable(tests.TestCase):
726
753
"""Check that any compiled functions that are available are the default.
728
755
It is possible to have typos, etc in the import line, such that
729
_dirstate_helpers_c is actually available, but the compiled functions are
756
_dirstate_helpers_pyx is actually available, but the compiled functions are
733
760
def test_bisect_dirblock(self):
734
if CompiledDirstateHelpersFeature.available():
735
from bzrlib._dirstate_helpers_c import bisect_dirblock_c
736
self.assertIs(bisect_dirblock_c, dirstate.bisect_dirblock)
761
if compiled_dirstate_helpers_feature.available():
762
from bzrlib._dirstate_helpers_pyx import bisect_dirblock
738
from bzrlib._dirstate_helpers_py import bisect_dirblock_py
739
self.assertIs(bisect_dirblock_py, dirstate.bisect_dirblock)
764
from bzrlib._dirstate_helpers_py import bisect_dirblock
765
self.assertIs(bisect_dirblock, dirstate.bisect_dirblock)
741
767
def test__bisect_path_left(self):
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)
768
if compiled_dirstate_helpers_feature.available():
769
from bzrlib._dirstate_helpers_pyx import _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)
771
from bzrlib._dirstate_helpers_py import _bisect_path_left
772
self.assertIs(_bisect_path_left, dirstate._bisect_path_left)
749
774
def test__bisect_path_right(self):
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)
775
if compiled_dirstate_helpers_feature.available():
776
from bzrlib._dirstate_helpers_pyx import _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)
778
from bzrlib._dirstate_helpers_py import _bisect_path_right
779
self.assertIs(_bisect_path_right, dirstate._bisect_path_right)
757
781
def test_cmp_by_dirs(self):
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)
782
if compiled_dirstate_helpers_feature.available():
783
from bzrlib._dirstate_helpers_pyx import 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)
785
from bzrlib._dirstate_helpers_py import cmp_by_dirs
786
self.assertIs(cmp_by_dirs, dirstate.cmp_by_dirs)
765
788
def test__read_dirblocks(self):
766
if CompiledDirstateHelpersFeature.available():
767
from bzrlib._dirstate_helpers_c import _read_dirblocks_c
768
self.assertIs(_read_dirblocks_c, dirstate._read_dirblocks)
789
if compiled_dirstate_helpers_feature.available():
790
from bzrlib._dirstate_helpers_pyx import _read_dirblocks
770
from bzrlib._dirstate_helpers_py import _read_dirblocks_py
771
self.assertIs(_read_dirblocks_py, dirstate._read_dirblocks)
792
from bzrlib._dirstate_helpers_py import _read_dirblocks
793
self.assertIs(_read_dirblocks, dirstate._read_dirblocks)
773
795
def test_update_entry(self):
774
if CompiledDirstateHelpersFeature.available():
775
from bzrlib._dirstate_helpers_c import update_entry
776
self.assertIs(update_entry, dirstate.update_entry)
796
if compiled_dirstate_helpers_feature.available():
797
from bzrlib._dirstate_helpers_pyx import update_entry
778
from bzrlib.dirstate import py_update_entry
779
self.assertIs(py_update_entry, dirstate.py_update_entry)
799
from bzrlib.dirstate import update_entry
800
self.assertIs(update_entry, dirstate.update_entry)
781
802
def test_process_entry(self):
782
if CompiledDirstateHelpersFeature.available():
783
from bzrlib._dirstate_helpers_c import ProcessEntryC
803
if compiled_dirstate_helpers_feature.available():
804
from bzrlib._dirstate_helpers_pyx import ProcessEntryC
784
805
self.assertIs(ProcessEntryC, dirstate._process_entry)
786
807
from bzrlib.dirstate import ProcessEntryPython
790
811
class TestUpdateEntry(test_dirstate.TestCaseWithDirState):
791
812
"""Test the DirState.update_entry functions"""
814
scenarios = multiply_scenarios(
815
dir_reader_scenarios(), ue_scenarios)
821
super(TestUpdateEntry, self).setUp()
822
self.overrideAttr(dirstate, 'update_entry', self.update_entry)
793
824
def get_state_with_a(self):
794
825
"""Create a DirState tracking a single object named 'a'"""
795
826
state = test_dirstate.InstrumentedDirState.initialize('dirstate')
796
827
self.addCleanup(state.unlock)
797
828
state.add('a', 'a-id', 'file', None, '')
798
829
entry = state._get_entry(0, path_utf8='a')
799
self.set_update_entry()
800
830
return state, entry
802
def set_update_entry(self):
803
self.update_entry = dirstate.py_update_entry
805
832
def test_observed_sha1_cachable(self):
806
833
state, entry = self.get_state_with_a()
807
835
atime = time.time() - 10
808
836
self.build_tree(['a'])
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)
837
statvalue = test_dirstate._FakeStat.from_stat(os.lstat('a'))
838
statvalue.st_mtime = statvalue.st_ctime = atime
839
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
840
state._dirblock_state)
812
841
state._observed_sha1(entry, "foo", statvalue)
813
842
self.assertEqual('foo', entry[1][0][1])
814
843
packed_stat = dirstate.pack_stat(statvalue)
815
844
self.assertEqual(packed_stat, entry[1][0][4])
845
self.assertEqual(dirstate.DirState.IN_MEMORY_HASH_MODIFIED,
846
state._dirblock_state)
817
848
def test_observed_sha1_not_cachable(self):
818
849
state, entry = self.get_state_with_a()
819
851
oldval = entry[1][0][1]
820
852
oldstat = entry[1][0][4]
821
853
self.build_tree(['a'])
822
854
statvalue = os.lstat('a')
855
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
856
state._dirblock_state)
823
857
state._observed_sha1(entry, "foo", statvalue)
824
858
self.assertEqual(oldval, entry[1][0][1])
825
859
self.assertEqual(oldstat, entry[1][0][4])
860
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
861
state._dirblock_state)
827
863
def test_update_entry(self):
828
864
state, _ = self.get_state_with_a()
853
889
stat_value=stat_value)
854
890
self.assertEqual(None, link_or_sha1)
856
# The dirblock entry should not have cached the file's sha1 (too new)
892
# The dirblock entry should not have computed or cached the file's
893
# sha1, but it did update the files' st_size. However, this is not
894
# worth writing a dirstate file for, so we leave the state UNMODIFIED
857
895
self.assertEqual(('f', '', 14, False, dirstate.DirState.NULLSTAT),
859
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
897
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
860
898
state._dirblock_state)
861
899
mode = stat_value.st_mode
862
900
self.assertEqual([('is_exec', mode, False)], state._log)
938
977
# Dirblock is not updated (the link is too new)
939
978
self.assertEqual([('l', '', 6, False, dirstate.DirState.NULLSTAT)],
941
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
980
# The file entry turned into a symlink, that is considered
981
# HASH modified worthy.
982
self.assertEqual(dirstate.DirState.IN_MEMORY_HASH_MODIFIED,
942
983
state._dirblock_state)
944
985
# Because the stat_value looks new, we should re-read the target
945
987
link_or_sha1 = self.update_entry(state, entry, abspath='a',
946
988
stat_value=stat_value)
947
989
self.assertEqual('target', link_or_sha1)
948
self.assertEqual([('read_link', 'a', ''),
949
('read_link', 'a', ''),
990
self.assertEqual([('read_link', 'a', '')], state._log)
951
991
self.assertEqual([('l', '', 6, False, dirstate.DirState.NULLSTAT)],
953
994
state.adjust_time(+20) # Skip into the future, all files look old
954
996
link_or_sha1 = self.update_entry(state, entry, abspath='a',
955
997
stat_value=stat_value)
998
# The symlink stayed a symlink. So while it is new enough to cache, we
999
# don't bother setting the flag, because it is not really worth saving
1000
# (when we stat the symlink, we'll have paged in the target.)
1001
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1002
state._dirblock_state)
956
1003
self.assertEqual('target', link_or_sha1)
957
1004
# We need to re-read the link because only now can we cache it
958
self.assertEqual([('read_link', 'a', ''),
959
('read_link', 'a', ''),
960
('read_link', 'a', ''),
1005
self.assertEqual([('read_link', 'a', '')], state._log)
962
1006
self.assertEqual([('l', 'target', 6, False, packed_stat)],
965
1010
# Another call won't re-read the link
966
self.assertEqual([('read_link', 'a', ''),
967
('read_link', 'a', ''),
968
('read_link', 'a', ''),
1011
self.assertEqual([], state._log)
970
1012
link_or_sha1 = self.update_entry(state, entry, abspath='a',
971
1013
stat_value=stat_value)
972
1014
self.assertEqual('target', link_or_sha1)
987
1029
self.build_tree(['a/'])
988
1030
state.adjust_time(+20)
989
1031
self.assertIs(None, self.do_update_entry(state, entry, 'a'))
1032
# a/ used to be a file, but is now a directory, worth saving
990
1033
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
991
1034
state._dirblock_state)
993
1036
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
994
1037
state._dirblock_state)
995
self.assertIs(None, self.do_update_entry(state, entry, 'a'))
1038
# No changes to a/ means not worth saving.
1039
self.assertIs(None, self.do_update_entry(state, entry, 'a'))
1040
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1041
state._dirblock_state)
1042
# Change the last-modified time for the directory
1043
t = time.time() - 100.0
1045
os.utime('a', (t, t))
1047
# It looks like Win32 + FAT doesn't allow to change times on a dir.
1048
raise tests.TestSkipped("can't update mtime of a dir on FAT")
1049
saved_packed_stat = entry[1][0][-1]
1050
self.assertIs(None, self.do_update_entry(state, entry, 'a'))
1051
# We *do* go ahead and update the information in the dirblocks, but we
1052
# don't bother setting IN_MEMORY_MODIFIED because it is trivial to
1054
self.assertNotEqual(saved_packed_stat, entry[1][0][-1])
996
1055
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
997
1056
state._dirblock_state)
1228
1287
return statvalue, sha1
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):
1290
class TestProcessEntry(test_dirstate.TestCaseWithDirState):
1292
scenarios = multiply_scenarios(dir_reader_scenarios(), pe_scenarios)
1295
_process_entry = None
1243
1297
def setUp(self):
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
1298
super(TestProcessEntry, self).setUp()
1299
self.overrideAttr(dirstate, '_process_entry', self._process_entry)
1255
1301
def assertChangedFileIds(self, expected, tree):
1256
1302
tree.lock_read()
1262
1308
self.assertEqual(sorted(expected), sorted(file_ids))
1310
def test_exceptions_raised(self):
1311
# This is a direct test of bug #495023, it relies on osutils.is_inside
1312
# getting called in an inner function. Which makes it a bit brittle,
1313
# but at least it does reproduce the bug.
1314
tree = self.make_branch_and_tree('tree')
1315
self.build_tree(['tree/file', 'tree/dir/', 'tree/dir/sub',
1316
'tree/dir2/', 'tree/dir2/sub2'])
1317
tree.add(['file', 'dir', 'dir/sub', 'dir2', 'dir2/sub2'])
1318
tree.commit('first commit')
1320
self.addCleanup(tree.unlock)
1321
basis_tree = tree.basis_tree()
1322
def is_inside_raises(*args, **kwargs):
1323
raise RuntimeError('stop this')
1324
self.overrideAttr(osutils, 'is_inside', is_inside_raises)
1325
self.assertListRaises(RuntimeError, tree.iter_changes, basis_tree)
1264
1327
def test_simple_changes(self):
1265
1328
tree = self.make_branch_and_tree('tree')
1266
1329
self.build_tree(['tree/file'])
1281
1344
self.assertChangedFileIds(['file-id'], tree)
1284
class TestProcessEntryC(TestProcessEntryPython):
1285
_test_needs_features = [CompiledDirstateHelpersFeature]
1287
def setup_process_entry(self):
1288
from bzrlib import _dirstate_helpers_c
1289
orig = dirstate._process_entry
1291
dirstate._process_entry = orig
1292
self.addCleanup(cleanup)
1293
dirstate._process_entry = _dirstate_helpers_c.ProcessEntryC
1347
class TestPackStat(tests.TestCase):
1348
"""Check packed representaton of stat values is robust on all inputs"""
1350
scenarios = helper_scenarios
1352
def pack(self, statlike_tuple):
1353
return self.helpers.pack_stat(os.stat_result(statlike_tuple))
1356
def unpack_field(packed_string, stat_field):
1357
return _dirstate_helpers_py._unpack_stat(packed_string)[stat_field]
1359
def test_result(self):
1360
self.assertEqual("AAAQAAAAABAAAAARAAAAAgAAAAEAAIHk",
1361
self.pack((33252, 1, 2, 0, 0, 0, 4096, 15.5, 16.5, 17.5)))
1363
def test_giant_inode(self):
1364
packed = self.pack((33252, 0xF80000ABC, 0, 0, 0, 0, 0, 0, 0, 0))
1365
self.assertEqual(0x80000ABC, self.unpack_field(packed, "st_ino"))
1367
def test_giant_size(self):
1368
packed = self.pack((33252, 0, 0, 0, 0, 0, (1 << 33) + 4096, 0, 0, 0))
1369
self.assertEqual(4096, self.unpack_field(packed, "st_size"))
1371
def test_fractional_mtime(self):
1372
packed = self.pack((33252, 0, 0, 0, 0, 0, 0, 0, 16.9375, 0))
1373
self.assertEqual(16, self.unpack_field(packed, "st_mtime"))
1375
def test_ancient_mtime(self):
1376
packed = self.pack((33252, 0, 0, 0, 0, 0, 0, 0, -11644473600.0, 0))
1377
self.assertEqual(1240428288, self.unpack_field(packed, "st_mtime"))
1379
def test_distant_mtime(self):
1380
packed = self.pack((33252, 0, 0, 0, 0, 0, 0, 0, 64060588800.0, 0))
1381
self.assertEqual(3931046656, self.unpack_field(packed, "st_mtime"))
1383
def test_fractional_ctime(self):
1384
packed = self.pack((33252, 0, 0, 0, 0, 0, 0, 0, 0, 17.5625))
1385
self.assertEqual(17, self.unpack_field(packed, "st_ctime"))
1387
def test_ancient_ctime(self):
1388
packed = self.pack((33252, 0, 0, 0, 0, 0, 0, 0, 0, -11644473600.0))
1389
self.assertEqual(1240428288, self.unpack_field(packed, "st_ctime"))
1391
def test_distant_ctime(self):
1392
packed = self.pack((33252, 0, 0, 0, 0, 0, 0, 0, 0, 64060588800.0))
1393
self.assertEqual(3931046656, self.unpack_field(packed, "st_ctime"))
1395
def test_negative_dev(self):
1396
packed = self.pack((33252, 0, -0xFFFFFCDE, 0, 0, 0, 0, 0, 0, 0))
1397
self.assertEqual(0x322, self.unpack_field(packed, "st_dev"))