~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test__dirstate_helpers.py

  • Committer: Patch Queue Manager
  • Date: 2014-02-12 18:22:22 UTC
  • mfrom: (6589.2.1 trunk)
  • Revision ID: pqm@pqm.ubuntu.com-20140212182222-beouo25gaf1cny76
(vila) The XDG Base Directory Specification uses the XDG_CACHE_HOME,
 not XDG_CACHE_DIR. (Andrew Starr-Bochicchio)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2007, 2008 Canonical Ltd
 
1
# Copyright (C) 2007-2011 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
25
25
    errors,
26
26
    osutils,
27
27
    tests,
28
 
    )
29
 
from bzrlib.tests import (
30
 
        SymlinkFeature,
31
 
        )
32
 
from bzrlib.tests import test_dirstate
33
 
 
34
 
 
35
 
class _CompiledDirstateHelpersFeature(tests.Feature):
36
 
    def _probe(self):
37
 
        try:
38
 
            import bzrlib._dirstate_helpers_c
39
 
        except ImportError:
40
 
            return False
41
 
        return True
42
 
 
43
 
    def feature_name(self):
44
 
        return 'bzrlib._dirstate_helpers_c'
45
 
 
46
 
CompiledDirstateHelpersFeature = _CompiledDirstateHelpersFeature()
 
28
    _dirstate_helpers_py,
 
29
    )
 
30
from bzrlib.tests import (
 
31
    test_dirstate,
 
32
    )
 
33
from bzrlib.tests.test_osutils import dir_reader_scenarios
 
34
from bzrlib.tests.scenarios import (
 
35
    load_tests_apply_scenarios,
 
36
    multiply_scenarios,
 
37
    )
 
38
from bzrlib.tests import (
 
39
    features,
 
40
    )
 
41
 
 
42
 
 
43
load_tests = load_tests_apply_scenarios
 
44
 
 
45
 
 
46
compiled_dirstate_helpers_feature = features.ModuleAvailableFeature(
 
47
    'bzrlib._dirstate_helpers_pyx')
 
48
 
 
49
 
 
50
# FIXME: we should also parametrize against SHA1Provider !
 
51
 
 
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}))
 
57
 
 
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}))
 
63
 
 
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}))
47
68
 
48
69
 
49
70
class TestBisectPathMixin(object):
202
223
 
203
224
 
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."""
206
227
 
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
210
231
 
211
232
    def get_bisect(self):
212
233
        return bisect.bisect_left, 0
213
234
 
214
235
 
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"""
217
238
 
218
 
    _test_needs_features = [CompiledDirstateHelpersFeature]
 
239
    _test_needs_features = [compiled_dirstate_helpers_feature]
219
240
 
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
223
244
 
224
245
 
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"""
227
248
 
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
231
252
 
232
253
    def get_bisect(self):
233
254
        return bisect.bisect_right, -1
234
255
 
235
256
 
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"""
238
259
 
239
 
    _test_needs_features = [CompiledDirstateHelpersFeature]
 
260
    _test_needs_features = [compiled_dirstate_helpers_feature]
240
261
 
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
244
265
 
245
266
 
246
267
class TestBisectDirblock(tests.TestCase):
257
278
 
258
279
    def get_bisect_dirblock(self):
259
280
        """Return an implementation of bisect_dirblock"""
260
 
        from bzrlib._dirstate_helpers_py import bisect_dirblock_py
261
 
        return bisect_dirblock_py
 
281
        from bzrlib._dirstate_helpers_py import bisect_dirblock
 
282
        return bisect_dirblock
262
283
 
263
284
    def assertBisect(self, dirblocks, split_dirblocks, path, *args, **kwargs):
264
285
        """Assert that bisect_split works like bisect_left on the split paths.
348
369
    compiled version.
349
370
    """
350
371
 
351
 
    _test_needs_features = [CompiledDirstateHelpersFeature]
 
372
    _test_needs_features = [compiled_dirstate_helpers_feature]
352
373
 
353
374
    def get_bisect_dirblock(self):
354
 
        from bzrlib._dirstate_helpers_c import bisect_dirblock_c
355
 
        return bisect_dirblock_c
 
