~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test__dirstate_helpers.py

  • Committer: Ian Clatworthy
  • Date: 2007-08-13 14:16:53 UTC
  • mto: (2733.1.1 ianc-integration)
  • mto: This revision was merged to the branch mainline in revision 2734.
  • Revision ID: ian.clatworthy@internode.on.net-20070813141653-3cbrp00xowq58zv1
Added mini tutorial

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2007, 2008 Canonical Ltd
 
1
# Copyright (C) 2007 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
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
17
"""Tests for the compiled dirstate helpers."""
18
18
 
19
19
import bisect
20
20
import os
21
 
import time
22
21
 
23
22
from bzrlib import (
24
23
    dirstate,
25
 
    errors,
26
 
    osutils,
27
24
    tests,
28
25
    )
29
 
from bzrlib.tests import (
30
 
    test_dirstate,
31
 
    test_osutils,
32
 
    )
33
 
 
34
 
try:
35
 
    from bzrlib import _dirstate_helpers_pyx
36
 
    has_dirstate_helpers_pyx = True
37
 
except ImportError:
38
 
    has_dirstate_helpers_pyx = False
 
26
from bzrlib.tests import test_dirstate
39
27
 
40
28
 
41
29
class _CompiledDirstateHelpersFeature(tests.Feature):
42
30
    def _probe(self):
43
 
        return has_dirstate_helpers_pyx
 
31
        try:
 
32
            import bzrlib._dirstate_helpers_c
 
33
        except ImportError:
 
34
            return False
 
35
        return True
44
36
 
45
37
    def feature_name(self):
46
 
        return 'bzrlib._dirstate_helpers_pyx'
 
38
        return 'bzrlib._dirstate_helpers_c'
 
39
 
47
40
CompiledDirstateHelpersFeature = _CompiledDirstateHelpersFeature()
48
41
 
49
42
 
50
 
def load_tests(basic_tests, module, loader):
51
 
    # FIXME: we should also parametrize against SHA1Provider !
52
 
    suite = loader.suiteClass()
53
 
    remaining_tests = basic_tests
54
 
 
55
 
    dir_reader_scenarios = test_osutils.dir_reader_scenarios()
56
 
 
57
 
    ue_scenarios = [('dirstate_Python',
58
 
                     {'update_entry': dirstate.py_update_entry})]
59
 
    if has_dirstate_helpers_pyx:
60
 
        pyrex_scenario = ('dirstate_Pyrex',
61
 
                          {'update_entry': _dirstate_helpers_pyx.update_entry})
62
 
        ue_scenarios.append(pyrex_scenario)
63
 
    process_entry_tests, remaining_tests = tests.split_suite_by_condition(
64
 
        remaining_tests, tests.condition_isinstance(TestUpdateEntry))
65
 
    tests.multiply_tests(process_entry_tests,
66
 
                         tests.multiply_scenarios(dir_reader_scenarios,
67
 
                                                  ue_scenarios),
68
 
                         suite)
69
 
 
70
 
    pe_scenarios = [('dirstate_Python',
71
 
                     {'_process_entry': dirstate.ProcessEntryPython})]
72
 
    if has_dirstate_helpers_pyx:
73
 
        pyrex_scenario = (
74
 
            'dirstate_Pyrex',
75
 
            {'_process_entry': _dirstate_helpers_pyx.ProcessEntryC})
76
 
        pe_scenarios.append(pyrex_scenario)
77
 
    process_entry_tests, remaining_tests = tests.split_suite_by_condition(
78
 
        remaining_tests, tests.condition_isinstance(TestProcessEntry))
79
 
    tests.multiply_tests(process_entry_tests,
80
 
                         tests.multiply_scenarios(dir_reader_scenarios,
81
 
                                                  pe_scenarios),
82
 
                         suite)
83
 
 
84
 
    dir_reader_tests, remaining_tests = tests.split_suite_by_condition(
85
 
        remaining_tests, tests.condition_isinstance(
86
 
            test_dirstate.TestCaseWithDirState))
87
 
    tests.multiply_tests(dir_reader_tests, dir_reader_scenarios, suite)
88
 
    suite.addTest(remaining_tests)
89
 
 
90
 
    return suite
