Rev 4716: (jam) Merge the 2.0 stable series to bzr.dev, in file:///home/pqm/archives/thelove/bzr/%2Btrunk/

Canonical.com Patch Queue Manager pqm at pqm.ubuntu.com
Fri Sep 25 23:33:39 BST 2009


At file:///home/pqm/archives/thelove/bzr/%2Btrunk/

------------------------------------------------------------
revno: 4716 [merge]
revision-id: pqm at pqm.ubuntu.com-20090925223336-1thhoyfhq5hpr6bh
parent: pqm at pqm.ubuntu.com-20090924094523-nsz6mp3qwor3xpp3
parent: john at arbash-meinel.com-20090925212421-2xhep7gf3e4717op
committer: Canonical.com Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Fri 2009-09-25 23:33:36 +0100
message:
  (jam) Merge the 2.0 stable series to bzr.dev,
  	including 2.0.0 and CHKInventory.filter()
modified:
  NEWS                           NEWS-20050323055033-4e00b5db738777ff
  bzrlib/inventory.py            inventory.py-20050309040759-6648b84ca2005b37
  bzrlib/tests/per_inventory/__init__.py __init__.py-20070821044532-olbadbokgv3qv1yd-1
  bzrlib/tests/per_inventory/basics.py basics.py-20070903044446-kdjwbiu1p1zi9phs-1
  bzrlib/tests/test_inv.py       testinv.py-20050722220913-1dc326138d1a5892
=== modified file 'NEWS'
--- a/NEWS	2009-09-24 09:45:23 +0000
+++ b/NEWS	2009-09-25 21:24:21 +0000
@@ -183,13 +183,38 @@
 Bug Fixes
 *********
 