375
        from bzrlib._dirstate_helpers_pyx import bisect_dirblock
 
376
        return bisect_dirblock
356
377
 
357
378
 
358
379
class TestCmpByDirs(tests.TestCase):
367
388
 
368
389
    def get_cmp_by_dirs(self):
369
390
        """Get a specific implementation of cmp_by_dirs."""
370
 
        from bzrlib._dirstate_helpers_py import cmp_by_dirs_py
371
 
        return cmp_by_dirs_py
 
391
        from bzrlib._dirstate_helpers_py import cmp_by_dirs
 
392
        return cmp_by_dirs
372
393
 
373
394
    def assertCmpByDirs(self, expected, str1, str2):
374
395
        """Compare the two strings, in both directions.
470
491
class TestCompiledCmpByDirs(TestCmpByDirs):
471
492
    """Test the pyrex implementation of cmp_by_dirs"""
472
493
 
473
 
    _test_needs_features = [CompiledDirstateHelpersFeature]
 
494
    _test_needs_features = [compiled_dirstate_helpers_feature]
474
495
 
475
496
    def get_cmp_by_dirs(self):
476
 
        from bzrlib._dirstate_helpers_c import cmp_by_dirs_c
477
 
        return cmp_by_dirs_c
 
497
        from bzrlib._dirstate_helpers_pyx import cmp_by_dirs
 
498
        return cmp_by_dirs
478
499
 
479
500
 
480
501
class TestCmpPathByDirblock(tests.TestCase):
489
510
 
490
511
    def get_cmp_path_by_dirblock(self):
491
512
        """Get a specific implementation of _cmp_path_by_dirblock."""
492
 
        from bzrlib._dirstate_helpers_py import _cmp_path_by_dirblock_py
493
 
        return _cmp_path_by_dirblock_py
 
513
        from bzrlib._dirstate_helpers_py import _cmp_path_by_dirblock
 
514
        return _cmp_path_by_dirblock
494
515
 
495
516
    def assertCmpPathByDirblock(self, paths):
496
517
        """Compare all paths and make sure they evaluate to the correct order.
621
642
class TestCompiledCmpPathByDirblock(TestCmpPathByDirblock):
622
643
    """Test the pyrex implementation of _cmp_path_by_dirblock"""
623
644
 
624
 
    _test_needs_features = [CompiledDirstateHelpersFeature]
 
645
    _test_needs_features = [compiled_dirstate_helpers_feature]
625
646
 
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
629
650
 
630
651
 
631
652
class TestMemRChr(tests.TestCase):
632
653
    """Test memrchr functionality"""
633
654
 
634
 
    _test_needs_features = [CompiledDirstateHelpersFeature]
 
655
    _test_needs_features = [compiled_dirstate_helpers_feature]
635
656
 
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))
639
660
 
640
661
    def test_missing(self):
681
702
    implementation.
682
703
    """
683
704
 
 
705
    # inherits scenarios from test_dirstate
 
706
 
684
707
    def get_read_dirblocks(self):
685
 
        from bzrlib._dirstate_helpers_py import _read_dirblocks_py
686
 
        return _read_dirblocks_py
 
708
        from bzrlib._dirstate_helpers_py import _read_dirblocks
 
709
        return _read_dirblocks
687
710
 
688
711
    def test_smoketest(self):
689
712
        """Make sure that we can create and read back a simple file."""
699
722
 
700
723
    def test_trailing_garbage(self):
701
724
        tree, state, expected = self.create_basic_dirstate()
702
 
        # We can modify the file as long as it hasn't been read yet.
 
725
        # On Unix, we can write extra data as long as we haven't read yet, but
 
726
        # on Win32, if you've opened the file with FILE_SHARE_READ, trying to
 
727
        # open it in append mode will fail.
 
728
        state.unlock()
703
729
        f = open('dirstate', 'ab')
704
730
        try:
705
731
            # Add bogus trailing garbage
706
732
            f.write('bogus\n')
707
733
        finally:
708
734
            f.close()
 
735
            state.lock_read()
709
736
        e = self.assertRaises(errors.DirstateCorrupt,
710
737
                              state._read_dirblocks_if_needed)
711
738
        # Make sure we mention the bogus characters in the error
715
742
class TestCompiledReadDirblocks(TestReadDirblocks):
716
743
    """Test the pyrex implementation of _read_dirblocks"""
717
744
 
718
 
    _test_needs_features = [CompiledDirstateHelpersFeature]
 
745
    _test_needs_features = [compiled_dirstate_helpers_feature]
719
746
 
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
723
750
 
724
751
 
725
752
class TestUsingCompiledIfAvailable(tests.TestCase):
726
753
    """Check that any compiled functions that are available are the default.
