Rev 6017: (spiv) selftest speedup and infrastructure improvement from lp:bzr r6018 and in file:///home/pqm/archives/thelove/bzr/2.4/

Canonical.com Patch Queue Manager pqm at pqm.ubuntu.com
Wed Jul 13 00:48:41 UTC 2011


At file:///home/pqm/archives/thelove/bzr/2.4/

------------------------------------------------------------
revno: 6017 [merge]
revision-id: pqm at pqm.ubuntu.com-20110713004839-rkdceu7bli2c7ihv
parent: pqm at pqm.ubuntu.com-20110711093601-cl620lhsert3l79f
parent: andrew.bennetts at canonical.com-20110712233311-o5j4bvptl3kvw0l6
committer: Canonical.com Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: 2.4
timestamp: Wed 2011-07-13 00:48:39 +0000
message:
  (spiv) selftest speedup and infrastructure improvement from lp:bzr r6018 and
   r6020. (Andrew Bennetts)
modified:
  bzrlib/branchbuilder.py        branchbuilder.py-20070427022007-zlxpqz2lannhk6y8-1
  bzrlib/tests/__init__.py       selftest.py-20050531073622-8d0e3c8845c97a64
  bzrlib/tests/per_repository/test_fetch.py test_fetch.py-20070814052151-5cxha9slx4c93uog-1
  bzrlib/tests/test_branchbuilder.py test_branchbuilder.p-20070427022007-zlxpqz2lannhk6y8-2
  bzrlib/tests/test_merge.py     testmerge.py-20050905070950-c1b5aa49ff911024
  doc/en/release-notes/bzr-2.4.txt bzr2.4.txt-20110114053217-k7ym9jfz243fddjm-1
