Rev 3705: Refactor to allow a pluggable dirstate update_entry with interface tests. in http://people.ubuntu.com/~robertc/baz2.0/readdir

Robert Collins robertc at robertcollins.net
Sat Sep 13 08:59:12 BST 2008


At http://people.ubuntu.com/~robertc/baz2.0/readdir

------------------------------------------------------------
revno: 3705
revision-id: robertc at robertcollins.net-20080913075905-4q1okaa2yntxxop2
parent: robertc at robertcollins.net-20080913043902-o8cuque1ozwpvk6x
committer: Robert Collins <robertc at robertcollins.net>
branch nick: update_entry
timestamp: Sat 2008-09-13 17:59:05 +1000
message:
  Refactor to allow a pluggable dirstate update_entry with interface tests.
modified:
  bzrlib/_dirstate_helpers_c.pyx dirstate_helpers.pyx-20070503201057-u425eni465q4idwn-3
  bzrlib/dirstate.py             dirstate.py-20060728012006-d6mvoihjb3je9peu-1
  bzrlib/tests/test__dirstate_helpers.py test_dirstate_helper-20070504035751-jsbn00xodv0y1eve-2
  bzrlib/tests/test_dirstate.py  test_dirstate.py-20060728012006-d6mvoihjb3je9peu-2
  bzrlib/workingtree_4.py        workingtree_4.py-20070208044105-5fgpc5j3ljlh5q6c-1
=== modified file 'bzrlib/_dirstate_helpers_c.pyx'
--- a/bzrlib/_dirstate_helpers_c.pyx	2008-09-02 19:15:57 +0000
+++ b/bzrlib/_dirstate_helpers_c.pyx	2008-09-13 07:59:05 +0000
@@ -19,8 +19,8 @@
 This is the python implementation for DirState functions.
 """
 
-from bzrlib import errors
-from bzrlib.dirstate import DirState
+from bzrlib import errors, osutils
+from bzrlib.dirstate import DirState, pack_stat
 
 
 # Give Pyrex some function definitions for it to understand.
@@ -719,3 +719,80 @@
 
     reader._parse_dirblocks()
     state._dirblock_state = DirState.IN_MEMORY_UNMODIFIED
+
+
+_stat_to_minikind = DirState._stat_to_minikind
+
+
+def update_entry(self, entry, abspath, stat_value,
+                 _stat_to_minikind=_stat_to_minikind,
+                 _pack_stat=pack_stat):
+    """Update the entry based on what is actually on disk.
+
+    :param entry: This is the dirblock entry for the file in question.
+    :param abspath: The path on disk for this file.
+    :param stat_value: (optional) if we already have done a stat on the
+        file, re-use it.
+    :return: The sha1 hexdigest of the file (40 bytes) or link target of a
+            symlink.
+    """
+    try:
+        minikind = _stat_to_minikind[stat_value.st_mode & 0170000]
+    except KeyError:
+        # Unhandled kind
+        return None
+    packed_stat = _pack_stat(stat_value)
+    (saved_minikind, saved_link_or_sha1, saved_file_size,
+     saved_executable, saved_packed_stat) = entry[1][0]
+
+    if (minikind == saved_minikind
+        and packed_stat == saved_packed_stat):
+        # The stat hasn't changed since we saved, so we can re-use the
+        # saved sha hash.
+        if minikind == 'd':
+            return None
+
+        # size should also be in packed_stat
+        if saved_file_size == stat_value.st_size:
+            return saved_link_or_sha1
+
+    # If we have gotten this far, that means that we need to actually
+    # process this entry.
+    link_or_sha1 = None
+    if minikind == 'f':
+        link_or_sha1 = self._sha1_file(abspath)
+        executable = self._is_executable(stat_value.st_mode,
+                                         saved_executable)
+        if self._cutoff_time is None:
+            self._sha_cutoff_time()
+        if (stat_value.st_mtime < self._cutoff_time
+            and stat_value.st_ctime < self._cutoff_time):
+            entry[1][0] = ('f', link_or_sha1, stat_value.st_size,
+                           executable, packed_stat)
+        else:
+            entry[1][0] = ('f', '', stat_value.st_size,
+                           executable, DirState.NULLSTAT)
+    elif minikind == 'd':
+        link_or_sha1 = None
+        entry[1][0] = ('d', '', 0, False, packed_stat)
+        if saved_minikind != 'd':
+            # This changed from something into a directory. Make sure we
+            # have a directory block for it. This doesn't happen very
+            # often, so this doesn't have to be super fast.
+            block_index, entry_index, dir_present, file_present = \
+                self._get_block_entry_index(entry[0][0], entry[0][1], 0)
+            self._ensure_block(block_index, entry_index,
+                               osutils.pathjoin(entry[0][0], entry[0][1]))
+    elif minikind == 'l':
+        link_or_sha1 = self._read_link(abspath, saved_link_or_sha1)
+        if self._cutoff_time is None:
+            self._sha_cutoff_time()
+        if (stat_value.st_mtime < self._cutoff_time
+            and stat_value.st_ctime < self._cutoff_time):
+            entry[1][0] = ('l', link_or_sha1, stat_value.st_size,
+                           False, packed_stat)
+        else:
+            entry[1][0] = ('l', '', stat_value.st_size,
+                           False, DirState.NULLSTAT)
+    self._dirblock_state = DirState.IN_MEMORY_MODIFIED
+    return link_or_sha1

=== modified file 'bzrlib/dirstate.py'
--- a/bzrlib/dirstate.py	2008-07-30 08:55:10 +0000
+++ b/bzrlib/dirstate.py	2008-09-13 07:59:05 +0000
@@ -1472,79 +1472,6 @@
                     # it is being resurrected here, so blank it out temporarily.
                     self._dirblocks[block_index][1][entry_index][1][1] = null
 
-    def update_entry(self, entry, abspath, stat_value,
-                     _stat_to_minikind=_stat_to_minikind,
-                     _pack_stat=pack_stat):
-        """Update the entry based on what is actually on disk.
-
-        :param entry: This is the dirblock entry for the file in question.
-        :param abspath: The path on disk for this file.
-        :param stat_value: (optional) if we already have done a stat on the
-            file, re-use it.
-        :return: The sha1 hexdigest of the file (40 bytes) or link target of a
-                symlink.
-        """
-        try:
-            minikind = _stat_to_minikind[stat_value.st_mode & 0170000]
-        except KeyError:
-            # Unhandled kind
-            return None
-        packed_stat = _pack_stat(stat_value)
-        (saved_minikind, saved_link_or_sha1, saved_file_size,
-         saved_executable, saved_packed_stat) = entry[1][0]
-
-        if (minikind == saved_minikind
-            and packed_stat == saved_packed_stat):
-            # The stat hasn't changed since we saved, so we can re-use the
-            # saved sha hash.
-            if minikind == 'd':
-                return None
-
-            # size should also be in packed_stat
-            if saved_file_size == stat_value.st_size:
-                return saved_link_or_sha1
-
-        # If we have gotten this far, that means that we need to actually
-        # process this entry.
-        link_or_sha1 = None
-        if minikind == 'f':
-            link_or_sha1 = self._sha1_file(abspath)
-            executable = self._is_executable(stat_value.st_mode,
-                                             saved_executable)
-            if self._cutoff_time is None:
-                self._sha_cutoff_time()
-            if (stat_value.st_mtime < self._cutoff_time
-                and stat_value.st_ctime < self._cutoff_time):
-                entry[1][0] = ('f', link_or_sha1, stat_value.st_size,
-                               executable, packed_stat)
-            else:
-                entry[1][0] = ('f', '', stat_value.st_size,
-                               executable, DirState.NULLSTAT)
-        elif minikind == 'd':
-            link_or_sha1 = None
-            entry[1][0] = ('d', '', 0, False, packed_stat)
-            if saved_minikind != 'd':
-                # This changed from something into a directory. Make sure we
-                # have a directory block for it. This doesn't happen very
-                # often, so this doesn't have to be super fast.
-                block_index, entry_index, dir_present, file_present = \
-                    self._get_block_entry_index(entry[0][0], entry[0][1], 0)
-                self._ensure_block(block_index, entry_index,
-                                   osutils.pathjoin(entry[0][0], entry[0][1]))
-        elif minikind == 'l':
-            link_or_sha1 = self._read_link(abspath, saved_link_or_sha1)
-            if self._cutoff_time is None:
-                self._sha_cutoff_time()
-            if (stat_value.st_mtime < self._cutoff_time
-                and stat_value.st_ctime < self._cutoff_time):
-                entry[1][0] = ('l', link_or_sha1, stat_value.st_size,
-                               False, packed_stat)
-            else:
-                entry[1][0] = ('l', '', stat_value.st_size,
-                               False, DirState.NULLSTAT)
-        self._dirblock_state = DirState.IN_MEMORY_MODIFIED
-        return link_or_sha1
-
     def _sha_cutoff_time(self):
         """Return cutoff time.
 