+* Improve the time for ``bzr log DIR`` for 2a format repositories.
+  We had been using the same code path as for <2a formats, which required
+  iterating over all objects in all revisions.
+  (John Arbash Meinel, #374730)
+
 * Make sure that we unlock the tree if we fail to create a TreeTransform
   object when doing a merge, and there is limbo, or pending-deletions
   directory.  (Gary van der Merwe, #427773)
 
 
-bzr 2.0.0 (Not Released Yet)
-############################
+bzr 2.0.0
+#########
+
+:2.0.0: 2009-09-22
+:Codename: Instant Karma
+
+This release of Bazaar makes the 2a (previously 'brisbane-core') format
+the default when new branches or repositories are created.  This format is
+substantially smaller and faster for many operations.  Most of the work in
+this release focuses on bug fixes and stabilization, covering both 2a and
+previous formats.  (See the Upgrade Guide for information on migrating
+existing projects.)
+
+This release also improves the documentation content and presentation,
+including adding Windows HtmlHelp manuals.
+
+The Bazaar team decided that 2.0 will be a long-term supported release,
+with bugfix-only 2.0.x releases based on it, continuing for at least six
+months or until the following stable release.
+
+Changes from 2.0.0rc2 to final
+******************************
 
 * Officially branded as 2.0.0 rather than 2.0 to clarify between things
   that "want to happen on the 2.0.x stable series" versus things that want
@@ -297,14 +322,6 @@
 :Codename: no worries
 :2.0.0rc1: 2009-08-26
 
-This release of Bazaar makes 2a 'brisbane-core' format the
-default.  Most of the work in this release now focuses on bug
-fixes and stabilization, covering both 2a and previous formats.
-
-The Bazaar team decided that 2.0 will be a long-term supported
-release, with bugfix-only releases based on it, continuing for at
-least six months or until the following stable release.
-
 Compatibility Breaks
 ********************
 

=== modified file 'bzrlib/inventory.py'
--- a/bzrlib/inventory.py	2009-08-30 23:51:10 +0000
+++ b/bzrlib/inventory.py	2009-09-25 21:24:21 +0000
@@ -1197,6 +1197,14 @@
             raise errors.InconsistentDelta("<deleted>", parent_id,
                 "The file id was deleted but its children were not deleted.")
 
+    def create_by_apply_delta(self, inventory_delta, new_revision_id,
+                              propagate_caches=False):
+        """See CHKInventory.create_by_apply_delta()"""
+        new_inv = self.copy()
+        new_inv.apply_delta(inventory_delta)
+        new_inv.revision_id = new_revision_id
+        return new_inv
+
     def _set_root(self, ie):
         self.root = ie
         self._byid = {self.root.file_id: self.root}
@@ -1546,6 +1554,123 @@
         else:
             raise ValueError("unknown kind %r" % entry.kind)
 
+    def _expand_fileids_to_parents_and_children(self, file_ids):
+        """Give a more wholistic view starting with the given file_ids.
+
+        For any file_id which maps to a directory, we will include all children
+        of that directory. We will also include all directories which are
+        parents of the given file_ids, but we will not include their children.
+
+        eg:
+          /     # TREE_ROOT
+          foo/  # foo-id
+            baz # baz-id
+            frob/ # frob-id
+              fringle # fringle-id
+          bar/  # bar-id
+            bing # bing-id
+
+        if given [foo-id] we will include
+            TREE_ROOT as interesting parents
+        and 
+            foo-id, baz-id, frob-id, fringle-id
+        As interesting ids.
+        """
+        interesting = set()
+        # TODO: Pre-pass over the list of fileids to see if anything is already
+        #       deserialized in self._fileid_to_entry_cache
+
+        directories_to_expand = set()
+        children_of_parent_id = {}
+        # It is okay if some of the fileids are missing
+        for entry in self._getitems(file_ids):
+            if entry.kind == 'directory':
+                directories_to_expand.add(entry.file_id)
+            interesting.add(entry.parent_id)
+            children_of_parent_id.setdefault(entry.parent_id, []
+                                             ).append(entry.file_id)
+
+        # Now, interesting has all of the direct parents, but not the
+        # parents of those parents. It also may have some duplicates with
+        # specific_fileids
+        remaining_parents = interesting.difference(file_ids)
+        # When we hit the TREE_ROOT, we'll get an interesting parent of None,
+        # but we don't actually want to recurse into that
+        interesting.add(None) # this will auto-filter it in the loop
+        remaining_parents.discard(None) 
+        while remaining_parents:
+            if None in remaining_parents:
+                import pdb; pdb.set_trace()
+            next_parents = set()
+            for entry in self._getitems(remaining_parents):
+                next_parents.add(entry.parent_id)
+                children_of_parent_id.setdefault(entry.parent_id, []
+                                                 ).append(entry.file_id)
+            # Remove any search tips we've already processed
+            remaining_parents = next_parents.difference(interesting)
+            interesting.update(remaining_parents)
+            # We should probably also .difference(directories_to_expand)
+        interesting.update(file_ids)
+        interesting.discard(None)
+        while directories_to_expand:
+            # Expand directories by looking in the
+            # parent_id_basename_to_file_id map
+            keys = [(f,) for f in directories_to_expand]
+            directories_to_expand = set()
+            items = self.parent_id_basename_to_file_id.iteritems(keys)
+            next_file_ids = set([item[1] for item in items])
+            next_file_ids = next_file_ids.difference(interesting)
+            interesting.update(next_file_ids)
+            for entry in self._getitems(next_file_ids):
+                if entry.kind == 'directory':
+                    directories_to_expand.add(entry.file_id)
+                children_of_parent_id.setdefault(entry.parent_id, []
+                                                 ).append(entry.file_id)
+        return interesting, children_of_parent_id
+
+    def filter(self, specific_fileids):
+        """Get an inventory view filtered against a set of file-ids.
+
+        Children of directories and parents are included.
+
+        The result may or may not reference the underlying inventory
+        so it should be treated as immutable.
+        """
+        (interesting,
+         parent_to_children) = self._expand_fileids_to_parents_and_children(
+                                specific_fileids)
+        # There is some overlap here, but we assume that all interesting items
+        # are in the _fileid_to_entry_cache because we had to read them to
+        # determine if they were a dir we wanted to recurse, or just a file
+        # This should give us all the entries we'll want to add, so start
+        # adding
+        other = Inventory(self.root_id)
+        other.root.revision = self.root.revision
+        other.revision_id = self.revision_id
+        if not interesting or not parent_to_children:
+            # empty filter, or filtering entrys that don't exist
+            # (if even 1 existed, then we would have populated
+            # parent_to_children with at least the tree root.)
+            return other
+        cache = self._fileid_to_entry_cache
+        try:
+            remaining_children = collections.deque(parent_to_children[self.root_id])
+        except:
+            import pdb; pdb.set_trace()
+            raise
+        while remaining_children:
+            file_id = remaining_children.popleft()
+            ie = cache[file_id]
+            if ie.kind == 'directory':
+                ie = ie.copy() # We create a copy to depopulate the .children attribute
+            # TODO: depending on the uses of 'other' we should probably alwyas
+            #       '.copy()' to prevent someone from mutating other and
+            #       invaliding our internal cache
+            other.add(ie)
+            if file_id in parent_to_children:
+                remaining_children.extend(parent_to_children[file_id])
+        return other
+
     @staticmethod
     def _bytes_to_utf8name_key(bytes):
         """Get the file_id, revision_id key out of bytes."""
@@ -1885,6 +2010,27 @@
             # really we're passing an inventory, not a tree...
             raise errors.NoSuchId(self, file_id)
 
+    def _getitems(self, file_ids):
+        """Similar to __getitem__, but lets you query for multiple.
+        
+        The returned order is undefined. And currently if an item doesn't
+        exist, it isn't included in the output.
+        """
+        result = []
+        remaining = []
+        for file_id in file_ids:
+            entry = self._fileid_to_entry_cache.get(file_id, None)
+            if entry is None:
+                remaining.append(file_id)
+            else:
+                result.append(entry)
+        file_keys = [(f,) for f in remaining]
+        for file_key, value in self.id_to_entry.iteritems(file_keys):
+            entry = self._bytes_to_entry(value)
+            result.append(entry)
+            self._fileid_to_entry_cache[entry.file_id] = entry
+        return result
+
     def has_id(self, file_id):
         # Perhaps have an explicit 'contains' method on CHKMap ?
         if self._fileid_to_entry_cache.get(file_id, None) is not None:

=== modified file 'bzrlib/tests/per_inventory/__init__.py'
--- a/bzrlib/tests/per_inventory/__init__.py	2009-07-10 07:14:02 +0000
+++ b/bzrlib/tests/per_inventory/__init__.py	2009-09-24 18:51:41 +0000
@@ -16,15 +16,52 @@
 
 """Tests for different inventory implementations"""
 
-from bzrlib.tests import multiply_tests
+from bzrlib import (
+    groupcompress,
+    tests,
+    )
 
 def load_tests(basic_tests, module, loader):
     """Generate suite containing all parameterized tests"""
     modules_to_test = [
         'bzrlib.tests.per_inventory.basics',
         ]
-    from bzrlib.inventory import Inventory
-    scenarios = [('Inventory', {'inventory_class':Inventory})]
+    from bzrlib.inventory import Inventory, CHKInventory
+
+    def inv_to_chk_inv(test, inv):
+        """CHKInventory needs a backing VF, so we create one."""
+        factory = groupcompress.make_pack_factory(True, True, 1)
+        trans = test.get_transport('chk-inv')
+        trans.ensure_base()
+        vf = factory(trans)
+        # We intentionally use a non-standard maximum_size, so that we are more
+        # likely to trigger splits, and get increased test coverage.
+        chk_inv = CHKInventory.from_inventory(vf, inv,
+                        maximum_size=100,
+                        search_key_name='hash-255-way')
+        return chk_inv
+    scenarios = [('Inventory', {'_inventory_class': Inventory,
+                                '_inv_to_test_inv': lambda test, inv: inv
+                               }),
+                 ('CHKInventory', {'_inventory_class': CHKInventory,
+                                   '_inv_to_test_inv': inv_to_chk_inv,
+                                  })]
     # add the tests for the sub modules
-    return multiply_tests(loader.loadTestsFromModuleNames(modules_to_test),
+    return tests.multiply_tests(
+        loader.loadTestsFromModuleNames(modules_to_test),
         scenarios, basic_tests)
+
+
+class TestCaseWithInventory(tests.TestCaseWithMemoryTransport):
+
+    _inventory_class = None # set by load_tests
+    _inv_to_test_inv = None # set by load_tests
+
+    def make_test_inventory(self):
+        """Return an instance of the Inventory class under test."""
+        return self._inventory_class()
+
+    def inv_to_test_inv(self, inv):
+        """Convert a regular Inventory object into an inventory under test."""
+        return self._inv_to_test_inv(self, inv)
+

=== modified file 'bzrlib/tests/per_inventory/basics.py'
--- a/bzrlib/tests/per_inventory/basics.py	2009-07-17 00:49:02 +0000
+++ b/bzrlib/tests/per_inventory/basics.py	2009-09-24 20:09:36 +0000
@@ -21,6 +21,8 @@
 
 from bzrlib import (
         errors,
+        inventory,
+        osutils,
         )
 
 from bzrlib.inventory import (
@@ -28,22 +30,36 @@
         InventoryEntry,
         InventoryFile,
         InventoryLink,
-        ROOT_ID,
         TreeReference,
         )
 
-from bzrlib.tests import (
-        TestCase,
-        )
-
-
-class TestInventory(TestCase):
-
-    def make_inventory(self, root_id):
-        return self.inventory_class(root_id=root_id)
+from bzrlib.tests.per_inventory import TestCaseWithInventory
+
+
+
+class TestInventory(TestCaseWithInventory):
+
+    def make_init_inventory(self):
+        inv = inventory.Inventory('tree-root')
+        inv.revision = 'initial-rev'
+        inv.root.revision = 'initial-rev'
+        return self.inv_to_test_inv(inv)
+
+    def make_file(self, file_id, name, parent_id, content='content\n',
+                  revision='new-test-rev'):
+        ie = InventoryFile(file_id, name, parent_id)
+        ie.text_sha1 = osutils.sha_string(content)
+        ie.text_size = len(content)
+        ie.revision = revision
+        return ie
+
+    def make_link(self, file_id, name, parent_id, target='link-target\n'):
+        ie = InventoryLink(file_id, name, parent_id)
+        ie.symlink_target = target
+        return ie
 
     def prepare_inv_with_nested_dirs(self):
-        inv = self.make_inventory('tree-root')
+        inv = inventory.Inventory('tree-root')
         for args in [('src', 'directory', 'src-id'),
                      ('doc', 'directory', 'doc-id'),
                      ('src/hello.c', 'file', 'hello-id'),
@@ -53,172 +69,98 @@
                      ('src/zz.c', 'file', 'zzc-id'),
                      ('src/sub/a', 'file', 'a-id'),
                      ('Makefile', 'file', 'makefile-id')]:
-            inv.add_path(*args)
-        return inv
-
-
-class TestInventoryUpdates(TestInventory):
-
-    def test_creation_from_root_id(self):
-        # iff a root id is passed to the constructor, a root directory is made
-        inv = self.make_inventory(root_id='tree-root')
-        self.assertNotEqual(None, inv.root)
-        self.assertEqual('tree-root', inv.root.file_id)
-
-    def test_add_path_of_root(self):
-        # if no root id is given at creation time, there is no root directory
-        inv = self.make_inventory(root_id=None)
-        self.assertIs(None, inv.root)
-        # add a root entry by adding its path
-        ie = inv.add_path("", "directory", "my-root")
-        self.assertEqual("my-root", ie.file_id)
-        self.assertIs(ie, inv.root)
-
-    def test_add_path(self):
-        inv = self.make_inventory(root_id='tree_root')
-        ie = inv.add_path('hello', 'file', 'hello-id')
-        self.assertEqual('hello-id', ie.file_id)
-        self.assertEqual('file', ie.kind)
-
-    def test_copy(self):
-        """Make sure copy() works and creates a deep copy."""
-        inv = self.make_inventory(root_id='some-tree-root')
-        ie = inv.add_path('hello', 'file', 'hello-id')
-        inv2 = inv.copy()
-        inv.root.file_id = 'some-new-root'
-        ie.name = 'file2'
-        self.assertEqual('some-tree-root', inv2.root.file_id)
-        self.assertEqual('hello', inv2['hello-id'].name)
-
-    def test_copy_empty(self):
-        """Make sure an empty inventory can be copied."""
-        inv = self.make_inventory(root_id=None)
-        inv2 = inv.copy()
-        self.assertIs(None, inv2.root)
-
-    def test_copy_copies_root_revision(self):
-        """Make sure the revision of the root gets copied."""
-        inv = self.make_inventory(root_id='someroot')
-        inv.root.revision = 'therev'
-        inv2 = inv.copy()
-        self.assertEquals('someroot', inv2.root.file_id)
-        self.assertEquals('therev', inv2.root.revision)
-
-    def test_create_tree_reference(self):
-        inv = self.make_inventory('tree-root-123')
-        inv.add(TreeReference('nested-id', 'nested', parent_id='tree-root-123',
-                              revision='rev', reference_revision='rev2'))
-
-    def test_error_encoding(self):
-        inv = self.make_inventory('tree-root')
-        inv.add(InventoryFile('a-id', u'\u1234', 'tree-root'))
-        e = self.assertRaises(errors.InconsistentDelta, inv.add,
-            InventoryFile('b-id', u'\u1234', 'tree-root'))
-        self.assertContainsRe(str(e), r'\\u1234')
-
-    def test_add_recursive(self):
-        parent = InventoryDirectory('src-id', 'src', 'tree-root')
-        child = InventoryFile('hello-id', 'hello.c', 'src-id')
-        parent.children[child.file_id] = child
-        inv = self.make_inventory('tree-root')
-        inv.add(parent)
-        self.assertEqual('src/hello.c', inv.id2path('hello-id'))
-
-
-class TestInventoryApplyDelta(TestInventory):
+            ie = inv.add_path(*args)
+            if args[1] == 'file':
+                ie.text_sha1 = osutils.sha_string('content\n')
+                ie.text_size = len('content\n')
+        return self.inv_to_test_inv(inv)
+
+
+class TestInventoryCreateByApplyDelta(TestInventory):
     """A subset of the inventory delta application tests.
 
     See test_inv which has comprehensive delta application tests for
-    inventories, dirstate, and repository based inventories, unlike the tests
-    here which only test in-memory implementations that can support a plain
-    'apply_delta'.
+    inventories, dirstate, and repository based inventories.
     """
-
-    def test_apply_delta_add(self):
-        inv = self.make_inventory('tree-root')
-        inv.apply_delta([
-            (None, "a", "a-id", InventoryFile('a-id', 'a', 'tree-root')),
-            ])
-        self.assertEqual('a', inv.id2path('a-id'))
-
-    def test_apply_delta_delete(self):
-        inv = self.make_inventory('tree-root')
-        inv.apply_delta([
-            (None, "a", "a-id", InventoryFile('a-id', 'a', 'tree-root')),
-            ])
-        self.assertEqual('a', inv.id2path('a-id'))
-        a_ie = inv['a-id']
-        inv.apply_delta([("a", None, "a-id", None)])
+    def test_add(self):
+        inv = self.make_init_inventory()
+        inv = inv.create_by_apply_delta([
+            (None, "a", "a-id", self.make_file('a-id', 'a', 'tree-root')),
+            ], 'new-test-rev')
+        self.assertEqual('a', inv.id2path('a-id'))
+
+    def test_delete(self):
+        inv = self.make_init_inventory()
+        inv = inv.create_by_apply_delta([
+            (None, "a", "a-id", self.make_file('a-id', 'a', 'tree-root')),
+            ], 'new-rev-1')
+        self.assertEqual('a', inv.id2path('a-id'))
+        inv = inv.create_by_apply_delta([
+            ("a", None, "a-id", None),
+            ], 'new-rev-2')
         self.assertRaises(errors.NoSuchId, inv.id2path, 'a-id')
 
-    def test_apply_delta_rename(self):
-        inv = self.make_inventory('tree-root')
-        inv.apply_delta([
-            (None, "a", "a-id", InventoryFile('a-id', 'a', 'tree-root')),
-            ])
+    def test_rename(self):
+        inv = self.make_init_inventory()
+        inv = inv.create_by_apply_delta([
+            (None, "a", "a-id", self.make_file('a-id', 'a', 'tree-root')),
+            ], 'new-rev-1')
         self.assertEqual('a', inv.id2path('a-id'))
         a_ie = inv['a-id']
-        b_ie = InventoryFile(a_ie.file_id, "b", a_ie.parent_id)
-        inv.apply_delta([("a", "b", "a-id", b_ie)])
+        b_ie = self.make_file(a_ie.file_id, "b", a_ie.parent_id)
+        inv = inv.create_by_apply_delta([("a", "b", "a-id", b_ie)], 'new-rev-2')
         self.assertEqual("b", inv.id2path('a-id'))
 
-    def test_apply_delta_illegal(self):
+    def test_illegal(self):
         # A file-id cannot appear in a delta more than once
-        inv = self.make_inventory('tree-root')
-        self.assertRaises(errors.InconsistentDelta, inv.apply_delta, [
-            ("a", "a", "id-1", InventoryFile('id-1', 'a', 'tree-root')),
-            ("a", "b", "id-1", InventoryFile('id-1', 'b', 'tree-root')),
-            ])
+        inv = self.make_init_inventory()
+        self.assertRaises(errors.InconsistentDelta, inv.create_by_apply_delta, [
+            (None, "a", "id-1", self.make_file('id-1', 'a', 'tree-root')),
+            (None, "b", "id-1", self.make_file('id-1', 'b', 'tree-root')),
+            ], 'new-rev-1')
 
 
 class TestInventoryReads(TestInventory):
 
     def test_is_root(self):
         """Ensure our root-checking code is accurate."""
-        inv = self.make_inventory('TREE_ROOT')
-        self.assertTrue(inv.is_root('TREE_ROOT'))
+        inv = self.make_init_inventory()
+        self.assertTrue(inv.is_root('tree-root'))
         self.assertFalse(inv.is_root('booga'))
-        inv.root.file_id = 'booga'
+        ie = inv['tree-root'].copy()
+        ie.file_id = 'booga'
+        inv = inv.create_by_apply_delta([("", None, "tree-root", None),
+                                         (None, "", "booga", ie)], 'new-rev-2')
         self.assertFalse(inv.is_root('TREE_ROOT'))
         self.assertTrue(inv.is_root('booga'))
-        # works properly even if no root is set
-        inv.root = None
-        self.assertFalse(inv.is_root('TREE_ROOT'))
-        self.assertFalse(inv.is_root('booga'))
 
     def test_ids(self):
         """Test detection of files within selected directories."""
-        inv = self.make_inventory(ROOT_ID)
+        inv = inventory.Inventory('TREE_ROOT')
         for args in [('src', 'directory', 'src-id'),
                      ('doc', 'directory', 'doc-id'),
                      ('src/hello.c', 'file'),
                      ('src/bye.c', 'file', 'bye-id'),
                      ('Makefile', 'file')]:
-            inv.add_path(*args)
+            ie = inv.add_path(*args)
+            if args[1] == 'file':
+                ie.text_sha1 = osutils.sha_string('content\n')
+                ie.text_size = len('content\n')
+        inv = self.inv_to_test_inv(inv)
         self.assertEqual(inv.path2id('src'), 'src-id')
         self.assertEqual(inv.path2id('src/bye.c'), 'bye-id')
-        self.assert_('src-id' in inv)
+        self.assertTrue('src-id' in inv)
 
     def test_non_directory_children(self):
         """Test path2id when a parent directory has no children"""
-        inv = self.make_inventory('tree_root')
-        inv.add(InventoryFile('file-id','file',
-                                        parent_id='tree_root'))
-        inv.add(InventoryLink('link-id','link',
-                                        parent_id='tree_root'))
+        inv = inventory.Inventory('tree-root')
+        inv.add(self.make_file('file-id','file', 'tree-root'))
+        inv.add(self.make_link('link-id','link', 'tree-root'))
         self.assertIs(None, inv.path2id('file/subfile'))
         self.assertIs(None, inv.path2id('link/subfile'))
 
     def test_iter_entries(self):
-        inv = self.make_inventory('tree-root')
-        for args in [('src', 'directory', 'src-id'),
-                     ('doc', 'directory', 'doc-id'),
-                     ('src/hello.c', 'file', 'hello-id'),
-                     ('src/bye.c', 'file', 'bye-id'),
-                     ('src/sub', 'directory', 'sub-id'),
-                     ('src/sub/a', 'file', 'a-id'),
-                     ('Makefile', 'file', 'makefile-id')]:
-            inv.add_path(*args)
+        inv = self.prepare_inv_with_nested_dirs()
 
         # Test all entries
         self.assertEqual([
@@ -230,6 +172,8 @@
             ('src/hello.c', 'hello-id'),
             ('src/sub', 'sub-id'),
             ('src/sub/a', 'a-id'),
+            ('src/zz.c', 'zzc-id'),
+            ('zz', 'zz-id'),
             ], [(path, ie.file_id) for path, ie in inv.iter_entries()])
 
         # Test a subdirectory
@@ -238,6 +182,7 @@
             ('hello.c', 'hello-id'),
             ('sub', 'sub-id'),
             ('sub/a', 'a-id'),
+            ('zz.c', 'zzc-id'),
             ], [(path, ie.file_id) for path, ie in inv.iter_entries(
             from_dir='src-id')])
 
@@ -247,6 +192,7 @@
             ('Makefile', 'makefile-id'),
             ('doc', 'doc-id'),
             ('src', 'src-id'),
+            ('zz', 'zz-id'),
             ], [(path, ie.file_id) for path, ie in inv.iter_entries(
             recursive=False)])
 