=== modified file 'bzrlib/branchbuilder.py'
--- a/bzrlib/branchbuilder.py	2010-12-20 11:47:15 +0000
+++ b/bzrlib/branchbuilder.py	2011-07-12 23:32:24 +0000
@@ -166,6 +166,11 @@
         committer=None, timezone=None, message_callback=None):
         """Build a commit, shaped in a specific way.
 
+        Most of the actions are self-explanatory.  'flush' is special action to
+        break a series of actions into discrete steps so that complex changes
+        (such as unversioning a file-id and re-adding it with a different kind)
+        can be expressed in a way that will clearly work.
+
         :param revision_id: The handle for the new commit, can be None
         :param parent_ids: A list of parent_ids to use for the commit.
             It can be None, which indicates to use the last commit.
@@ -174,6 +179,7 @@
             ('modify', ('file-id', 'new-content'))
             ('unversion', 'file-id')
             ('rename', ('orig-path', 'new-path'))
+            ('flush', None)
         :param message: An optional commit message, if not supplied, a default
             commit message will be written.
         :param message_callback: A message callback to use for the commit, as
@@ -208,53 +214,76 @@
             # inventory entry. And the only public function to create a
             # directory is MemoryTree.mkdir() which creates the directory, but
             # also always adds it. So we have to use a multi-pass setup.
-            to_add_directories = []
-            to_add_files = []
-            to_add_file_ids = []
-            to_add_kinds = []
-            new_contents = {}
-            to_unversion_ids = []
-            to_rename = []
+            pending = _PendingActions()
             for action, info in actions:
                 if action == 'add':
                     path, file_id, kind, content = info
                     if kind == 'directory':
-                        to_add_directories.append((path, file_id))
+                        pending.to_add_directories.append((path, file_id))
                     else:
-                        to_add_files.append(path)
-                        to_add_file_ids.append(file_id)
-                        to_add_kinds.append(kind)
+                        pending.to_add_files.append(path)
+                        pending.to_add_file_ids.append(file_id)
+                        pending.to_add_kinds.append(kind)
                         if content is not None:
-                            new_contents[file_id] = content
+                            pending.new_contents[file_id] = content
                 elif action == 'modify':
                     file_id, content = info
-                    new_contents[file_id] = content
+                    pending.new_contents[file_id] = content
                 elif action == 'unversion':
-                    to_unversion_ids.append(info)
+                    pending.to_unversion_ids.add(info)
                 elif action == 'rename':
                     from_relpath, to_relpath = info
-                    to_rename.append((from_relpath, to_relpath))
+                    pending.to_rename.append((from_relpath, to_relpath))
+                elif action == 'flush':
+                    self._flush_pending(tree, pending)
+                    pending = _PendingActions()
                 else:
                     raise ValueError('Unknown build action: "%s"' % (action,))
-            if to_unversion_ids:
-                tree.unversion(to_unversion_ids)
-            for path, file_id in to_add_directories:
-                if path == '':
-                    # Special case, because the path already exists
-                    tree.add([path], [file_id], ['directory'])
-                else:
-                    tree.mkdir(path, file_id)
-            for from_relpath, to_relpath in to_rename:
-                tree.rename_one(from_relpath, to_relpath)
-            tree.add(to_add_files, to_add_file_ids, to_add_kinds)
-            for file_id, content in new_contents.iteritems():
-                tree.put_file_bytes_non_atomic(file_id, content)
+            self._flush_pending(tree, pending)
             return self._do_commit(tree, message=message, rev_id=revision_id,
                 timestamp=timestamp, timezone=timezone, committer=committer,
                 message_callback=message_callback)
         finally:
             tree.unlock()
 
+    def _flush_pending(self, tree, pending):
+        """Flush the pending actions in 'pending', i.e. apply them to 'tree'."""
+        for path, file_id in pending.to_add_directories:
+            if path == '':
+                old_id = tree.path2id(path)
+                if old_id is not None and old_id in pending.to_unversion_ids:
+                    # We're overwriting this path, no need to unversion
+                    pending.to_unversion_ids.discard(old_id)
+                # Special case, because the path already exists
+                tree.add([path], [file_id], ['directory'])
+            else:
+                tree.mkdir(path, file_id)
+        for from_relpath, to_relpath in pending.to_rename:
+            tree.rename_one(from_relpath, to_relpath)
+        if pending.to_unversion_ids:
+            tree.unversion(pending.to_unversion_ids)
+        tree.add(pending.to_add_files, pending.to_add_file_ids, pending.to_add_kinds)
+        for file_id, content in pending.new_contents.iteritems():
+            tree.put_file_bytes_non_atomic(file_id, content)
+
     def get_branch(self):
         """Return the branch created by the builder."""
         return self._branch
+
+
+class _PendingActions(object):
+    """Pending actions for build_snapshot to take.
+
+    This is just a simple class to hold a bunch of the intermediate state of
+    build_snapshot in single object.
+    """
+
+    def __init__(self):
+        self.to_add_directories = []
+        self.to_add_files = []
+        self.to_add_file_ids = []
+        self.to_add_kinds = []
+        self.new_contents = {}
+        self.to_unversion_ids = set()
+        self.to_rename = []
+

=== modified file 'bzrlib/tests/__init__.py'
--- a/bzrlib/tests/__init__.py	2011-06-27 15:42:09 +0000
+++ b/bzrlib/tests/__init__.py	2011-07-12 23:33:11 +0000
@@ -2505,7 +2505,11 @@
         real branch.
         """
         root = TestCaseWithMemoryTransport.TEST_ROOT
-        bzrdir.BzrDir.create_standalone_workingtree(root)
+        wt = bzrdir.BzrDir.create_standalone_workingtree(root)
+        # Hack for speed: remember the raw bytes of the dirstate file so that
+        # we don't need to re-open the wt to check it hasn't changed.
+        TestCaseWithMemoryTransport._SAFETY_NET_PRISTINE_DIRSTATE = (
+            wt.control_transport.get_bytes('dirstate'))
 
     def _check_safety_net(self):
         """Check that the safety .bzr directory have not been touched.
@@ -2514,10 +2518,10 @@
         propagating. This method ensures than a test did not leaked.
         """
         root = TestCaseWithMemoryTransport.TEST_ROOT