@@ -2774,6 +2701,82 @@
             raise errors.ObjectNotLocked(self)
 
 
+def py_update_entry(self, entry, abspath, stat_value,
+                 _stat_to_minikind=DirState._stat_to_minikind,
+                 _pack_stat=pack_stat):
+    """Update the entry based on what is actually on disk.
+
+    :param entry: This is the dirblock entry for the file in question.
+    :param abspath: The path on disk for this file.
+    :param stat_value: (optional) if we already have done a stat on the
+        file, re-use it.
+    :return: The sha1 hexdigest of the file (40 bytes) or link target of a
+            symlink.
+    """
+    try:
+        minikind = _stat_to_minikind[stat_value.st_mode & 0170000]
+    except KeyError:
+        # Unhandled kind
+        return None
+    packed_stat = _pack_stat(stat_value)
+    (saved_minikind, saved_link_or_sha1, saved_file_size,
+     saved_executable, saved_packed_stat) = entry[1][0]
+
+    if (minikind == saved_minikind
+        and packed_stat == saved_packed_stat):
+        # The stat hasn't changed since we saved, so we can re-use the
+        # saved sha hash.
+        if minikind == 'd':
+            return None
+
+        # size should also be in packed_stat
+        if saved_file_size == stat_value.st_size:
+            return saved_link_or_sha1
+
+    # If we have gotten this far, that means that we need to actually
+    # process this entry.
+    link_or_sha1 = None
+    if minikind == 'f':
+        link_or_sha1 = self._sha1_file(abspath)
+        executable = self._is_executable(stat_value.st_mode,
+                                         saved_executable)
+        if self._cutoff_time is None:
+            self._sha_cutoff_time()
+        if (stat_value.st_mtime < self._cutoff_time
+            and stat_value.st_ctime < self._cutoff_time):
+            entry[1][0] = ('f', link_or_sha1, stat_value.st_size,
+                           executable, packed_stat)
+        else:
+            entry[1][0] = ('f', '', stat_value.st_size,
+                           executable, DirState.NULLSTAT)
+    elif minikind == 'd':
+        link_or_sha1 = None
+        entry[1][0] = ('d', '', 0, False, packed_stat)
+        if saved_minikind != 'd':
+            # This changed from something into a directory. Make sure we
+            # have a directory block for it. This doesn't happen very
+            # often, so this doesn't have to be super fast.
+            block_index, entry_index, dir_present, file_present = \
+                self._get_block_entry_index(entry[0][0], entry[0][1], 0)
+            self._ensure_block(block_index, entry_index,
+                               osutils.pathjoin(entry[0][0], entry[0][1]))
+    elif minikind == 'l':
+        link_or_sha1 = self._read_link(abspath, saved_link_or_sha1)
+        if self._cutoff_time is None:
+            self._sha_cutoff_time()
+        if (stat_value.st_mtime < self._cutoff_time
+            and stat_value.st_ctime < self._cutoff_time):
+            entry[1][0] = ('l', link_or_sha1, stat_value.st_size,
+                           False, packed_stat)
+        else:
+            entry[1][0] = ('l', '', stat_value.st_size,
+                           False, DirState.NULLSTAT)
+    self._dirblock_state = DirState.IN_MEMORY_MODIFIED
+    return link_or_sha1
+
+update_entry = py_update_entry
+
+
 # Try to load the compiled form if possible
 try:
     from bzrlib._dirstate_helpers_c import (
@@ -2782,6 +2785,7 @@
         _bisect_path_left_c as _bisect_path_left,
         _bisect_path_right_c as _bisect_path_right,
         cmp_by_dirs_c as cmp_by_dirs,
+        update_entry as update_entry,
         )
 except ImportError:
     from bzrlib._dirstate_helpers_py import (

=== modified file 'bzrlib/tests/test__dirstate_helpers.py'
--- a/bzrlib/tests/test__dirstate_helpers.py	2008-09-02 18:51:03 +0000
+++ b/bzrlib/tests/test__dirstate_helpers.py	2008-09-13 07:59:05 +0000
@@ -24,6 +24,9 @@
     errors,
     tests,
     )