@@ -255,24 +201,23 @@
             ('bye.c', 'bye-id'),
             ('hello.c', 'hello-id'),
             ('sub', 'sub-id'),
+            ('zz.c', 'zzc-id'),
             ], [(path, ie.file_id) for path, ie in inv.iter_entries(
             from_dir='src-id', recursive=False)])
 
     def test_iter_just_entries(self):
-        inv = self.make_inventory('tree-root')
-        for args in [('src', 'directory', 'src-id'),
-                     ('doc', 'directory', 'doc-id'),
-                     ('src/hello.c', 'file', 'hello-id'),
-                     ('src/bye.c', 'file', 'bye-id'),
-                     ('Makefile', 'file', 'makefile-id')]:
-            inv.add_path(*args)
+        inv = self.prepare_inv_with_nested_dirs()
         self.assertEqual([
+            'a-id',
             'bye-id',
             'doc-id',
             'hello-id',
             'makefile-id',
             'src-id',
+            'sub-id',
             'tree-root',
+            'zz-id',
+            'zzc-id',
             ], sorted([ie.file_id for ie in inv.iter_just_entries()]))
 
     def test_iter_entries_by_dir(self):
@@ -387,3 +332,10 @@
             ('src/sub/a', 'a-id'),
             ('src/zz.c', 'zzc-id'),
             ], [(path, ie.file_id) for path, ie in new_inv.iter_entries()])