91
 
 
92
 
 
93
43
class TestBisectPathMixin(object):
94
44
    """Test that _bisect_path_*() returns the expected values.
95
45
 
246
196
 
247
197
 
248
198
class TestBisectPathLeft(tests.TestCase, TestBisectPathMixin):
249
 
    """Run all Bisect Path tests against _bisect_path_left."""
 
199
    """Run all Bisect Path tests against _bisect_path_left_py."""
250
200
 
251
201
    def get_bisect_path(self):
252
 
        from bzrlib._dirstate_helpers_py import _bisect_path_left
253
 
        return _bisect_path_left
 
202
        from bzrlib._dirstate_helpers_py import _bisect_path_left_py
 
203
        return _bisect_path_left_py
254
204
 
255
205
    def get_bisect(self):
256
206
        return bisect.bisect_left, 0
257
207
 
258
208
 
259
209
class TestCompiledBisectPathLeft(TestBisectPathLeft):
260
 
    """Run all Bisect Path tests against _bisect_path_lect"""
 
210
    """Run all Bisect Path tests against _bisect_path_right_c"""
261
211
 
262
212
    _test_needs_features = [CompiledDirstateHelpersFeature]
263
213
 
264
214
    def get_bisect_path(self):
265
 
        from bzrlib._dirstate_helpers_pyx import _bisect_path_left
266
 
        return _bisect_path_left
 
215
        from bzrlib._dirstate_helpers_c import _bisect_path_left_c
 
216
        return _bisect_path_left_c
267
217
 
268
218
 
269
219
class TestBisectPathRight(tests.TestCase, TestBisectPathMixin):
270
 
    """Run all Bisect Path tests against _bisect_path_right"""
 
220
    """Run all Bisect Path tests against _bisect_path_right_py"""
271
221
 
272
222
    def get_bisect_path(self):
273
 
        from bzrlib._dirstate_helpers_py import _bisect_path_right
274
 
        return _bisect_path_right
 
223
        from bzrlib._dirstate_helpers_py import _bisect_path_right_py
 
224
        return _bisect_path_right_py
275
225
 
276
226
    def get_bisect(self):
277
227
        return bisect.bisect_right, -1
278
228
 
279
229
 
280
230
class TestCompiledBisectPathRight(TestBisectPathRight):
281
 
    """Run all Bisect Path tests against _bisect_path_right"""
 
231
    """Run all Bisect Path tests against _bisect_path_right_c"""
282
232
 
283
233
    _test_needs_features = [CompiledDirstateHelpersFeature]
284
234
 
285
235
    def get_bisect_path(self):
286
 
        from bzrlib._dirstate_helpers_pyx import _bisect_path_right
287
 
        return _bisect_path_right
 
236
        from bzrlib._dirstate_helpers_c import _bisect_path_right_c
 
237
        return _bisect_path_right_c
288
238
 
289
239
 
290
240
class TestBisectDirblock(tests.TestCase):
301
251
 
302
252
    def get_bisect_dirblock(self):
303
253
        """Return an implementation of bisect_dirblock"""
304
 
        from bzrlib._dirstate_helpers_py import bisect_dirblock
305
 
        return bisect_dirblock
 
254
        from bzrlib._dirstate_helpers_py import bisect_dirblock_py
 
255
        return bisect_dirblock_py
306
256
 
307
257
    def assertBisect(self, dirblocks, split_dirblocks, path, *args, **kwargs):
308
258
        """Assert that bisect_split works like bisect_left on the split paths.
395
345
    _test_needs_features = [CompiledDirstateHelpersFeature]
396
346
 
397
347
    def get_bisect_dirblock(self):
398
 
        from bzrlib._dirstate_helpers_pyx import bisect_dirblock
399
 
        return bisect_dirblock
 
348
        from bzrlib._dirstate_helpers_c import bisect_dirblock_c
 
349
        return bisect_dirblock_c
400
350
 
401
351
 
402
352
class TestCmpByDirs(tests.TestCase):
411
361
 
412
362
    def get_cmp_by_dirs(self):
413
363
        """Get a specific implementation of cmp_by_dirs."""
414
 
        from bzrlib._dirstate_helpers_py import cmp_by_dirs
415
 
        return cmp_by_dirs
 
364
        from bzrlib._dirstate_helpers_py import cmp_by_dirs_py
 
365
        return cmp_by_dirs_py
416
366
 
417
367
    def assertCmpByDirs(self, expected, str1, str2):
418
368
        """Compare the two strings, in both directions.
517
467
    _test_needs_features = [CompiledDirstateHelpersFeature]
518
468
 
519
469
    def get_cmp_by_dirs(self):
520
 
        from bzrlib._dirstate_helpers_pyx import cmp_by_dirs
521
 
        return cmp_by_dirs
 
470
        from bzrlib._dirstate_helpers_c import cmp_by_dirs_c
 
471
        return cmp_by_dirs_c
522
472
 
523
473
 
524
474
class TestCmpPathByDirblock(tests.TestCase):
533
483
 
534
484
    def get_cmp_path_by_dirblock(self):
535
485
        """Get a specific implementation of _cmp_path_by_dirblock."""
536
 
        from bzrlib._dirstate_helpers_py import _cmp_path_by_dirblock
537
 
        return _cmp_path_by_dirblock
 
486
        from bzrlib._dirstate_helpers_py import _cmp_path_by_dirblock_py
 
487
        return _cmp_path_by_dirblock_py
538
488
 
539
489
    def assertCmpPathByDirblock(self, paths):
540
490
        """Compare all paths and make sure they evaluate to the correct order.
668
618
    _test_needs_features = [CompiledDirstateHelpersFeature]
669
619
 
670
620
    def get_cmp_by_dirs(self):
671
 
        from bzrlib._dirstate_helpers_pyx import _cmp_path_by_dirblock
672
 
        return _cmp_path_by_dirblock
 
621
        from bzrlib._dirstate_helpers_c import _cmp_path_by_dirblock_c
 
622
        return _cmp_path_by_dirblock_c
673
623
 
674
624
 
675
625
class TestMemRChr(tests.TestCase):
678
628
    _test_needs_features = [CompiledDirstateHelpersFeature]
679
629
 
680
630
    def assertMemRChr(self, expected, s, c):
681
 
        from bzrlib._dirstate_helpers_pyx import _py_memrchr
 