+from bzrlib.tests import (
+        SymlinkFeature,
+        )
 from bzrlib.tests import test_dirstate
 
 
@@ -765,3 +768,344 @@
             from bzrlib._dirstate_helpers_py import _read_dirblocks_py
             self.assertIs(_read_dirblocks_py, dirstate._read_dirblocks)
 
+    def test_update_entry(self):
+        if CompiledDirstateHelpersFeature.available():
+            from bzrlib._dirstate_helpers_c import update_entry
+            self.assertIs(update_entry, dirstate.update_entry)
+        else:
+            from bzrlib.dirstate import py_update_entry
+            self.assertIs(py_update_entry, dirstate.py_update_entry)
+
+
+class TestUpdateEntry(test_dirstate.TestCaseWithDirState):
+    """Test the DirState.update_entry functions"""
+
+    def get_state_with_a(self):
+        """Create a DirState tracking a single object named 'a'"""
+        state = test_dirstate.InstrumentedDirState.initialize('dirstate')
+        self.addCleanup(state.unlock)
+        state.add('a', 'a-id', 'file', None, '')
+        entry = state._get_entry(0, path_utf8='a')
+        self.set_update_entry()
+        return state, entry
+
+    def set_update_entry(self):
+        self.update_entry = dirstate.py_update_entry
+
+    def test_update_entry(self):
+        state, entry = self.get_state_with_a()
+        self.build_tree(['a'])
+        # Add one where we don't provide the stat or sha already
+        self.assertEqual(('', 'a', 'a-id'), entry[0])
+        self.assertEqual([('f', '', 0, False, dirstate.DirState.NULLSTAT)],
+                         entry[1])
+        # Flush the buffers to disk
+        state.save()
+        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
+                         state._dirblock_state)
+
+        stat_value = os.lstat('a')
+        packed_stat = dirstate.pack_stat(stat_value)
+        link_or_sha1 = self.update_entry(state, entry, abspath='a',
+                                          stat_value=stat_value)
+        self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
+                         link_or_sha1)
+
+        # The dirblock entry should not cache the file's sha1
+        self.assertEqual([('f', '', 14, False, dirstate.DirState.NULLSTAT)],
+                         entry[1])
+        self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
+                         state._dirblock_state)
+        mode = stat_value.st_mode
+        self.assertEqual([('sha1', 'a'), ('is_exec', mode, False)], state._log)
+
+        state.save()
+        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
+                         state._dirblock_state)
+
+        # If we do it again right away, we don't know if the file has changed
+        # so we will re-read the file. Roll the clock back so the file is
+        # guaranteed to look too new.
+        state.adjust_time(-10)
+
+        link_or_sha1 = self.update_entry(state, entry, abspath='a',
+                                          stat_value=stat_value)
+        self.assertEqual([('sha1', 'a'), ('is_exec', mode, False),
+                          ('sha1', 'a'), ('is_exec', mode, False),
+                         ], state._log)
+        self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
+                         link_or_sha1)
+        self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
+                         state._dirblock_state)
+        self.assertEqual([('f', '', 14, False, dirstate.DirState.NULLSTAT)],
+                         entry[1])
+        state.save()
+
+        # However, if we move the clock forward so the file is considered
+        # "stable", it should just cache the value.
+        state.adjust_time(+20)
+        link_or_sha1 = self.update_entry(state, entry, abspath='a',
+                                          stat_value=stat_value)
+        self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
+                         link_or_sha1)
+        self.assertEqual([('sha1', 'a'), ('is_exec', mode, False),
+                          ('sha1', 'a'), ('is_exec', mode, False),
+                          ('sha1', 'a'), ('is_exec', mode, False),
+                         ], state._log)
+        self.assertEqual([('f', link_or_sha1, 14, False, packed_stat)],
+                         entry[1])
+
+        # Subsequent calls will just return the cached value
+        link_or_sha1 = self.update_entry(state, entry, abspath='a',
+                                          stat_value=stat_value)
+        self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
+                         link_or_sha1)
+        self.assertEqual([('sha1', 'a'), ('is_exec', mode, False),
+                          ('sha1', 'a'), ('is_exec', mode, False),
+                          ('sha1', 'a'), ('is_exec', mode, False),
+                         ], state._log)
+        self.assertEqual([('f', link_or_sha1, 14, False, packed_stat)],
+                         entry[1])
+
+    def test_update_entry_symlink(self):
+        """Update entry should read symlinks."""
+        self.requireFeature(SymlinkFeature)
+        state, entry = self.get_state_with_a()
+        state.save()
+        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
+                         state._dirblock_state)
+        os.symlink('target', 'a')
+
+        state.adjust_time(-10) # Make the symlink look new
+        stat_value = os.lstat('a')
+        packed_stat = dirstate.pack_stat(stat_value)
+        link_or_sha1 = self.update_entry(state, entry, abspath='a',
+                                          stat_value=stat_value)
+        self.assertEqual('target', link_or_sha1)
+        self.assertEqual([('read_link', 'a', '')], state._log)
+        # Dirblock is not updated (the link is too new)
+        self.assertEqual([('l', '', 6, False, dirstate.DirState.NULLSTAT)],
+                         entry[1])
+        self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
+                         state._dirblock_state)
+
+        # Because the stat_value looks new, we should re-read the target
+        link_or_sha1 = self.update_entry(state, entry, abspath='a',
+                                          stat_value=stat_value)
+        self.assertEqual('target', link_or_sha1)
+        self.assertEqual([('read_link', 'a', ''),
+                          ('read_link', 'a', ''),
+                         ], state._log)
+        self.assertEqual([('l', '', 6, False, dirstate.DirState.NULLSTAT)],
+                         entry[1])
+        state.adjust_time(+20) # Skip into the future, all files look old
+        link_or_sha1 = self.update_entry(state, entry, abspath='a',
+                                          stat_value=stat_value)
+        self.assertEqual('target', link_or_sha1)
+        # We need to re-read the link because only now can we cache it
+        self.assertEqual([('read_link', 'a', ''),
+                          ('read_link', 'a', ''),
+                          ('read_link', 'a', ''),
+                         ], state._log)
+        self.assertEqual([('l', 'target', 6, False, packed_stat)],
+                         entry[1])
+
+        # Another call won't re-read the link
+        self.assertEqual([('read_link', 'a', ''),
+                          ('read_link', 'a', ''),
+                          ('read_link', 'a', ''),
+                         ], state._log)
+        link_or_sha1 = self.update_entry(state, entry, abspath='a',
+                                          stat_value=stat_value)
+        self.assertEqual('target', link_or_sha1)
+        self.assertEqual([('l', 'target', 6, False, packed_stat)],
+                         entry[1])
+
+    def do_update_entry(self, state, entry, abspath):
+        stat_value = os.lstat(abspath)
+        return self.update_entry(state, entry, abspath, stat_value)
+
+    def test_update_entry_dir(self):
+        state, entry = self.get_state_with_a()
+        self.build_tree(['a/'])
+        self.assertIs(None, self.do_update_entry(state, entry, 'a'))
+
+    def test_update_entry_dir_unchanged(self):
+        state, entry = self.get_state_with_a()
+        self.build_tree(['a/'])
+        state.adjust_time(+20)
+        self.assertIs(None, self.do_update_entry(state, entry, 'a'))
+        self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
+                         state._dirblock_state)
+        state.save()
+        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
+                         state._dirblock_state)
+        self.assertIs(None, self.do_update_entry(state, entry, 'a'))
+        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
+                         state._dirblock_state)
+
+    def test_update_entry_file_unchanged(self):
+        state, entry = self.get_state_with_a()
+        self.build_tree(['a'])
+        sha1sum = 'b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6'
+        state.adjust_time(+20)
+        self.assertEqual(sha1sum, self.do_update_entry(state, entry, 'a'))
+        self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
+                         state._dirblock_state)
+        state.save()
+        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
+                         state._dirblock_state)
+        self.assertEqual(sha1sum, self.do_update_entry(state, entry, 'a'))
+        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
+                         state._dirblock_state)
+
+    def create_and_test_file(self, state, entry):
+        """Create a file at 'a' and verify the state finds it.
+
+        The state should already be versioning *something* at 'a'. This makes
+        sure that state.update_entry recognizes it as a file.
+        """
+        self.build_tree(['a'])
+        stat_value = os.lstat('a')
+        packed_stat = dirstate.pack_stat(stat_value)
+
+        link_or_sha1 = self.do_update_entry(state, entry, abspath='a')
+        self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
+                         link_or_sha1)
+        self.assertEqual([('f', link_or_sha1, 14, False, packed_stat)],
+                         entry[1])
+        return packed_stat
+
+    def create_and_test_dir(self, state, entry):
+        """Create a directory at 'a' and verify the state finds it.
+
+        The state should already be versioning *something* at 'a'. This makes
+        sure that state.update_entry recognizes it as a directory.
+        """
+        self.build_tree(['a/'])
+        stat_value = os.lstat('a')
+        packed_stat = dirstate.pack_stat(stat_value)
+
+        link_or_sha1 = self.do_update_entry(state, entry, abspath='a')
+        self.assertIs(None, link_or_sha1)
+        self.assertEqual([('d', '', 0, False, packed_stat)], entry[1])
+
+        return packed_stat
+
+    def create_and_test_symlink(self, state, entry):
+        """Create a symlink at 'a' and verify the state finds it.
+
+        The state should already be versioning *something* at 'a'. This makes
+        sure that state.update_entry recognizes it as a symlink.
+
+        This should not be called if this platform does not have symlink
+        support.
+        """
+        # caller should care about skipping test on platforms without symlinks
+        os.symlink('path/to/foo', 'a')
+
+        stat_value = os.lstat('a')
+        packed_stat = dirstate.pack_stat(stat_value)
+
+        link_or_sha1 = self.do_update_entry(state, entry, abspath='a')
+        self.assertEqual('path/to/foo', link_or_sha1)
+        self.assertEqual([('l', 'path/to/foo', 11, False, packed_stat)],
+                         entry[1])
+        return packed_stat
+
+    def test_update_file_to_dir(self):
+        """If a file changes to a directory we return None for the sha.
+        We also update the inventory record.
+        """
+        state, entry = self.get_state_with_a()
+        # The file sha1 won't be cached unless the file is old
+        state.adjust_time(+10)
+        self.create_and_test_file(state, entry)
+        os.remove('a')
+        self.create_and_test_dir(state, entry)
+
+    def test_update_file_to_symlink(self):
+        """File becomes a symlink"""
+        self.requireFeature(SymlinkFeature)
+        state, entry = self.get_state_with_a()
+        # The file sha1 won't be cached unless the file is old
+        state.adjust_time(+10)
+        self.create_and_test_file(state, entry)
+        os.remove('a')
+        self.create_and_test_symlink(state, entry)
+
+    def test_update_dir_to_file(self):
+        """Directory becoming a file updates the entry."""
+        state, entry = self.get_state_with_a()
+        # The file sha1 won't be cached unless the file is old
+        state.adjust_time(+10)
+        self.create_and_test_dir(state, entry)
+        os.rmdir('a')
+        self.create_and_test_file(state, entry)
+
+    def test_update_dir_to_symlink(self):
+        """Directory becomes a symlink"""
+        self.requireFeature(SymlinkFeature)
+        state, entry = self.get_state_with_a()
+        # The symlink target won't be cached if it isn't old
+        state.adjust_time(+10)
+        self.create_and_test_dir(state, entry)
+        os.rmdir('a')
+        self.create_and_test_symlink(state, entry)
+
+    def test_update_symlink_to_file(self):
+        """Symlink becomes a file"""
+        self.requireFeature(SymlinkFeature)
+        state, entry = self.get_state_with_a()
+        # The symlink and file info won't be cached unless old
+        state.adjust_time(+10)
+        self.create_and_test_symlink(state, entry)
+        os.remove('a')
+        self.create_and_test_file(state, entry)
+
+    def test_update_symlink_to_dir(self):
+        """Symlink becomes a directory"""
+        self.requireFeature(SymlinkFeature)
+        state, entry = self.get_state_with_a()
+        # The symlink target won't be cached if it isn't old
+        state.adjust_time(+10)
+        self.create_and_test_symlink(state, entry)
+        os.remove('a')
+        self.create_and_test_dir(state, entry)
+
+    def test__is_executable_win32(self):
+        state, entry = self.get_state_with_a()
+        self.build_tree(['a'])
+
+        # Make sure we are using the win32 implementation of _is_executable
+        state._is_executable = state._is_executable_win32
+
+        # The file on disk is not executable, but we are marking it as though
+        # it is. With _is_executable_win32 we ignore what is on disk.
+        entry[1][0] = ('f', '', 0, True, dirstate.DirState.NULLSTAT)
+
+        stat_value = os.lstat('a')
+        packed_stat = dirstate.pack_stat(stat_value)
+
+        state.adjust_time(-10) # Make sure everything is new
+        self.update_entry(state, entry, abspath='a', stat_value=stat_value)
+
+        # The row is updated, but the executable bit stays set.
+        self.assertEqual([('f', '', 14, True, dirstate.DirState.NULLSTAT)],
+                         entry[1])
+
+        # Make the disk object look old enough to cache
+        state.adjust_time(+20)
+        digest = 'b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6'
+        self.update_entry(state, entry, abspath='a', stat_value=stat_value)
+        self.assertEqual([('f', digest, 14, True, packed_stat)], entry[1])
+
+
+class TestCompiledUpdateEntry(TestUpdateEntry):
+    """Test the pyrex implementation of _read_dirblocks"""
+
+    _test_needs_features = [CompiledDirstateHelpersFeature]
+
+    def set_update_entry(self):
+        from bzrlib._dirstate_helpers_c import update_entry
+        self.update_entry = update_entry