+
+    def test_inv_filter_entry_not_present(self):
+        inv = self.prepare_inv_with_nested_dirs()
+        new_inv = inv.filter(['not-present-id'])
+        self.assertEqual([
+            ('', 'tree-root'),
+            ], [(path, ie.file_id) for path, ie in new_inv.iter_entries()])

=== modified file 'bzrlib/tests/test_inv.py'
--- a/bzrlib/tests/test_inv.py	2009-09-09 01:28:34 +0000
+++ b/bzrlib/tests/test_inv.py	2009-09-24 19:26:45 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 2005, 2006, 2007 Canonical Ltd
+# Copyright (C) 2005, 2006, 2007, 2008, 2009 Canonical Ltd
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -17,12 +17,14 @@
 
 from bzrlib import (
     chk_map,
+    groupcompress,
     bzrdir,
     errors,
     inventory,
     osutils,
     repository,
     revision,
+    tests,
     )
 from bzrlib.inventory import (CHKInventory, Inventory, ROOT_ID, InventoryFile,
     InventoryDirectory, InventoryEntry, TreeReference)
@@ -257,6 +259,76 @@
     return repo.get_inventory('result')
 
 
+class TestInventoryUpdates(TestCase):
+
+    def test_creation_from_root_id(self):
+        # iff a root id is passed to the constructor, a root directory is made
+        inv = inventory.Inventory(root_id='tree-root')
+        self.assertNotEqual(None, inv.root)
+        self.assertEqual('tree-root', inv.root.file_id)
+
+    def test_add_path_of_root(self):
+        # if no root id is given at creation time, there is no root directory
+        inv = inventory.Inventory(root_id=None)
+        self.assertIs(None, inv.root)
+        # add a root entry by adding its path
+        ie = inv.add_path("", "directory", "my-root")
+        ie.revision = 'test-rev'
+        self.assertEqual("my-root", ie.file_id)
+        self.assertIs(ie, inv.root)
+
+    def test_add_path(self):
+        inv = inventory.Inventory(root_id='tree_root')
+        ie = inv.add_path('hello', 'file', 'hello-id')
+        self.assertEqual('hello-id', ie.file_id)
+        self.assertEqual('file', ie.kind)
+
+    def test_copy(self):
+        """Make sure copy() works and creates a deep copy."""
+        inv = inventory.Inventory(root_id='some-tree-root')
+        ie = inv.add_path('hello', 'file', 'hello-id')
+        inv2 = inv.copy()
+        inv.root.file_id = 'some-new-root'
+        ie.name = 'file2'
+        self.assertEqual('some-tree-root', inv2.root.file_id)
+        self.assertEqual('hello', inv2['hello-id'].name)
+
+    def test_copy_empty(self):
+        """Make sure an empty inventory can be copied."""
+        inv = inventory.Inventory(root_id=None)
+        inv2 = inv.copy()
+        self.assertIs(None, inv2.root)
+
+    def test_copy_copies_root_revision(self):
+        """Make sure the revision of the root gets copied."""
+        inv = inventory.Inventory(root_id='someroot')
+        inv.root.revision = 'therev'
+        inv2 = inv.copy()
+        self.assertEquals('someroot', inv2.root.file_id)
+        self.assertEquals('therev', inv2.root.revision)
+
+    def test_create_tree_reference(self):
+        inv = inventory.Inventory('tree-root-123')
+        inv.add(TreeReference('nested-id', 'nested', parent_id='tree-root-123',
+                              revision='rev', reference_revision='rev2'))
+
+    def test_error_encoding(self):
+        inv = inventory.Inventory('tree-root')
+        inv.add(InventoryFile('a-id', u'\u1234', 'tree-root'))
+        e = self.assertRaises(errors.InconsistentDelta, inv.add,
+            InventoryFile('b-id', u'\u1234', 'tree-root'))
+        self.assertContainsRe(str(e), r'\\u1234')
+
+    def test_add_recursive(self):
+        parent = InventoryDirectory('src-id', 'src', 'tree-root')
+        child = InventoryFile('hello-id', 'hello.c', 'src-id')
+        parent.children[child.file_id] = child
+        inv = inventory.Inventory('tree-root')
+        inv.add(parent)
+        self.assertEqual('src/hello.c', inv.id2path('hello-id'))
+
+
+
 class TestDeltaApplication(TestCaseWithTransport):
  
     def get_empty_inventory(self, reference_inv=None):