631
        from bzrlib._dirstate_helpers_c import _py_memrchr
682
632
        self.assertEqual(expected, _py_memrchr(s, c))
683
633
 
684
634
    def test_missing(self):
726
676
    """
727
677
 
728
678
    def get_read_dirblocks(self):
729
 
        from bzrlib._dirstate_helpers_py import _read_dirblocks
730
 
        return _read_dirblocks
 
679
        from bzrlib._dirstate_helpers_py import _read_dirblocks_py
 
680
        return _read_dirblocks_py
731
681
 
732
682
    def test_smoketest(self):
733
683
        """Make sure that we can create and read back a simple file."""
741
691
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
742
692
                         state._dirblock_state)
743
693
 
744
 
    def test_trailing_garbage(self):
745
 
        tree, state, expected = self.create_basic_dirstate()
746
 
        # We can modify the file as long as it hasn't been read yet.
747
 
        f = open('dirstate', 'ab')
748
 
        try:
749
 
            # Add bogus trailing garbage
750
 
            f.write('bogus\n')
751
 
        finally:
752
 
            f.close()
753
 
        e = self.assertRaises(errors.DirstateCorrupt,
754
 
                              state._read_dirblocks_if_needed)
755
 
        # Make sure we mention the bogus characters in the error
756
 
        self.assertContainsRe(str(e), 'bogus')
757
 
 
758
694
 
759
695
class TestCompiledReadDirblocks(TestReadDirblocks):
760
696
    """Test the pyrex implementation of _read_dirblocks"""
762
698
    _test_needs_features = [CompiledDirstateHelpersFeature]
763
699
 
764
700
    def get_read_dirblocks(self):
765
 
        from bzrlib._dirstate_helpers_pyx import _read_dirblocks
766
 
        return _read_dirblocks
 
701
        from bzrlib._dirstate_helpers_c import _read_dirblocks_c
 
702
        return _read_dirblocks_c
767
703
 
768
704
 
769
705
class TestUsingCompiledIfAvailable(tests.TestCase):
770
706
    """Check that any compiled functions that are available are the default.
771
707
 
772
708
    It is possible to have typos, etc in the import line, such that
773
 
    _dirstate_helpers_pyx is actually available, but the compiled functions are
 
709
    _dirstate_helpers_c is actually available, but the compiled functions are
774
710
    not being used.