-        self.permit_url(_mod_transport.get_transport(root).base)
-        wt = workingtree.WorkingTree.open(root)
-        last_rev = wt.last_revision()
-        if last_rev != 'null:':
+        t = _mod_transport.get_transport(root)
+        self.permit_url(t.base)
+        if (t.get_bytes('.bzr/checkout/dirstate') != 
+                TestCaseWithMemoryTransport._SAFETY_NET_PRISTINE_DIRSTATE):
             # The current test have modified the /bzr directory, we need to
             # recreate a new one or all the followng tests will fail.
             # If you need to inspect its content uncomment the following line

=== modified file 'bzrlib/tests/per_repository/test_fetch.py'
--- a/bzrlib/tests/per_repository/test_fetch.py	2011-05-13 12:51:16 +0000
+++ b/bzrlib/tests/per_repository/test_fetch.py	2011-07-12 23:32:24 +0000
@@ -200,6 +200,7 @@
              ('base', None, []),
              ('tip', None, [('unversion', 'my-root'),
                             ('unversion', ROOT_ID),
+                            ('flush', None),
                             ('add', ('', 'my-root', 'directory', '')),
                             ]),
             ], root_id='my-root')
@@ -228,9 +229,11 @@
             # 'my-root' at root
              ('right', None, [('unversion', 'my-root'),
                               ('unversion', ROOT_ID),
+                              ('flush', None),
                               ('add', ('', 'my-root', 'directory', ''))]),
              ('tip', ['base', 'right'], [('unversion', 'my-root'),
                             ('unversion', ROOT_ID),
+                            ('flush', None),
                             ('add', ('', 'my-root', 'directory', '')),
                             ]),
             ], root_id='my-root')

=== modified file 'bzrlib/tests/test_branchbuilder.py'
--- a/bzrlib/tests/test_branchbuilder.py	2011-04-17 23:06:22 +0000
+++ b/bzrlib/tests/test_branchbuilder.py	2011-07-12 23:32:24 +0000
@@ -247,6 +247,18 @@
                               (u'dir', 'dir-id', 'directory'),
                               (u'dir/a', 'a-id', 'file')], rev_tree)
 
+    def test_rename_out_of_unversioned_subdir(self):
+        builder = self.build_a_rev()
+        builder.build_snapshot('B-id', None,
+            [('add', ('dir', 'dir-id', 'directory', None)),
+             ('rename', ('a', 'dir/a'))])
+        builder.build_snapshot('C-id', None,
+            [('rename', ('dir/a', 'a')),
+             ('unversion', 'dir-id')])
+        rev_tree = builder.get_branch().repository.revision_tree('C-id')
+        self.assertTreeShape([(u'', 'a-root-id', 'directory'),
+                              (u'a', 'a-id', 'file')], rev_tree)
+
     def test_set_parent(self):
         builder = self.build_a_rev()
         builder.start_series()
@@ -364,3 +376,63 @@
         self.addCleanup(b.unlock)
         self.assertEqual(('ghost',),
             b.repository.get_graph().get_parent_map(['tip'])['tip'])