=== modified file 'bzrlib/tests/test_dirstate.py'
--- a/bzrlib/tests/test_dirstate.py	2008-09-01 14:03:34 +0000
+++ b/bzrlib/tests/test_dirstate.py	2008-09-13 07:59:05 +0000
@@ -1666,326 +1666,6 @@
         self.st_mode = mode
 
 
-class TestUpdateEntry(TestCaseWithDirState):
-    """Test the DirState.update_entry functions"""
-
-    def get_state_with_a(self):
-        """Create a DirState tracking a single object named 'a'"""
-        state = InstrumentedDirState.initialize('dirstate')
-        self.addCleanup(state.unlock)
-        state.add('a', 'a-id', 'file', None, '')
-        entry = state._get_entry(0, path_utf8='a')
-        return state, entry
-
-    def test_update_entry(self):
-        state, entry = self.get_state_with_a()
-        self.build_tree(['a'])
-        # Add one where we don't provide the stat or sha already
-        self.assertEqual(('', 'a', 'a-id'), entry[0])
-        self.assertEqual([('f', '', 0, False, dirstate.DirState.NULLSTAT)],
-                         entry[1])
-        # Flush the buffers to disk
-        state.save()
-        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
-                         state._dirblock_state)
-
-        stat_value = os.lstat('a')
-        packed_stat = dirstate.pack_stat(stat_value)
-        link_or_sha1 = state.update_entry(entry, abspath='a',
-                                          stat_value=stat_value)
-        self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
-                         link_or_sha1)
-
-        # The dirblock entry should not cache the file's sha1
-        self.assertEqual([('f', '', 14, False, dirstate.DirState.NULLSTAT)],
-                         entry[1])
-        self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
-                         state._dirblock_state)
-        mode = stat_value.st_mode
-        self.assertEqual([('sha1', 'a'), ('is_exec', mode, False)], state._log)
-
-        state.save()
-        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
-                         state._dirblock_state)
-
-        # If we do it again right away, we don't know if the file has changed
-        # so we will re-read the file. Roll the clock back so the file is
-        # guaranteed to look too new.
-        state.adjust_time(-10)
-
-        link_or_sha1 = state.update_entry(entry, abspath='a',
-                                          stat_value=stat_value)
-        self.assertEqual([('sha1', 'a'), ('is_exec', mode, False),
-                          ('sha1', 'a'), ('is_exec', mode, False),
-                         ], state._log)
-        self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
-                         link_or_sha1)
-        self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
-                         state._dirblock_state)
-        self.assertEqual([('f', '', 14, False, dirstate.DirState.NULLSTAT)],
-                         entry[1])
-        state.save()
-
-        # However, if we move the clock forward so the file is considered
-        # "stable", it should just cache the value.
-        state.adjust_time(+20)
-        link_or_sha1 = state.update_entry(entry, abspath='a',
-                                          stat_value=stat_value)
-        self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
-                         link_or_sha1)
-        self.assertEqual([('sha1', 'a'), ('is_exec', mode, False),
-                          ('sha1', 'a'), ('is_exec', mode, False),
-                          ('sha1', 'a'), ('is_exec', mode, False),
-                         ], state._log)
-        self.assertEqual([('f', link_or_sha1, 14, False, packed_stat)],
-                         entry[1])
-
-        # Subsequent calls will just return the cached value
-        link_or_sha1 = state.update_entry(entry, abspath='a',
-                                          stat_value=stat_value)
-        self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
-                         link_or_sha1)
-        self.assertEqual([('sha1', 'a'), ('is_exec', mode, False),
-                          ('sha1', 'a'), ('is_exec', mode, False),
-                          ('sha1', 'a'), ('is_exec', mode, False),
-                         ], state._log)
-        self.assertEqual([('f', link_or_sha1, 14, False, packed_stat)],
-                         entry[1])
-
-    def test_update_entry_symlink(self):
-        """Update entry should read symlinks."""
-        self.requireFeature(SymlinkFeature)
-        state, entry = self.get_state_with_a()
-        state.save()
-        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
-                         state._dirblock_state)
-        os.symlink('target', 'a')
-
-        state.adjust_time(-10) # Make the symlink look new
-        stat_value = os.lstat('a')
-        packed_stat = dirstate.pack_stat(stat_value)
-        link_or_sha1 = state.update_entry(entry, abspath='a',
-                                          stat_value=stat_value)
-        self.assertEqual('target', link_or_sha1)
-        self.assertEqual([('read_link', 'a', '')], state._log)
-        # Dirblock is not updated (the link is too new)
-        self.assertEqual([('l', '', 6, False, dirstate.DirState.NULLSTAT)],
-                         entry[1])
-        self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
-                         state._dirblock_state)
-
-        # Because the stat_value looks new, we should re-read the target
-        link_or_sha1 = state.update_entry(entry, abspath='a',
-                                          stat_value=stat_value)
-        self.assertEqual('target', link_or_sha1)
-        self.assertEqual([('read_link', 'a', ''),
-                          ('read_link', 'a', ''),
-                         ], state._log)
-        self.assertEqual([('l', '', 6, False, dirstate.DirState.NULLSTAT)],
-                         entry[1])
-        state.adjust_time(+20) # Skip into the future, all files look old
-        link_or_sha1 = state.update_entry(entry, abspath='a',
-                                          stat_value=stat_value)
-        self.assertEqual('target', link_or_sha1)
-        # We need to re-read the link because only now can we cache it
-        self.assertEqual([('read_link', 'a', ''),
-                          ('read_link', 'a', ''),
-                          ('read_link', 'a', ''),
-                         ], state._log)
-        self.assertEqual([('l', 'target', 6, False, packed_stat)],
-                         entry[1])
-
-        # Another call won't re-read the link
-        self.assertEqual([('read_link', 'a', ''),
-                          ('read_link', 'a', ''),
-                          ('read_link', 'a', ''),
-                         ], state._log)
-        link_or_sha1 = state.update_entry(entry, abspath='a',
-                                          stat_value=stat_value)
-        self.assertEqual('target', link_or_sha1)
-        self.assertEqual([('l', 'target', 6, False, packed_stat)],
-                         entry[1])
-
-    def do_update_entry(self, state, entry, abspath):
-        stat_value = os.lstat(abspath)
-        return state.update_entry(entry, abspath, stat_value)
-
-    def test_update_entry_dir(self):
-        state, entry = self.get_state_with_a()
-        self.build_tree(['a/'])
-        self.assertIs(None, self.do_update_entry(state, entry, 'a'))
-
-    def test_update_entry_dir_unchanged(self):
-        state, entry = self.get_state_with_a()
-        self.build_tree(['a/'])
-        state.adjust_time(+20)
-        self.assertIs(None, self.do_update_entry(state, entry, 'a'))
-        self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
-                         state._dirblock_state)
-        state.save()
-        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
-                         state._dirblock_state)
-        self.assertIs(None, self.do_update_entry(state, entry, 'a'))
-        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
-                         state._dirblock_state)
-
-    def test_update_entry_file_unchanged(self):
-        state, entry = self.get_state_with_a()
-        self.build_tree(['a'])
-        sha1sum = 'b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6'
-        state.adjust_time(+20)
-        self.assertEqual(sha1sum, self.do_update_entry(state, entry, 'a'))
-        self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
-                         state._dirblock_state)
-        state.save()
-        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
-                         state._dirblock_state)
-        self.assertEqual(sha1sum, self.do_update_entry(state, entry, 'a'))
-        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
-                         state._dirblock_state)
-
-    def create_and_test_file(self, state, entry):
-        """Create a file at 'a' and verify the state finds it.
-
-        The state should already be versioning *something* at 'a'. This makes
-        sure that state.update_entry recognizes it as a file.
-        """
-        self.build_tree(['a'])
-        stat_value = os.lstat('a')
-        packed_stat = dirstate.pack_stat(stat_value)
-
-        link_or_sha1 = self.do_update_entry(state, entry, abspath='a')
-        self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
-                         link_or_sha1)
-        self.assertEqual([('f', link_or_sha1, 14, False, packed_stat)],
-                         entry[1])
-        return packed_stat
-
-    def create_and_test_dir(self, state, entry):
-        """Create a directory at 'a' and verify the state finds it.
-
-        The state should already be versioning *something* at 'a'. This makes
-        sure that state.update_entry recognizes it as a directory.
-        """
-        self.build_tree(['a/'])
-        stat_value = os.lstat('a')
-        packed_stat = dirstate.pack_stat(stat_value)
-
-        link_or_sha1 = self.do_update_entry(state, entry, abspath='a')
-        self.assertIs(None, link_or_sha1)
-        self.assertEqual([('d', '', 0, False, packed_stat)], entry[1])
-
-        return packed_stat
-
-    def create_and_test_symlink(self, state, entry):
-        """Create a symlink at 'a' and verify the state finds it.
-
-        The state should already be versioning *something* at 'a'. This makes
-        sure that state.update_entry recognizes it as a symlink.
-
-        This should not be called if this platform does not have symlink
-        support.
-        """
-        # caller should care about skipping test on platforms without symlinks
-        os.symlink('path/to/foo', 'a')
-
-        stat_value = os.lstat('a')
-        packed_stat = dirstate.pack_stat(stat_value)
-
-        link_or_sha1 = self.do_update_entry(state, entry, abspath='a')
-        self.assertEqual('path/to/foo', link_or_sha1)
-        self.assertEqual([('l', 'path/to/foo', 11, False, packed_stat)],
-                         entry[1])
-        return packed_stat
-
-    def test_update_file_to_dir(self):
-        """If a file changes to a directory we return None for the sha.
-        We also update the inventory record.
-        """
-        state, entry = self.get_state_with_a()
-        # The file sha1 won't be cached unless the file is old
-        state.adjust_time(+10)
-        self.create_and_test_file(state, entry)
-        os.remove('a')
-        self.create_and_test_dir(state, entry)
-
-    def test_update_file_to_symlink(self):
-        """File becomes a symlink"""
-        self.requireFeature(SymlinkFeature)
-        state, entry = self.get_state_with_a()
-        # The file sha1 won't be cached unless the file is old
-        state.adjust_time(+10)
-        self.create_and_test_file(state, entry)
-        os.remove('a')
-        self.create_and_test_symlink(state, entry)
-
-    def test_update_dir_to_file(self):
-        """Directory becoming a file updates the entry."""
-        state, entry = self.get_state_with_a()
-        # The file sha1 won't be cached unless the file is old
-        state.adjust_time(+10)
-        self.create_and_test_dir(state, entry)
-        os.rmdir('a')
-        self.create_and_test_file(state, entry)
-
-    def test_update_dir_to_symlink(self):
-        """Directory becomes a symlink"""
-        self.requireFeature(SymlinkFeature)
-        state, entry = self.get_state_with_a()
-        # The symlink target won't be cached if it isn't old
-        state.adjust_time(+10)
-        self.create_and_test_dir(state, entry)
-        os.rmdir('a')
-        self.create_and_test_symlink(state, entry)
-
-    def test_update_symlink_to_file(self):
-        """Symlink becomes a file"""
-        self.requireFeature(SymlinkFeature)
-        state, entry = self.get_state_with_a()
-        # The symlink and file info won't be cached unless old
-        state.adjust_time(+10)
-        self.create_and_test_symlink(state, entry)
-        os.remove('a')
-        self.create_and_test_file(state, entry)
-
-    def test_update_symlink_to_dir(self):
-        """Symlink becomes a directory"""
-        self.requireFeature(SymlinkFeature)
-        state, entry = self.get_state_with_a()
-        # The symlink target won't be cached if it isn't old
-        state.adjust_time(+10)
-        self.create_and_test_symlink(state, entry)
-        os.remove('a')
-        self.create_and_test_dir(state, entry)
-
-    def test__is_executable_win32(self):
-        state, entry = self.get_state_with_a()
-        self.build_tree(['a'])
-
-        # Make sure we are using the win32 implementation of _is_executable
-        state._is_executable = state._is_executable_win32
-
-        # The file on disk is not executable, but we are marking it as though
-        # it is. With _is_executable_win32 we ignore what is on disk.
-        entry[1][0] = ('f', '', 0, True, dirstate.DirState.NULLSTAT)
-
-        stat_value = os.lstat('a')
-        packed_stat = dirstate.pack_stat(stat_value)
-
-        state.adjust_time(-10) # Make sure everything is new
-        state.update_entry(entry, abspath='a', stat_value=stat_value)
-
-        # The row is updated, but the executable bit stays set.
-        self.assertEqual([('f', '', 14, True, dirstate.DirState.NULLSTAT)],
-                         entry[1])
-
-        # Make the disk object look old enough to cache
-        state.adjust_time(+20)
-        digest = 'b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6'
-        state.update_entry(entry, abspath='a', stat_value=stat_value)
-        self.assertEqual([('f', digest, 14, True, packed_stat)], entry[1])
-
-
 class TestPackStat(TestCaseWithTransport):
 
     def assertPackStat(self, expected, stat_value):