775
711
    """
776
712
 
777
713
    def test_bisect_dirblock(self):
778
714
        if CompiledDirstateHelpersFeature.available():
779
 
            from bzrlib._dirstate_helpers_pyx import bisect_dirblock
 
715
            from bzrlib._dirstate_helpers_c import bisect_dirblock_c
 
716
            self.assertIs(bisect_dirblock_c, dirstate.bisect_dirblock)
780
717
        else:
781
 
            from bzrlib._dirstate_helpers_py import bisect_dirblock
782
 
        self.assertIs(bisect_dirblock, dirstate.bisect_dirblock)
 
718
            from bzrlib._dirstate_helpers_py import bisect_dirblock_py
 
719
            self.assertIs(bisect_dirblock_py, dirstate.bisect_dirblock)
783
720
 
784
721
    def test__bisect_path_left(self):
785
722
        if CompiledDirstateHelpersFeature.available():
786
 
            from bzrlib._dirstate_helpers_pyx import _bisect_path_left
 
723
            from bzrlib._dirstate_helpers_c import _bisect_path_left_c
 
724
            self.assertIs(_bisect_path_left_c, dirstate._bisect_path_left)
787
725
        else:
788
 
            from bzrlib._dirstate_helpers_py import _bisect_path_left
789
 
        self.assertIs(_bisect_path_left, dirstate._bisect_path_left)
 
726
            from bzrlib._dirstate_helpers_py import _bisect_path_left_py
 
727
            self.assertIs(_bisect_path_left_py, dirstate._bisect_path_left)
790
728
 
791
729
    def test__bisect_path_right(self):
792
730
        if CompiledDirstateHelpersFeature.available():
793
 
            from bzrlib._dirstate_helpers_pyx import _bisect_path_right
 
731
            from bzrlib._dirstate_helpers_c import _bisect_path_right_c
 
732
            self.assertIs(_bisect_path_right_c, dirstate._bisect_path_right)
794
733
        else:
795
 
            from bzrlib._dirstate_helpers_py import _bisect_path_right
796
 
        self.assertIs(_bisect_path_right, dirstate._bisect_path_right)
 
734
            from bzrlib._dirstate_helpers_py import _bisect_path_right_py
 
735
            self.assertIs(_bisect_path_right_py, dirstate._bisect_path_right)
797
736
 
798
737
    def test_cmp_by_dirs(self):
799
738
        if CompiledDirstateHelpersFeature.available():
800
 
            from bzrlib._dirstate_helpers_pyx import cmp_by_dirs
 
739
            from bzrlib._dirstate_helpers_c import cmp_by_dirs_c
 
740
            self.assertIs(cmp_by_dirs_c, dirstate.cmp_by_dirs)
801
741
        else:
802
 
            from bzrlib._dirstate_helpers_py import cmp_by_dirs
803
 
        self.assertIs(cmp_by_dirs, dirstate.cmp_by_dirs)
 
742
            from bzrlib._dirstate_helpers_py import cmp_by_dirs_py
 
743
            self.assertIs(cmp_by_dirs_py, dirstate.cmp_by_dirs)
804
744
 
805
745
    def test__read_dirblocks(self):
806
746
        if CompiledDirstateHelpersFeature.available():
807
 
            from bzrlib._dirstate_helpers_pyx import _read_dirblocks
808
 
        else:
809
 
            from bzrlib._dirstate_helpers_py import _read_dirblocks
810
 
        self.assertIs(_read_dirblocks, dirstate._read_dirblocks)
811
 
 
812
 
    def test_update_entry(self):
813
 
        if CompiledDirstateHelpersFeature.available():
814
 
            from bzrlib._dirstate_helpers_pyx import update_entry
815
 
        else:
816
 
            from bzrlib.dirstate import update_entry
817
 
        self.assertIs(update_entry, dirstate.update_entry)
818
 
 
819
 
    def test_process_entry(self):
820
 
        if CompiledDirstateHelpersFeature.available():
821
 
            from bzrlib._dirstate_helpers_pyx import ProcessEntryC
822
 
            self.assertIs(ProcessEntryC, dirstate._process_entry)
823
 
        else:
824
 
            from bzrlib.dirstate import ProcessEntryPython
825
 
            self.assertIs(ProcessEntryPython, dirstate._process_entry)
826
 
 
827
 
 
828
 
class TestUpdateEntry(test_dirstate.TestCaseWithDirState):
829
 
    """Test the DirState.update_entry functions"""
830
 
 
831
 
    # Set by load_tests
832
 
    update_entry = None
833
 
 
834
 
    def setUp(self):
835
 
        super(TestUpdateEntry, self).setUp()
836
 
        orig = dirstate.update_entry
837
 
        def cleanup():
838
 
            dirstate.update_entry = orig
839
 
        self.addCleanup(cleanup)
840
 
        dirstate.update_entry = self.update_entry
841
 
 
842
 
    def get_state_with_a(self):
843
 
        """Create a DirState tracking a single object named 'a'"""
844
 
        state = test_dirstate.InstrumentedDirState.initialize('dirstate')
845
 
        self.addCleanup(state.unlock)
846
 
        state.add('a', 'a-id', 'file', None, '')
847
 
        entry = state._get_entry(0, path_utf8='a')
848
 
        return state, entry
849
 
 
850
 
    def test_observed_sha1_cachable(self):
851
 
        state, entry = self.get_state_with_a()
852
 
        atime = time.time() - 10
853
 
        self.build_tree(['a'])
854
 
        statvalue = os.lstat('a')
855
 
        statvalue = test_dirstate._FakeStat(statvalue.st_size, atime, atime,
856
 
            statvalue.st_dev, statvalue.st_ino, statvalue.st_mode)
857
 
        state._observed_sha1(entry, "foo", statvalue)
858
 
        self.assertEqual('foo', entry[1][0][1])
859
 
        packed_stat = dirstate.pack_stat(statvalue)
860
 
        self.assertEqual(packed_stat, entry[1][0][4])
861
 
 
862
 
    def test_observed_sha1_not_cachable(self):
863
 
        state, entry = self.get_state_with_a()
864
 
        oldval = entry[1][0][1]
865
 
        oldstat = entry[1][0][4]
866
 
        self.build_tree(['a'])
867
 
        statvalue = os.lstat('a')
868
 
        state._observed_sha1(entry, "foo", statvalue)
869
 
        self.assertEqual(oldval, entry[1][0][1])
870
 
        self.assertEqual(oldstat, entry[1][0][4])
871
 
 
872
 
    def test_update_entry(self):
873
 
        state, _ = self.get_state_with_a()
874
 
        tree = self.make_branch_and_tree('tree')
875
 
        tree.lock_write()
876
 
        empty_revid = tree.commit('empty')
877
 
        self.build_tree(['tree/a'])
878
 
        tree.add(['a'], ['a-id'])
879
 
        with_a_id = tree.commit('with_a')
880
 
        self.addCleanup(tree.unlock)
881
 
        state.set_parent_trees(
882
 
            [(empty_revid, tree.branch.repository.revision_tree(empty_revid))],
883
 
            [])
884
 
        entry = state._get_entry(0, path_utf8='a')
885
 
        self.build_tree(['a'])
886
 
        # Add one where we don't provide the stat or sha already
887
 
        self.assertEqual(('', 'a', 'a-id'), entry[0])
888
 
        self.assertEqual(('f', '', 0, False, dirstate.DirState.NULLSTAT),
889
 
                         entry[1][0])
890
 
        # Flush the buffers to disk
891
 
        state.save()
892
 
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
893
 
                         state._dirblock_state)
894
 
 
895
 
        stat_value = os.lstat('a')
896
 
        packed_stat = dirstate.pack_stat(stat_value)
897
 
        link_or_sha1 = self.update_entry(state, entry, abspath='a',
898
 
                                          stat_value=stat_value)
899
 
        self.assertEqual(None, link_or_sha1)
900
 
 
901
 
        # The dirblock entry should not have cached the file's sha1 (too new)
902
 
        self.assertEqual(('f', '', 14, False, dirstate.DirState.NULLSTAT),
903
 
                         entry[1][0])
904
 
        self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
905
 
                         state._dirblock_state)
906
 
        mode = stat_value.st_mode
907
 
        self.assertEqual([('is_exec', mode, False)], state._log)
908
 
 
909
 
        state.save()
910
 
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
911
 
                         state._dirblock_state)
912
 
 
913
 
        # If we do it again right away, we don't know if the file has changed
914
 
        # so we will re-read the file. Roll the clock back so the file is
915
 
        # guaranteed to look too new.
916
 
        state.adjust_time(-10)
917
 
        del state._log[:]
918
 
 
919
 
        link_or_sha1 = self.update_entry(state, entry, abspath='a',
920
 
                                          stat_value=stat_value)
921
 
        self.assertEqual([('is_exec', mode, False)], state._log)
922
 
        self.assertEqual(None, link_or_sha1)
923
 
        self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
924
 
                         state._dirblock_state)
925
 
        self.assertEqual(('f', '', 14, False, dirstate.DirState.NULLSTAT),
926
 
                         entry[1][0])
927
 
        state.save()
928
 
 
929
 
        # If it is cachable (the clock has moved forward) but new it still
930
 
        # won't calculate the sha or cache it.
931
 
        state.adjust_time(+20)
932
 
        del state._log[:]
933
 
        link_or_sha1 = dirstate.update_entry(state, entry, abspath='a',
934
 
                                          stat_value=stat_value)
935
 
        self.assertEqual(None, link_or_sha1)
936
 
        self.assertEqual([('is_exec', mode, False)], state._log)
937
 
        self.assertEqual(('f', '', 14, False, dirstate.DirState.NULLSTAT),
938
 
                         entry[1][0])
939
 
 
940
 
        # If the file is no longer new, and the clock has been moved forward
941
 
        # sufficiently, it will cache the sha.
942
 
        del state._log[:]
943
 
        state.set_parent_trees(
944
 
            [(with_a_id, tree.branch.repository.revision_tree(with_a_id))],
945
 
            [])
946
 
        entry = state._get_entry(0, path_utf8='a')
947
 
 
948
 
        link_or_sha1 = self.update_entry(state, entry, abspath='a',
949
 
                                          stat_value=stat_value)
950
 
        self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
951
 
                         link_or_sha1)
952
 
        self.assertEqual([('is_exec', mode, False), ('sha1', 'a')],
953
 
                          state._log)
954
 
        self.assertEqual(('f', link_or_sha1, 14, False, packed_stat),
955
 
                         entry[1][0])
956
 
 
957
 
        # Subsequent calls will just return the cached value
958
 
        del state._log[:]
959
 
        link_or_sha1 = self.update_entry(state, entry, abspath='a',
960
 
                                          stat_value=stat_value)
961
 
        self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
962
 
                         link_or_sha1)
963
 
        self.assertEqual([], state._log)
964
 
        self.assertEqual(('f', link_or_sha1, 14, False, packed_stat),
965
 
                         entry[1][0])
966
 
 
967
 
    def test_update_entry_symlink(self):
968
 
        """Update entry should read symlinks."""
969
 
        self.requireFeature(tests.SymlinkFeature)
970
 
        state, entry = self.get_state_with_a()
971
 
        state.save()
972
 
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
973
 
                         state._dirblock_state)
974
 
        os.symlink('target', 'a')
975
 
 
976
 
        state.adjust_time(-10) # Make the symlink look new
977
 
        stat_value = os.lstat('a')
978
 
        packed_stat = dirstate.pack_stat(stat_value)
979
 
        link_or_sha1 = self.update_entry(state, entry, abspath='a',
980
 
                                          stat_value=stat_value)
981
 
        self.assertEqual('target', link_or_sha1)
982
 
        self.assertEqual([('read_link', 'a', '')], state._log)
983
 
        # Dirblock is not updated (the link is too new)
984
 
        self.assertEqual([('l', '', 6, False, dirstate.DirState.NULLSTAT)],
985
 
                         entry[1])
986
 
        self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
987
 
                         state._dirblock_state)
988
 
 
989
 
        # Because the stat_value looks new, we should re-read the target
990
 
        link_or_sha1 = self.update_entry(state, entry, abspath='a',
991
 
                                          stat_value=stat_value)
992
 
        self.assertEqual('target', link_or_sha1)
993
 
        self.assertEqual([('read_link', 'a', ''),
994
 
                          ('read_link', 'a', ''),
995
 
                         ], state._log)
996
 
        self.assertEqual([('l', '', 6, False, dirstate.DirState.NULLSTAT)],
997
 
                         entry[1])
998
 
        state.adjust_time(+20) # Skip into the future, all files look old
999
 
        link_or_sha1 = self.update_entry(state, entry, abspath='a',
1000
 
                                          stat_value=stat_value)
1001
 
        self.assertEqual('target', link_or_sha1)
1002
 
        # We need to re-read the link because only now can we cache it
1003
 
        self.assertEqual([('read_link', 'a', ''),
1004
 
                          ('read_link', 'a', ''),
1005
 
                          ('read_link', 'a', ''),
1006
 
                         ], state._log)
1007
 
        self.assertEqual([('l', 'target', 6, False, packed_stat)],
1008
 
                         entry[1])
1009
 
 
1010
 
        # Another call won't re-read the link
1011
 
        self.assertEqual([('read_link', 'a', ''),
1012
 
                          ('read_link', 'a', ''),
1013
 
                          ('read_link', 'a', ''),
1014
 
                         ], state._log)
1015
 
        link_or_sha1 = self.update_entry(state, entry, abspath='a',
1016
 
                                          stat_value=stat_value)
1017
 
        self.assertEqual('target', link_or_sha1)
1018
 
        self.assertEqual([('l', 'target', 6, False, packed_stat)],
1019
 
                         entry[1])
1020
 
 
1021
 
    def do_update_entry(self, state, entry, abspath):
1022
 
        stat_value = os.lstat(abspath)
1023
 
        return self.update_entry(state, entry, abspath, stat_value)
1024
 
 
1025
 
    def test_update_entry_dir(self):
1026
 
        state, entry = self.get_state_with_a()
1027
 
        self.build_tree(['a/'])
1028
 
        self.assertIs(None, self.do_update_entry(state, entry, 'a'))
1029
 
 
1030
 
    def test_update_entry_dir_unchanged(self):
1031
 
        state, entry = self.get_state_with_a()
1032
 
        self.build_tree(['a/'])
1033
 
        state.adjust_time(+20)
1034
 
        self.assertIs(None, self.do_update_entry(state, entry, 'a'))
1035
 
        self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
1036
 
                         state._dirblock_state)
1037
 
        state.save()
1038
 
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1039
 
                         state._dirblock_state)
1040
 
        self.assertIs(None, self.do_update_entry(state, entry, 'a'))
1041
 
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1042
 
                         state._dirblock_state)
1043
 
 
1044
 
    def test_update_entry_file_unchanged(self):
1045
 
        state, _ = self.get_state_with_a()
1046
 
        tree = self.make_branch_and_tree('tree')
1047
 
        tree.lock_write()
1048
 
        self.build_tree(['tree/a'])
1049
 
        tree.add(['a'], ['a-id'])
1050
 
        with_a_id = tree.commit('witha')
1051
 
        self.addCleanup(tree.unlock)
1052
 
        state.set_parent_trees(
1053
 
            [(with_a_id, tree.branch.repository.revision_tree(with_a_id))],
1054
 
            [])
1055
 
        entry = state._get_entry(0, path_utf8='a')
1056
 
        self.build_tree(['a'])
1057
 
        sha1sum = 'b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6'
1058
 
        state.adjust_time(+20)
1059
 
        self.assertEqual(sha1sum, self.do_update_entry(state, entry, 'a'))
1060
 
        self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
1061
 
                         state._dirblock_state)
1062
 
        state.save()
1063
 
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1064
 
                         state._dirblock_state)
1065
 
        self.assertEqual(sha1sum, self.do_update_entry(state, entry, 'a'))
1066
 
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1067
 
                         state._dirblock_state)
1068
 
 
1069
 
    def test_update_entry_tree_reference(self):
1070
 
        state = test_dirstate.InstrumentedDirState.initialize('dirstate')
1071
 
        self.addCleanup(state.unlock)
1072
 
        state.add('r', 'r-id', 'tree-reference', None, '')
1073
 
        self.build_tree(['r/'])
1074
 
        entry = state._get_entry(0, path_utf8='r')
1075
 
        self.do_update_entry(state, entry, 'r')
1076
 
        entry = state._get_entry(0, path_utf8='r')
1077
 
        self.assertEqual('t', entry[1][0][0])
1078
 
 
1079
 
    def create_and_test_file(self, state, entry):
1080
 
        """Create a file at 'a' and verify the state finds it during update.