727
754
 
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
730
757
    not being used.
731
758
    """
732
759
 
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
737
763
        else:
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)
740
766
 
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
745
770
        else:
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)
748
773
 
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
753
777
        else:
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)
756
780
 
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
761
784
        else:
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)
764
787
 
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
769
791
        else:
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)
772
794
 
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
777
798
        else:
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)
780
801
 
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)
785
806
        else:
786
807
            from bzrlib.dirstate import ProcessEntryPython
790
811
class TestUpdateEntry(test_dirstate.TestCaseWithDirState):
791
812
    """Test the DirState.update_entry functions"""
792
813
 
 
814
    scenarios = multiply_scenarios(
 
815
        dir_reader_scenarios(), ue_scenarios)
 
816
 
 
817
    # Set by load_tests
 
818
    update_entry = None
 
819
 
 
820
    def setUp(self):
 
821
        super(TestUpdateEntry, self).setUp()
 
822
        self.overrideAttr(dirstate, 'update_entry', self.update_entry)
 
823
 
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
801
831
 
802
 
    def set_update_entry(self):
803
 
        self.update_entry = dirstate.py_update_entry
804
 
 
805
832
    def test_observed_sha1_cachable(self):
806
833
        state, entry = self.get_state_with_a()
 
834
        state.save()
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)
816
847
 
817
848
    def test_observed_sha1_not_cachable(self):
818
849
        state, entry = self.get_state_with_a()
 
850
        state.save()
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)
826
862
 
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)
855
891
 
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),
858
896
                         entry[1][0])
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)
865
903
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
866
904
                         state._dirblock_state)
867
905
 
868
 
        # If we do it again right away, we don't know if the file has changed
869
 
        # so we will re-read the file. Roll the clock back so the file is
870
 
        # guaranteed to look too new.
 
906
        # Roll the clock back so the file is guaranteed to look too new. We
 
907
        # should still not compute the sha1.
871
908
        state.adjust_time(-10)
872
909
        del state._log[:]
873
910
 
875
912
                                          stat_value=stat_value)
876
913
        self.assertEqual([('is_exec', mode, False)], state._log)
877
914
        self.assertEqual(None, link_or_sha1)
878
 
        self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
 
915
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
879
916
                         state._dirblock_state)
880
917
        self.assertEqual(('f', '', 14, False, dirstate.DirState.NULLSTAT),
881
918
                         entry[1][0])
891
928
        self.assertEqual([('is_exec', mode, False)], state._log)
892
929
        self.assertEqual(('f', '', 14, False, dirstate.DirState.NULLSTAT),
893
930
                         entry[1][0])
 
931
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
 
932
                         state._dirblock_state)
894
933
 
895
934
        # If the file is no longer new, and the clock has been moved forward
896
935
        # sufficiently, it will cache the sha.
921
960
 
922
961
    def test_update_entry_symlink(self):
923
962
        """Update entry should read symlinks."""
924
 
        self.requireFeature(SymlinkFeature)
 
963
        self.requireFeature(features.SymlinkFeature)
925
964
        state, entry = self.get_state_with_a()
926
965
        state.save()
927
966
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
938
977
        # Dirblock is not updated (the link is too new)
939
978
        self.assertEqual([('l', '', 6, False, dirstate.DirState.NULLSTAT)],
940
979
                         entry[1])
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)
943
984
 
944
985
        # Because the stat_value looks new, we should re-read the target
 
986
        del state._log[:]
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', ''),
950
 
                         ], state._log)
 
990
        self.assertEqual([('read_link', 'a', '')], state._log)
951
991
        self.assertEqual([('l', '', 6, False, dirstate.DirState.NULLSTAT)],
952
992
                         entry[1])
 
993
        state.save()
953
994
        state.adjust_time(+20) # Skip into the future, all files look old
 
995
        del state._log[:]
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', ''),
961
 
                         ], state._log)
 
1005
        self.assertEqual([('read_link', 'a', '')], state._log)
962
1006
        self.assertEqual([('l', 'target', 6, False, packed_stat)],
963
1007
                         entry[1])
964
1008
 
 
1009
        del state._log[:]
965
1010
        # Another call won't re-read the link
966
 
        self.assertEqual([('read_link', 'a', ''),
967
 
                          ('read_link', 'a', ''),
968
 
                          ('read_link', 'a', ''),
969
 
                         ], state._log)
 
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)
992
1035
        state.save()
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
 
1044
        try:
 
1045
            os.utime('a', (t, t))
 
1046
        except OSError:
 
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
 
1053
        # recompute.
 
1054
        self.assertNotEqual(saved_packed_stat, entry[1][0][-1])
996
1055
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
997
1056
                         state._dirblock_state)
998
1057
 
1022
1081
                         state._dirblock_state)
1023
1082
 
1024
1083
    def test_update_entry_tree_reference(self):
1025
 
        self.set_update_entry()
1026
1084
        state = test_dirstate.InstrumentedDirState.initialize('dirstate')
1027
1085
        self.addCleanup(state.unlock)
1028
1086
        state.add('r', 'r-id', 'tree-reference', None, '')
1064
1122
 
1065
1123
        return packed_stat
1066
1124
 
 
1125
    # FIXME: Add unicode version
1067
1126
    def create_and_test_symlink(self, state, entry):
1068
1127
        """Create a symlink at 'a' and verify the state finds it.