@@ -503,6 +575,22 @@
             inv, delta)
 
 
+class TestInventory(TestCase):
+
+    def test_is_root(self):
+        """Ensure our root-checking code is accurate."""
+        inv = inventory.Inventory('TREE_ROOT')
+        self.assertTrue(inv.is_root('TREE_ROOT'))
+        self.assertFalse(inv.is_root('booga'))
+        inv.root.file_id = 'booga'
+        self.assertFalse(inv.is_root('TREE_ROOT'))
+        self.assertTrue(inv.is_root('booga'))
+        # works properly even if no root is set
+        inv.root = None
+        self.assertFalse(inv.is_root('TREE_ROOT'))
+        self.assertFalse(inv.is_root('booga'))
+
+
 class TestInventoryEntry(TestCase):
 
     def test_file_kind_character(self):
@@ -650,17 +738,12 @@
         self.assertEqual(expected_change, change)
 
 
-class TestCHKInventory(TestCaseWithTransport):
+class TestCHKInventory(tests.TestCaseWithMemoryTransport):
 
     def get_chk_bytes(self):
-        # The easiest way to get a CHK store is a development6 repository and
-        # then work with the chk_bytes attribute directly.
-        repo = self.make_repository(".", format="development6-rich-root")
-        repo.lock_write()
-        self.addCleanup(repo.unlock)
-        repo.start_write_group()
-        self.addCleanup(repo.abort_write_group)
-        return repo.chk_bytes
+        factory = groupcompress.make_pack_factory(True, True, 1)
+        trans = self.get_transport('')
+        return factory(trans)
 
     def read_bytes(self, chk_bytes, key):
         stream = chk_bytes.get_record_stream([key], 'unordered', True)