1081
 
 
1082
 
        The state should already be versioning *something* at 'a'. This makes
1083
 
        sure that state.update_entry recognizes it as a file.
1084
 
        """
1085
 
        self.build_tree(['a'])
1086
 
        stat_value = os.lstat('a')
1087
 
        packed_stat = dirstate.pack_stat(stat_value)
1088
 
 
1089
 
        link_or_sha1 = self.do_update_entry(state, entry, abspath='a')
1090
 
        self.assertEqual(None, link_or_sha1)
1091
 
        self.assertEqual([('f', '', 14, False, dirstate.DirState.NULLSTAT)],
1092
 
                         entry[1])
1093
 
        return packed_stat
1094
 
 
1095
 
    def create_and_test_dir(self, state, entry):
1096
 
        """Create a directory at 'a' and verify the state finds it.
1097
 
 
1098
 
        The state should already be versioning *something* at 'a'. This makes
1099
 
        sure that state.update_entry recognizes it as a directory.
1100
 
        """
1101
 
        self.build_tree(['a/'])
1102
 
        stat_value = os.lstat('a')
1103
 
        packed_stat = dirstate.pack_stat(stat_value)
1104
 
 
1105
 
        link_or_sha1 = self.do_update_entry(state, entry, abspath='a')
1106
 
        self.assertIs(None, link_or_sha1)
1107
 
        self.assertEqual([('d', '', 0, False, packed_stat)], entry[1])
1108
 
 
1109
 
        return packed_stat
1110
 
 
1111
 
    # FIXME: Add unicode version
1112
 
    def create_and_test_symlink(self, state, entry):
1113
 
        """Create a symlink at 'a' and verify the state finds it.