1069
1128
 
1098
1157
 
1099
1158
    def test_update_file_to_symlink(self):
1100
1159
        """File becomes a symlink"""
1101
 
        self.requireFeature(SymlinkFeature)
 
1160
        self.requireFeature(features.SymlinkFeature)
1102
1161
        state, entry = self.get_state_with_a()
1103
1162
        # The file sha1 won't be cached unless the file is old
1104
1163
        state.adjust_time(+10)
1117
1176
 
1118
1177
    def test_update_dir_to_symlink(self):
1119
1178
        """Directory becomes a symlink"""
1120
 
        self.requireFeature(SymlinkFeature)
 
1179
        self.requireFeature(features.SymlinkFeature)
1121
1180
        state, entry = self.get_state_with_a()
1122
1181
        # The symlink target won't be cached if it isn't old
1123
1182
        state.adjust_time(+10)
1127
1186
 
1128
1187
    def test_update_symlink_to_file(self):
1129
1188
        """Symlink becomes a file"""
1130
 
        self.requireFeature(SymlinkFeature)
 
1189
        self.requireFeature(features.SymlinkFeature)
1131
1190
        state, entry = self.get_state_with_a()
1132
1191
        # The symlink and file info won't be cached unless old
1133
1192
        state.adjust_time(+10)
1137
1196
 
1138
1197
    def test_update_symlink_to_dir(self):
1139
1198
        """Symlink becomes a directory"""
1140
 
        self.requireFeature(SymlinkFeature)
 
1199
        self.requireFeature(features.SymlinkFeature)
1141
1200
        state, entry = self.get_state_with_a()
1142
1201
        # The symlink target won't be cached if it isn't old
1143
1202
        state.adjust_time(+10)
1166
1225
        self.assertEqual([('f', '', 14, True, dirstate.DirState.NULLSTAT)],
1167
1226
                         entry[1])
1168
1227
 
1169
 
        # Make the disk object look old enough to cache (but it won't cache the sha
1170
 
        # as it is a new file).
 
1228
        # Make the disk object look old enough to cache (but it won't cache the
 
1229
        # sha as it is a new file).
1171
1230
        state.adjust_time(+20)
1172
1231
        digest = 'b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6'
1173
1232
        self.update_entry(state, entry, abspath='a', stat_value=stat_value)
1194
1253
        entry = state._get_entry(0, path_utf8='a file')
1195
1254
        state._sha_cutoff_time()