@@ -1131,3 +1214,135 @@
         self.assertIsInstance(ie2.name, unicode)
         self.assertEqual(('tree\xce\xa9name', 'tree-root-id', 'tree-rev-id'),
                          inv._bytes_to_utf8name_key(bytes))
+
+
+class TestCHKInventoryExpand(tests.TestCaseWithMemoryTransport):
+
+    def get_chk_bytes(self):
+        factory = groupcompress.make_pack_factory(True, True, 1)
+        trans = self.get_transport('')
+        return factory(trans)
+
+    def make_dir(self, inv, name, parent_id):
+        inv.add(inv.make_entry('directory', name, parent_id, name + '-id'))
+
+    def make_file(self, inv, name, parent_id, content='content\n'):
+        ie = inv.make_entry('file', name, parent_id, name + '-id')
+        ie.text_sha1 = osutils.sha_string(content)
+        ie.text_size = len(content)
+        inv.add(ie)
+
+    def make_simple_inventory(self):
+        inv = Inventory('TREE_ROOT')
+        inv.revision_id = "revid"
+        inv.root.revision = "rootrev"
+        # /                 TREE_ROOT
+        # dir1/             dir1-id
+        #   sub-file1       sub-file1-id
+        #   sub-file2       sub-file2-id
+        #   sub-dir1/       sub-dir1-id
+        #     subsub-file1  subsub-file1-id
+        # dir2/             dir2-id
+        #   sub2-file1      sub2-file1-id
+        # top               top-id
+        self.make_dir(inv, 'dir1', 'TREE_ROOT')
+        self.make_dir(inv, 'dir2', 'TREE_ROOT')
+        self.make_dir(inv, 'sub-dir1', 'dir1-id')
+        self.make_file(inv, 'top', 'TREE_ROOT')
+        self.make_file(inv, 'sub-file1', 'dir1-id')
+        self.make_file(inv, 'sub-file2', 'dir1-id')
+        self.make_file(inv, 'subsub-file1', 'sub-dir1-id')
+        self.make_file(inv, 'sub2-file1', 'dir2-id')
+        chk_bytes = self.get_chk_bytes()
+        #  use a small maximum_size to force internal paging structures
+        chk_inv = CHKInventory.from_inventory(chk_bytes, inv,
+                        maximum_size=100,
+                        search_key_name='hash-255-way')
+        bytes = ''.join(chk_inv.to_lines())
+        return CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
+
+    def assert_Getitems(self, expected_fileids, inv, file_ids):
+        self.assertEqual(sorted(expected_fileids),
+                         sorted([ie.file_id for ie in inv._getitems(file_ids)]))
+
+    def assertExpand(self, all_ids, inv, file_ids):
+        (val_all_ids,
+         val_children) = inv._expand_fileids_to_parents_and_children(file_ids)
+        self.assertEqual(set(all_ids), val_all_ids)
+        entries = inv._getitems(val_all_ids)
+        expected_children = {}
+        for entry in entries:
+            s = expected_children.setdefault(entry.parent_id, [])
+            s.append(entry.file_id)
+        val_children = dict((k, sorted(v)) for k, v
+                            in val_children.iteritems())
+        expected_children = dict((k, sorted(v)) for k, v
+                            in expected_children.iteritems())
+        self.assertEqual(expected_children, val_children)
+
+    def test_make_simple_inventory(self):
+        inv = self.make_simple_inventory()
+        layout = []
+        for path, entry in inv.iter_entries_by_dir():
+            layout.append((path, entry.file_id))
+        self.assertEqual([
+            ('', 'TREE_ROOT'),
+            ('dir1', 'dir1-id'),
+            ('dir2', 'dir2-id'),
+            ('top', 'top-id'),
+            ('dir1/sub-dir1', 'sub-dir1-id'),
+            ('dir1/sub-file1', 'sub-file1-id'),
+            ('dir1/sub-file2', 'sub-file2-id'),
+            ('dir1/sub-dir1/subsub-file1', 'subsub-file1-id'),
+            ('dir2/sub2-file1', 'sub2-file1-id'),
+            ], layout)
+
+    def test__getitems(self):
+        inv = self.make_simple_inventory()
+        # Reading from disk
+        self.assert_Getitems(['dir1-id'], inv, ['dir1-id'])
+        self.assertTrue('dir1-id' in inv._fileid_to_entry_cache)
+        self.assertFalse('sub-file2-id' in inv._fileid_to_entry_cache)
+        # From cache
+        self.assert_Getitems(['dir1-id'], inv, ['dir1-id'])
+        # Mixed
+        self.assert_Getitems(['dir1-id', 'sub-file2-id'], inv,
+                             ['dir1-id', 'sub-file2-id'])
+        self.assertTrue('dir1-id' in inv._fileid_to_entry_cache)
+        self.assertTrue('sub-file2-id' in inv._fileid_to_entry_cache)
+
+    def test_single_file(self):
+        inv = self.make_simple_inventory()
+        self.assertExpand(['TREE_ROOT', 'top-id'], inv, ['top-id'])
+
+    def test_get_all_parents(self):
+        inv = self.make_simple_inventory()
+        self.assertExpand(['TREE_ROOT', 'dir1-id', 'sub-dir1-id',
+                           'subsub-file1-id',
+                          ], inv, ['subsub-file1-id'])
+
+    def test_get_children(self):
+        inv = self.make_simple_inventory()
+        self.assertExpand(['TREE_ROOT', 'dir1-id', 'sub-dir1-id',
+                           'sub-file1-id', 'sub-file2-id', 'subsub-file1-id',
+                          ], inv, ['dir1-id'])
+
+    def test_from_root(self):
+        inv = self.make_simple_inventory()
+        self.assertExpand(['TREE_ROOT', 'dir1-id', 'dir2-id', 'sub-dir1-id',
+                           'sub-file1-id', 'sub-file2-id', 'sub2-file1-id',
+                           'subsub-file1-id', 'top-id'], inv, ['TREE_ROOT'])
+
+    def test_top_level_file(self):
+        inv = self.make_simple_inventory()
+        self.assertExpand(['TREE_ROOT', 'top-id'], inv, ['top-id'])
+
+    def test_subsub_file(self):
+        inv = self.make_simple_inventory()
+        self.assertExpand(['TREE_ROOT', 'dir1-id', 'sub-dir1-id',
+                           'subsub-file1-id'], inv, ['subsub-file1-id'])
+
+    def test_sub_and_root(self):
+        inv = self.make_simple_inventory()
+        self.assertExpand(['TREE_ROOT', 'dir1-id', 'sub-dir1-id', 'top-id',
+                           'subsub-file1-id'], inv, ['top-id', 'subsub-file1-id'])




More information about the bazaar-commits mailing list