+
+    def test_unversion_root_add_new_root(self):
+        builder = BranchBuilder(self.get_transport().clone('foo'))
+        builder.start_series()
+        builder.build_snapshot('rev-1', None,
+            [('add', ('', 'TREE_ROOT', 'directory', ''))])
+        builder.build_snapshot('rev-2', None,
+            [('unversion', 'TREE_ROOT'),
+             ('add', ('', 'my-root', 'directory', ''))])
+        builder.finish_series()
+        rev_tree = builder.get_branch().repository.revision_tree('rev-2')
+        self.assertTreeShape([(u'', 'my-root', 'directory')], rev_tree)
+
+    def test_empty_flush(self):
+        """A flush with no actions before it is a no-op."""
+        builder = BranchBuilder(self.get_transport().clone('foo'))
+        builder.start_series()
+        builder.build_snapshot('rev-1', None,
+            [('add', ('', 'TREE_ROOT', 'directory', ''))])
+        builder.build_snapshot('rev-2', None, [('flush', None)])
+        builder.finish_series()
+        rev_tree = builder.get_branch().repository.revision_tree('rev-2')
+        self.assertTreeShape([(u'', 'TREE_ROOT', 'directory')], rev_tree)
+
+    def test_kind_change(self):
+        """It's possible to change the kind of an entry in a single snapshot
+        with a bit of help from the 'flush' action.
+        """
+        builder = BranchBuilder(self.get_transport().clone('foo'))
+        builder.start_series()
+        builder.build_snapshot('A-id', None,
+            [('add', (u'', 'a-root-id', 'directory', None)),
+             ('add', (u'a', 'a-id', 'file', 'content\n'))])
+        builder.build_snapshot('B-id', None,
+            [('unversion', 'a-id'),
+             ('flush', None),
+             ('add', (u'a', 'a-id', 'directory', None))])
+        builder.finish_series()
+        rev_tree = builder.get_branch().repository.revision_tree('B-id')
+        self.assertTreeShape(
+            [(u'', 'a-root-id', 'directory'), (u'a', 'a-id', 'directory')],
+            rev_tree)
+
+    def test_pivot_root(self):
+        """It's possible (albeit awkward) to move an existing dir to the root
+        in a single snapshot by using unversion then flush then add.
+        """
+        builder = BranchBuilder(self.get_transport().clone('foo'))
+        builder.start_series()
+        builder.build_snapshot('A-id', None,
+            [('add', (u'', 'orig-root', 'directory', None)),
+             ('add', (u'dir', 'dir-id', 'directory', None))])
+        builder.build_snapshot('B-id', None,
+            [('unversion', 'orig-root'),  # implicitly unversions all children
+             ('flush', None),
+             ('add', (u'', 'dir-id', 'directory', None))])
+        builder.finish_series()
+        rev_tree = builder.get_branch().repository.revision_tree('B-id')
+        self.assertTreeShape([(u'', 'dir-id', 'directory')], rev_tree)
+

=== modified file 'bzrlib/tests/test_merge.py'
--- a/bzrlib/tests/test_merge.py	2011-07-07 10:04:39 +0000
+++ b/bzrlib/tests/test_merge.py	2011-07-12 23:32:24 +0000
@@ -1902,6 +1902,7 @@
         builder.build_snapshot('C-id', ['A-id'], [])
         builder.build_snapshot('E-id', ['C-id', 'B-id'],
             [('unversion', 'a-id'),
+             ('flush', None),
              ('add', (u'a', 'a-id', 'directory', None))])
         builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
         merge_obj = self.make_merge_obj(builder, 'E-id')
@@ -1925,6 +1926,7 @@
         builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
         builder.build_snapshot('D-id', ['B-id', 'C-id'],
             [('unversion', 'a-id'),
+             ('flush', None),
              ('add', (u'a', 'a-id', 'directory', None))])
         merge_obj = self.make_merge_obj(builder, 'E-id')
         entries = list(merge_obj._entries_lca())

=== modified file 'doc/en/release-notes/bzr-2.4.txt'
--- a/doc/en/release-notes/bzr-2.4.txt	2011-07-07 15:06:49 +0000
+++ b/doc/en/release-notes/bzr-2.4.txt	2011-07-12 23:33:11 +0000
@@ -56,6 +56,19 @@
    suite.  This can include new facilities for writing tests, fixes to 
    spurious test failures and changes to the way things should be tested.
 
+* `BranchBuilder.build_snapshot` now supports a "flush" action.  This
+  cleanly and reliably allows tests using `BranchBuilder` to construct
+  branches that e.g. rename files out of a directory and unversion that
+  directory in the same revision.  Previously some changes were impossible
+  due to the order that `build_snapshot` performs its actions.
+  (Andrew Bennetts)
+
+* `TestCaseWithMemoryTransport` is faster now: `_check_safety_net` now
+  just compares the bytes in the dirstate file to its pristine state,
+  rather than opening the WorkingTree and calling ``last_revision()``.
+  This reduces the overall test suite time by about 10% on my laptop.
+  (Andrew Bennetts)
+
 
 bzr 2.4b5
 #########




More information about the bazaar-commits mailing list