1196
1255
        state._cutoff_time += 10
1197
 
        sha1 = dirstate.update_entry(state, entry, 'tree/a file',
1198
 
            os.lstat('tree/a file'))
 
1256
        sha1 = self.update_entry(state, entry, 'tree/a file',
 
1257
                                 os.lstat('tree/a file'))
1199
1258
        self.assertEqual(expected_sha, sha1)
1200
1259
 
1201
1260
    def test_sha1provider_stat_and_sha1_used(self):
1206
1265
        state._sha1_provider = UppercaseSHA1Provider()
1207
1266
        # If we used the standard provider, it would look like nothing has
1208
1267
        # changed
1209
 
        file_ids_changed = [change[0] for change 
1210
 
                in tree.iter_changes(tree.basis_tree())]
 
1268
        file_ids_changed = [change[0] for change
 
1269
                            in tree.iter_changes(tree.basis_tree())]
1211
1270
        self.assertEqual(['a-file-id'], file_ids_changed)
1212
1271
 
1213
1272
 
1228
1287
        return statvalue, sha1
1229
1288
 
1230
1289
 
1231
 
class TestCompiledUpdateEntry(TestUpdateEntry):
1232
 
    """Test the pyrex implementation of _read_dirblocks"""
1233
 
 
1234
 
    _test_needs_features = [CompiledDirstateHelpersFeature]
1235
 
 
1236
 
    def set_update_entry(self):
1237
 
        from bzrlib._dirstate_helpers_c import update_entry
1238
 
        self.update_entry = update_entry
1239
 
 
1240
 
 
1241
 
class TestProcessEntryPython(test_dirstate.TestCaseWithDirState):
 
1290
class TestProcessEntry(test_dirstate.TestCaseWithDirState):
 
1291
 
 
1292
    scenarios = multiply_scenarios(dir_reader_scenarios(), pe_scenarios)
 
1293
 
 
1294
    # Set by load_tests
 
1295
    _process_entry = None
1242
1296
 
1243
1297
    def setUp(self):
1244
 
        super(TestProcessEntryPython, self).setUp()
1245
 
        self.setup_process_entry()
1246
 
 
1247
 
    def setup_process_entry(self):
1248
 
        from bzrlib import dirstate
1249
 
        orig = dirstate._process_entry
1250
 
        def cleanup():
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)
1254
1300
 
1255
1301
    def assertChangedFileIds(self, expected, tree):
1256
1302
        tree.lock_read()
1261
1307
            tree.unlock()
1262
1308
        self.assertEqual(sorted(expected), sorted(file_ids))
1263
1309
 
 
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')
 
1319
        tree.lock_read()
 
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)
 
1326
 
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)
1282
1345
 
1283
1346
 
1284
 
class TestProcessEntryC(TestProcessEntryPython):
1285
 
    _test_needs_features = [CompiledDirstateHelpersFeature]
1286
 
 
1287
 
    def setup_process_entry(self):
1288
 
        from bzrlib import _dirstate_helpers_c
1289
 
        orig = dirstate._process_entry
1290
 
        def cleanup():
1291
 
            dirstate._process_entry = orig
1292
 
        self.addCleanup(cleanup)
1293
 
        dirstate._process_entry = _dirstate_helpers_c.ProcessEntryC
1294
 
 
 
1347
class TestPackStat(tests.TestCase):
 
1348
    """Check packed representaton of stat values is robust on all inputs"""
 
1349
 
 
1350
    scenarios = helper_scenarios
 
1351
 
 
1352
    def pack(self, statlike_tuple):
 
1353
        return self.helpers.pack_stat(os.stat_result(statlike_tuple))
 
1354
 
 
1355
    @staticmethod
 
1356
    def unpack_field(packed_string, stat_field):
 
1357
        return _dirstate_helpers_py._unpack_stat(packed_string)[stat_field]
 
1358
 
 
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)))
 
1362
 
 
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"))
 
1366
 
 
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"))
 
1370
 
 
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"))
 
1374
 
 
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"))
 
1378
 
 
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"))
 
1382
 
 
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"))
 
1386
 
 
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"))
 
1390
 
 
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"))
 
1394
 
 
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"))