1114
 
 
1115
 
        The state should already be versioning *something* at 'a'. This makes
1116
 
        sure that state.update_entry recognizes it as a symlink.
1117
 
 
1118
 
        This should not be called if this platform does not have symlink
1119
 
        support.
1120
 
        """
1121
 
        # caller should care about skipping test on platforms without symlinks
1122
 
        os.symlink('path/to/foo', 'a')
1123
 
 
1124
 
        stat_value = os.lstat('a')
1125
 
        packed_stat = dirstate.pack_stat(stat_value)
1126
 
 
1127
 
        link_or_sha1 = self.do_update_entry(state, entry, abspath='a')
1128
 
        self.assertEqual('path/to/foo', link_or_sha1)
1129
 
        self.assertEqual([('l', 'path/to/foo', 11, False, packed_stat)],
1130
 
                         entry[1])
1131
 
        return packed_stat
1132
 
 
1133
 
    def test_update_file_to_dir(self):
1134
 
        """If a file changes to a directory we return None for the sha.
1135
 
        We also update the inventory record.
1136
 
        """
1137
 
        state, entry = self.get_state_with_a()
1138
 
        # The file sha1 won't be cached unless the file is old
1139
 
        state.adjust_time(+10)
1140
 
        self.create_and_test_file(state, entry)
1141
 
        os.remove('a')
1142
 
        self.create_and_test_dir(state, entry)
1143
 
 
1144
 
    def test_update_file_to_symlink(self):
1145
 
        """File becomes a symlink"""
1146
 
        self.requireFeature(tests.SymlinkFeature)
1147
 
        state, entry = self.get_state_with_a()
1148
 
        # The file sha1 won't be cached unless the file is old
1149
 
        state.adjust_time(+10)
1150
 
        self.create_and_test_file(state, entry)
1151
 
        os.remove('a')
1152
 
        self.create_and_test_symlink(state, entry)
1153
 
 
1154
 
    def test_update_dir_to_file(self):
1155
 
        """Directory becoming a file updates the entry."""
1156
 
        state, entry = self.get_state_with_a()
1157
 
        # The file sha1 won't be cached unless the file is old
1158
 
        state.adjust_time(+10)
1159
 
        self.create_and_test_dir(state, entry)
1160
 
        os.rmdir('a')
1161
 
        self.create_and_test_file(state, entry)
1162
 
 
1163
 
    def test_update_dir_to_symlink(self):
1164
 
        """Directory becomes a symlink"""
1165
 
        self.requireFeature(tests.SymlinkFeature)
1166
 
        state, entry = self.get_state_with_a()
1167
 
        # The symlink target won't be cached if it isn't old
1168
 
        state.adjust_time(+10)
1169
 
        self.create_and_test_dir(state, entry)
1170
 
        os.rmdir('a')
1171
 
        self.create_and_test_symlink(state, entry)
1172
 
 
1173
 
    def test_update_symlink_to_file(self):
1174
 
        """Symlink becomes a file"""
1175
 
        self.requireFeature(tests.SymlinkFeature)
1176
 
        state, entry = self.get_state_with_a()
1177
 
        # The symlink and file info won't be cached unless old
1178
 
        state.adjust_time(+10)
1179
 
        self.create_and_test_symlink(state, entry)
1180
 
        os.remove('a')
1181
 
        self.create_and_test_file(state, entry)
1182
 
 
1183
 
    def test_update_symlink_to_dir(self):
1184
 
        """Symlink becomes a directory"""
1185
 
        self.requireFeature(tests.SymlinkFeature)
1186
 
        state, entry = self.get_state_with_a()
1187
 
        # The symlink target won't be cached if it isn't old
1188
 
        state.adjust_time(+10)
1189
 
        self.create_and_test_symlink(state, entry)
1190
 
        os.remove('a')
1191
 
        self.create_and_test_dir(state, entry)
1192
 
 
1193
 
    def test__is_executable_win32(self):
1194
 
        state, entry = self.get_state_with_a()
1195
 
        self.build_tree(['a'])
1196
 
 
1197
 
        # Make sure we are using the win32 implementation of _is_executable
1198
 
        state._is_executable = state._is_executable_win32
1199
 
 
1200
 
        # The file on disk is not executable, but we are marking it as though
1201
 
        # it is. With _is_executable_win32 we ignore what is on disk.
1202
 
        entry[1][0] = ('f', '', 0, True, dirstate.DirState.NULLSTAT)
1203
 
 
1204
 
        stat_value = os.lstat('a')
1205
 
        packed_stat = dirstate.pack_stat(stat_value)
1206
 
 
1207
 
        state.adjust_time(-10) # Make sure everything is new
1208
 
        self.update_entry(state, entry, abspath='a', stat_value=stat_value)
1209
 
 
1210
 
        # The row is updated, but the executable bit stays set.
1211
 
        self.assertEqual([('f', '', 14, True, dirstate.DirState.NULLSTAT)],
1212
 
                         entry[1])
1213
 
 
1214
 
        # Make the disk object look old enough to cache (but it won't cache the
1215
 
        # sha as it is a new file).
1216
 
        state.adjust_time(+20)
1217
 
        digest = 'b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6'
1218
 
        self.update_entry(state, entry, abspath='a', stat_value=stat_value)
1219
 
        self.assertEqual([('f', '', 14, True, dirstate.DirState.NULLSTAT)],
1220
 
            entry[1])
1221
 
 
1222
 
    def _prepare_tree(self):
1223
 
        # Create a tree
1224
 
        text = 'Hello World\n'
1225
 
        tree = self.make_branch_and_tree('tree')
1226
 
        self.build_tree_contents([('tree/a file', text)])
1227
 
        tree.add('a file', 'a-file-id')
1228
 
        # Note: dirstate does not sha prior to the first commit
1229
 
        # so commit now in order for the test to work
1230
 
        tree.commit('first')
1231
 
        return tree, text
1232
 
 
1233
 
    def test_sha1provider_sha1_used(self):
1234
 
        tree, text = self._prepare_tree()
1235
 
        state = dirstate.DirState.from_tree(tree, 'dirstate',
1236
 
            UppercaseSHA1Provider())
1237
 
        self.addCleanup(state.unlock)
1238
 
        expected_sha = osutils.sha_string(text.upper() + "foo")
1239
 
        entry = state._get_entry(0, path_utf8='a file')
1240
 
        state._sha_cutoff_time()
1241
 
        state._cutoff_time += 10
1242
 
        sha1 = self.update_entry(state, entry, 'tree/a file',
1243
 
                                 os.lstat('tree/a file'))
1244
 
        self.assertEqual(expected_sha, sha1)
1245
 
 
1246
 
    def test_sha1provider_stat_and_sha1_used(self):
1247
 
        tree, text = self._prepare_tree()
1248
 
        tree.lock_write()
1249
 
        self.addCleanup(tree.unlock)
1250
 
        state = tree._current_dirstate()
1251
 
        state._sha1_provider = UppercaseSHA1Provider()
1252
 
        # If we used the standard provider, it would look like nothing has
1253
 
        # changed
1254
 
        file_ids_changed = [change[0] for change
1255
 
                            in tree.iter_changes(tree.basis_tree())]
1256
 
        self.assertEqual(['a-file-id'], file_ids_changed)
1257
 
 
1258
 
 
1259
 
class UppercaseSHA1Provider(dirstate.SHA1Provider):
1260
 
    """A custom SHA1Provider."""
1261
 
 
1262
 
    def sha1(self, abspath):
1263
 
        return self.stat_and_sha1(abspath)[1]
1264
 
 
1265
 
    def stat_and_sha1(self, abspath):
1266
 
        file_obj = file(abspath, 'rb')
1267
 
        try:
1268
 
            statvalue = os.fstat(file_obj.fileno())
1269
 
            text = ''.join(file_obj.readlines())
1270
 
            sha1 = osutils.sha_string(text.upper() + "foo")
1271
 
        finally:
1272
 
            file_obj.close()
1273
 
        return statvalue, sha1
1274
 
 
1275
 
 
1276
 
class TestProcessEntry(test_dirstate.TestCaseWithDirState):
1277
 
 
1278
 
    # Set by load_tests
1279
 
    _process_entry = None
1280
 
 
1281
 
    def setUp(self):
1282
 
        super(TestProcessEntry, self).setUp()
1283
 
        orig = dirstate._process_entry
1284
 
        def cleanup():
1285
 
            dirstate._process_entry = orig
1286
 
        self.addCleanup(cleanup)
1287
 
        dirstate._process_entry = self._process_entry
1288
 
 
1289
 
    def assertChangedFileIds(self, expected, tree):
1290
 
        tree.lock_read()
1291
 
        try:
1292
 
            file_ids = [info[0] for info
1293
 
                        in tree.iter_changes(tree.basis_tree())]
1294
 
        finally:
1295
 
            tree.unlock()
1296
 
        self.assertEqual(sorted(expected), sorted(file_ids))
1297
 
 
1298
 
    def test_simple_changes(self):
1299
 
        tree = self.make_branch_and_tree('tree')
1300
 
        self.build_tree(['tree/file'])
1301
 
        tree.add(['file'], ['file-id'])
1302
 
        self.assertChangedFileIds([tree.get_root_id(), 'file-id'], tree)
1303
 
        tree.commit('one')
1304
 
        self.assertChangedFileIds([], tree)
1305
 
 
1306
 
    def test_sha1provider_stat_and_sha1_used(self):
1307
 
        tree = self.make_branch_and_tree('tree')
1308
 
        self.build_tree(['tree/file'])
1309
 
        tree.add(['file'], ['file-id'])
1310
 
        tree.commit('one')
1311
 
        tree.lock_write()
1312
 
        self.addCleanup(tree.unlock)
1313
 
        state = tree._current_dirstate()
1314
 
        state._sha1_provider = UppercaseSHA1Provider()
1315
 
        self.assertChangedFileIds(['file-id'], tree)
 
747
            from bzrlib._dirstate_helpers_c import _read_dirblocks_c
 
748
            self.assertIs(_read_dirblocks_c, dirstate._read_dirblocks)
 
749
        else:
 
750
            from bzrlib._dirstate_helpers_py import _read_dirblocks_py
 
751
            self.assertIs(_read_dirblocks_py, dirstate._read_dirblocks)
1316
752