=== modified file 'bzrlib/workingtree_4.py'
--- a/bzrlib/workingtree_4.py	2008-09-01 14:03:34 +0000
+++ b/bzrlib/workingtree_4.py	2008-09-13 07:59:05 +0000
@@ -409,8 +409,8 @@
                     return None
                 else:
                     raise
-        link_or_sha1 = state.update_entry(entry, file_abspath,
-                                          stat_value=stat_value)
+        link_or_sha1 = dirstate.update_entry(state, entry, file_abspath,
+            stat_value=stat_value)
         if entry[1][0][0] == 'f':
             return link_or_sha1
         return None
@@ -1856,6 +1856,7 @@
                 "revision {%s} is not stored in {%s}, but %s "
                 "can only be used for trees stored in the dirstate"
                 % (self.source._revision_id, self.target, self.iter_changes))
+        update_entry = dirstate.update_entry
         target_index = 0
         if self.source._revision_id == NULL_REVISION:
             source_index = None
@@ -2009,8 +2010,8 @@
             if path_info is not None and target_minikind in 'fdlt':
                 if not (target_index == 0):
                     raise AssertionError()
-                link_or_sha1 = state.update_entry(entry, abspath=path_info[4],
-                                                  stat_value=path_info[3])
+                link_or_sha1 = update_entry(state, entry,
+                    abspath=path_info[4], stat_value=path_info[3])
                 # The entry may have been modified by update_entry
                 target_details = entry[1][target_index]
                 target_minikind = target_details[0]




More information about the bazaar-commits mailing list