Rev 2742: (robertc) Index layer tweaks - -Dindex and a key_count method (only accurate on low level indices). (Robert Collins). in file:///home/pqm/archives/thelove/bzr/%2Btrunk/

Canonical.com Patch Queue Manager pqm at pqm.ubuntu.com
Wed Aug 22 05:44:34 BST 2007


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

------------------------------------------------------------
revno: 2742
revision-id: pqm at pqm.ubuntu.com-20070822044432-tfi063jpsr3vgnao
parent: pqm at pqm.ubuntu.com-20070822024917-nw7dh478y4d8cjeg
parent: robertc at robertcollins.net-20070822041335-kdxlx0fuj6qes2cy
committer: Canonical.com Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Wed 2007-08-22 05:44:32 +0100
message:
  (robertc) Index layer tweaks - -Dindex and a key_count method (only accurate on low level indices). (Robert Collins).
modified:
  bzrlib/debug.py                debug.py-20061102062349-vdhrw9qdpck8cl35-1
  bzrlib/index.py                index.py-20070712131115-lolkarso50vjr64s-1
  bzrlib/knit.py                 knit.py-20051212171256-f056ac8f0fbe1bd9
  bzrlib/tests/test_index.py     test_index.py-20070712131115-lolkarso50vjr64s-2
    ------------------------------------------------------------
    revno: 2624.2.19
    merged: robertc at robertcollins.net-20070822041335-kdxlx0fuj6qes2cy
    parent: robertc at robertcollins.net-20070822000026-kvufiqhlreokb1en
    committer: Robert Collins <robertc at robertcollins.net>
    branch nick: index
    timestamp: Wed 2007-08-22 14:13:35 +1000
    message:
      Why we should always test before committing.
    ------------------------------------------------------------
    revno: 2624.2.18
    merged: robertc at robertcollins.net-20070822000026-kvufiqhlreokb1en
    parent: robertc at robertcollins.net-20070808044555-10k443n441f093dx
    parent: pqm at pqm.ubuntu.com-20070821175054-6pcl32ipl9eopnqw
    committer: Robert Collins <robertc at robertcollins.net>
    branch nick: index
    timestamp: Wed 2007-08-22 10:00:26 +1000
    message:
      Merge bzr.dev
    ------------------------------------------------------------
    revno: 2624.2.17
    merged: robertc at robertcollins.net-20070808044555-10k443n441f093dx
    parent: robertc at robertcollins.net-20070807054430-65ige2n2869mp7t4
    committer: Robert Collins <robertc at robertcollins.net>
    branch nick: index
    timestamp: Wed 2007-08-08 14:45:55 +1000
    message:
      Review feedback.
    ------------------------------------------------------------
    revno: 2624.2.16
    merged: robertc at robertcollins.net-20070807054430-65ige2n2869mp7t4
    parent: robertc at robertcollins.net-20070803070942-1lrseqr6ligvrtrk
    committer: Robert Collins <robertc at robertcollins.net>
    branch nick: index
    timestamp: Tue 2007-08-07 15:44:30 +1000
    message:
      Add a key_count method to GraphIndex and friends, allowing optimisation of length calculations by the index.
    ------------------------------------------------------------
    revno: 2624.2.15
    merged: robertc at robertcollins.net-20070803070942-1lrseqr6ligvrtrk
    parent: robertc at robertcollins.net-20070801075314-2maihdqr02hah1t3
    committer: Robert Collins <robertc at robertcollins.net>
    branch nick: index
    timestamp: Fri 2007-08-03 17:09:42 +1000
    message:
      Add useful -Dindex flag.
=== modified file 'bzrlib/debug.py'
--- a/bzrlib/debug.py	2007-08-20 03:43:40 +0000
+++ b/bzrlib/debug.py	2007-08-22 00:00:26 +0000
@@ -26,6 +26,7 @@
  * error - show stack traces for all top level exceptions
  * hooks 
  * hpss - trace smart protocol requests and responses
+ * index - trace major index operations
  * lock - trace when lockdir locks are taken or released
 
 """

=== modified file 'bzrlib/index.py'
--- a/bzrlib/index.py	2007-08-01 07:53:14 +0000
+++ b/bzrlib/index.py	2007-08-22 04:13:35 +0000
@@ -27,9 +27,14 @@
 from cStringIO import StringIO
 import re
 
-from bzrlib import errors
+from bzrlib.lazy_import import lazy_import
+lazy_import(globals(), """
+from bzrlib.trace import mutter
+""")
+from bzrlib import debug, errors
 
 _OPTION_KEY_ELEMENTS = "key_elements="
+_OPTION_LEN = "len="
 _OPTION_NODE_REFS = "node_ref_lists="
 _SIGNATURE = "Bazaar Graph Index 1\n"
 
@@ -65,6 +70,7 @@
         :param key_elements: The number of bytestrings in each key.
         """
         self.reference_lists = reference_lists
+        self._keys = set()
         self._nodes = {}
         self._nodes_by_key = {}
         self._key_length = key_elements
@@ -105,6 +111,7 @@
         if key in self._nodes and self._nodes[key][0] == '':
             raise errors.BadIndexDuplicateKey(key, self)
         self._nodes[key] = ('', tuple(node_refs), value)
+        self._keys.add(key)
         if self._key_length > 1:
             key_dict = self._nodes_by_key
             if self.reference_lists:
@@ -123,6 +130,7 @@
         lines = [_SIGNATURE]
         lines.append(_OPTION_NODE_REFS + str(self.reference_lists) + '\n')
         lines.append(_OPTION_KEY_ELEMENTS + str(self._key_length) + '\n')
+        lines.append(_OPTION_LEN + str(len(self._keys)) + '\n')
         prefix_length = sum(len(x) for x in lines)
         # references are byte offsets. To avoid having to do nasty
         # polynomial work to resolve offsets (references to later in the 
@@ -232,6 +240,7 @@
         self._transport = transport
         self._name = name
         self._nodes = None
+        self._key_count = None
         self._keys_by_offset = None
         self._nodes_by_key = None
 
@@ -240,6 +249,8 @@
 
         Mutates self._nodes and self.keys_by_offset.
         """
+        if 'index' in debug.debug_flags:
+            mutter('Reading entire index %s', self._transport.abspath(self._name))
         stream = self._transport.get(self._name)
         self._read_prefix(stream)
         expected_elements = 3 + self._key_length
@@ -296,6 +307,7 @@
                 for subkey in key[:-1]:
                     key_dict = key_dict.setdefault(subkey, {})
                 key_dict[key[-1]] = key_value
+        # cache the keys for quick set intersections
         self._keys = set(self._nodes)
         if trailers != 1:
             # there must be one line - the empty trailer line.
@@ -337,6 +349,13 @@
             self._key_length = int(options_line[len(_OPTION_KEY_ELEMENTS):-1])
         except ValueError:
             raise errors.BadIndexOptions(self)
+        options_line = stream.readline()
+        if not options_line.startswith(_OPTION_LEN):
+            raise errors.BadIndexOptions(self)
+        try:
+            self._key_count = int(options_line[len(_OPTION_LEN):-1])
+        except ValueError:
+            raise errors.BadIndexOptions(self)
 
     def iter_entries(self, keys):
         """Iterate over keys within the index.
@@ -432,6 +451,16 @@
                 # the last thing looked up was a terminal element
                 yield (self, ) + key_dict
 
+    def key_count(self):
+        """Return an estimate of the number of keys in this index.
+        
+        For GraphIndex the estimate is exact.
+        """
+        if self._key_count is None:
+            # really this should just read the prefix
+            self._buffer_all()
+        return self._key_count
+
     def _signature(self):
         """The file signature for this index type."""
         return _SIGNATURE
@@ -538,6 +567,16 @@
                 seen_keys.add(node[1])
                 yield node
 
+    def key_count(self):
+        """Return an estimate of the number of keys in this index.
+        
+        For CombinedGraphIndex this is approximated by the sum of the keys of
+        the child indices. As child indices may have duplicate keys this can
+        have a maximum error of the number of child indices * largest number of
+        keys in any index.
+        """
+        return sum((index.key_count() for index in self._indices), 0)
+
     def validate(self):
         """Validate that everything in the index can be accessed."""
         for index in self._indices:
@@ -590,12 +629,12 @@
         """
         keys = set(keys)
         if self.reference_lists:
-            for key in keys.intersection(self._nodes):
+            for key in keys.intersection(self._keys):
                 node = self._nodes[key]
                 if not node[0]:
                     yield self, key, node[2], node[1]
         else:
-            for key in keys.intersection(self._nodes):
+            for key in keys.intersection(self._keys):
                 node = self._nodes[key]
                 if not node[0]:
                     yield self, key, node[2]
@@ -635,7 +674,7 @@
                 if self.reference_lists:
                     yield self, key, node[2], node[1]
                 else:
-                    yield self ,key, node[2]
+                    yield self, key, node[2]
             return
         for key in keys:
             # sanity check
@@ -670,6 +709,13 @@
             else:
                 yield (self, ) + key_dict
 
+    def key_count(self):
+        """Return an estimate of the number of keys in this index.
+        
+        For InMemoryGraphIndex the estimate is exact.
+        """
+        return len(self._keys)
+
     def validate(self):
         """In memory index's have no known corruption at the moment."""
 
@@ -684,11 +730,12 @@
     nodes and references being added will have prefix prepended.
     """
 
-    def __init__(self, adapted, prefix, missing_key_length, add_nodes_callback=None):
+    def __init__(self, adapted, prefix, missing_key_length,
+        add_nodes_callback=None):
         """Construct an adapter against adapted with prefix."""
         self.adapted = adapted
-        self.prefix = prefix + (None,)*missing_key_length
-        self.prefix_key = prefix
+        self.prefix_key = prefix + (None,)*missing_key_length
+        self.prefix = prefix
         self.prefix_len = len(prefix)
         self.add_nodes_callback = add_nodes_callback
 
@@ -701,18 +748,20 @@
         nodes = tuple(nodes)
         translated_nodes = []
         try:
+            # Add prefix_key to each reference node_refs is a tuple of tuples,
+            # so split it apart, and add prefix_key to the internal reference
             for (key, value, node_refs) in nodes:
                 adjusted_references = (
-                    tuple(tuple(self.prefix_key + ref_node for ref_node in ref_list)
+                    tuple(tuple(self.prefix + ref_node for ref_node in ref_list)
                         for ref_list in node_refs))
-                translated_nodes.append((self.prefix_key + key, value,
+                translated_nodes.append((self.prefix + key, value,
                     adjusted_references))
         except ValueError:
             # XXX: TODO add an explicit interface for getting the reference list
             # status, to handle this bit of user-friendliness in the API more 
             # explicitly.
             for (key, value) in nodes:
-                translated_nodes.append((self.prefix_key + key, value))
+                translated_nodes.append((self.prefix + key, value))
         self.add_nodes_callback(translated_nodes)
 
     def add_node(self, key, value, references=()):
@@ -732,11 +781,11 @@
         """Strip prefix data from nodes and return it."""
         for node in an_iter:
             # cross checks
-            if node[1][:self.prefix_len] != self.prefix_key:
+            if node[1][:self.prefix_len] != self.prefix:
                 raise errors.BadIndexData(self)
             for ref_list in node[3]:
                 for ref_node in ref_list:
-                    if ref_node[:self.prefix_len] != self.prefix_key:
+                    if ref_node[:self.prefix_len] != self.prefix:
                         raise errors.BadIndexData(self)
             yield node[0], node[1][self.prefix_len:], node[2], (
                 tuple(tuple(ref_node[self.prefix_len:] for ref_node in ref_list)
@@ -752,7 +801,7 @@
             defined order for the result iteration - it will be in the most
             efficient order for the index (in this case dictionary hash order).
         """
-        return self._strip_prefix(self.adapted.iter_entries_prefix([self.prefix]))
+        return self._strip_prefix(self.adapted.iter_entries_prefix([self.prefix_key]))
 
     def iter_entries(self, keys):
         """Iterate over keys within the index.
@@ -763,7 +812,7 @@
             efficient order for the index (keys iteration order in this case).
         """
         return self._strip_prefix(self.adapted.iter_entries(
-            self.prefix_key + key for key in keys))
+            self.prefix + key for key in keys))
 
     def iter_entries_prefix(self, keys):
         """Iterate over keys within the index using prefix matching.
@@ -783,7 +832,15 @@
             returned.
         """
         return self._strip_prefix(self.adapted.iter_entries_prefix(
-            self.prefix_key + key for key in keys))
+            self.prefix + key for key in keys))
+
+    def key_count(self):
+        """Return an estimate of the number of keys in this index.
+        
+        For GraphIndexPrefixAdapter this is relatively expensive - key
+        iteration with the prefix is done.
+        """
+        return len(list(self.iter_all_entries()))
 
     def validate(self):
         """Call the adapted's validate."""

=== modified file 'bzrlib/knit.py'
--- a/bzrlib/knit.py	2007-08-17 08:38:23 +0000
+++ b/bzrlib/knit.py	2007-08-22 00:00:26 +0000
@@ -97,7 +97,6 @@
     RevisionAlreadyPresent,
     )
 from bzrlib.tuned_gzip import GzipFile
-from bzrlib.trace import mutter
 from bzrlib.osutils import (
     contains_whitespace,
     contains_linebreaks,

=== modified file 'bzrlib/tests/test_index.py'
--- a/bzrlib/tests/test_index.py	2007-08-01 07:53:14 +0000
+++ b/bzrlib/tests/test_index.py	2007-08-08 04:45:55 +0000
@@ -27,32 +27,41 @@
         builder = GraphIndexBuilder()
         stream = builder.finish()
         contents = stream.read()
-        self.assertEqual("Bazaar Graph Index 1\nnode_ref_lists=0\nkey_elements=1\n\n", contents)
+        self.assertEqual(
+            "Bazaar Graph Index 1\nnode_ref_lists=0\nkey_elements=1\nlen=0\n\n",
+            contents)
 
     def test_build_index_empty_two_element_keys(self):
         builder = GraphIndexBuilder(key_elements=2)
         stream = builder.finish()
         contents = stream.read()
-        self.assertEqual("Bazaar Graph Index 1\nnode_ref_lists=0\nkey_elements=2\n\n", contents)
+        self.assertEqual(
+            "Bazaar Graph Index 1\nnode_ref_lists=0\nkey_elements=2\nlen=0\n\n",
+            contents)
 
     def test_build_index_one_reference_list_empty(self):
         builder = GraphIndexBuilder(reference_lists=1)
         stream = builder.finish()
         contents = stream.read()
-        self.assertEqual("Bazaar Graph Index 1\nnode_ref_lists=1\nkey_elements=1\n\n", contents)
+        self.assertEqual(
+            "Bazaar Graph Index 1\nnode_ref_lists=1\nkey_elements=1\nlen=0\n\n",
+            contents)
 
     def test_build_index_two_reference_list_empty(self):
         builder = GraphIndexBuilder(reference_lists=2)
         stream = builder.finish()
         contents = stream.read()
-        self.assertEqual("Bazaar Graph Index 1\nnode_ref_lists=2\nkey_elements=1\n\n", contents)
+        self.assertEqual(
+            "Bazaar Graph Index 1\nnode_ref_lists=2\nkey_elements=1\nlen=0\n\n",
+            contents)
 
     def test_build_index_one_node_no_refs(self):
         builder = GraphIndexBuilder()
         builder.add_node(('akey', ), 'data')
         stream = builder.finish()
         contents = stream.read()
-        self.assertEqual("Bazaar Graph Index 1\nnode_ref_lists=0\nkey_elements=1\n"
+        self.assertEqual(
+            "Bazaar Graph Index 1\nnode_ref_lists=0\nkey_elements=1\nlen=1\n"
             "akey\x00\x00\x00data\n\n", contents)
 
     def test_build_index_one_node_no_refs_accepts_empty_reflist(self):
@@ -60,7 +69,8 @@
         builder.add_node(('akey', ), 'data', ())
         stream = builder.finish()
         contents = stream.read()
-        self.assertEqual("Bazaar Graph Index 1\nnode_ref_lists=0\nkey_elements=1\n"
+        self.assertEqual(
+            "Bazaar Graph Index 1\nnode_ref_lists=0\nkey_elements=1\nlen=1\n"
             "akey\x00\x00\x00data\n\n", contents)
 
     def test_build_index_one_node_2_element_keys(self):
@@ -71,7 +81,8 @@
         builder.add_node(('akey', 'secondpart'), 'data')
         stream = builder.finish()
         contents = stream.read()
-        self.assertEqual("Bazaar Graph Index 1\nnode_ref_lists=0\nkey_elements=2\n"
+        self.assertEqual(
+            "Bazaar Graph Index 1\nnode_ref_lists=0\nkey_elements=2\nlen=1\n"
             "akey\x00secondpart\x00\x00\x00data\n\n", contents)
 
     def test_add_node_empty_value(self):
@@ -79,7 +90,8 @@
         builder.add_node(('akey', ), '')
         stream = builder.finish()
         contents = stream.read()
-        self.assertEqual("Bazaar Graph Index 1\nnode_ref_lists=0\nkey_elements=1\n"
+        self.assertEqual(
+            "Bazaar Graph Index 1\nnode_ref_lists=0\nkey_elements=1\nlen=1\n"
             "akey\x00\x00\x00\n\n", contents)
 
     def test_build_index_nodes_sorted(self):
@@ -93,7 +105,8 @@
         builder.add_node(('2001', ), 'data')
         stream = builder.finish()
         contents = stream.read()
-        self.assertEqual("Bazaar Graph Index 1\nnode_ref_lists=0\nkey_elements=1\n"
+        self.assertEqual(
+            "Bazaar Graph Index 1\nnode_ref_lists=0\nkey_elements=1\nlen=3\n"
             "2000\x00\x00\x00data\n"
             "2001\x00\x00\x00data\n"
             "2002\x00\x00\x00data\n"
@@ -116,7 +129,8 @@
         builder.add_node(('2001', '2001'), 'data')
         stream = builder.finish()
         contents = stream.read()
-        self.assertEqual("Bazaar Graph Index 1\nnode_ref_lists=0\nkey_elements=2\n"
+        self.assertEqual(
+            "Bazaar Graph Index 1\nnode_ref_lists=0\nkey_elements=2\nlen=9\n"
             "2000\x002000\x00\x00\x00data\n"
             "2000\x002001\x00\x00\x00data\n"
             "2000\x002002\x00\x00\x00data\n"
@@ -133,7 +147,8 @@
         builder.add_node(('key', ), 'data', ([], ))
         stream = builder.finish()
         contents = stream.read()
-        self.assertEqual("Bazaar Graph Index 1\nnode_ref_lists=1\nkey_elements=1\n"
+        self.assertEqual(
+            "Bazaar Graph Index 1\nnode_ref_lists=1\nkey_elements=1\nlen=1\n"
             "key\x00\x00\x00data\n"
             "\n", contents)
 
@@ -142,7 +157,8 @@
         builder.add_node(('key', 'key2'), 'data', ([], ))
         stream = builder.finish()
         contents = stream.read()
-        self.assertEqual("Bazaar Graph Index 1\nnode_ref_lists=1\nkey_elements=2\n"
+        self.assertEqual(
+            "Bazaar Graph Index 1\nnode_ref_lists=1\nkey_elements=2\nlen=1\n"
             "key\x00key2\x00\x00\x00data\n"
             "\n", contents)
 
@@ -151,7 +167,8 @@
         builder.add_node(('key', ), 'data', ([], []))
         stream = builder.finish()
         contents = stream.read()
-        self.assertEqual("Bazaar Graph Index 1\nnode_ref_lists=2\nkey_elements=1\n"
+        self.assertEqual(
+            "Bazaar Graph Index 1\nnode_ref_lists=2\nkey_elements=1\nlen=1\n"
             "key\x00\x00\t\x00data\n"
             "\n", contents)
 
@@ -161,8 +178,9 @@
         builder.add_node(('key', ), 'data', ([('reference', )], ))
         stream = builder.finish()
         contents = stream.read()
-        self.assertEqual("Bazaar Graph Index 1\nnode_ref_lists=1\nkey_elements=1\n"
-            "key\x00\x0066\x00data\n"
+        self.assertEqual(
+            "Bazaar Graph Index 1\nnode_ref_lists=1\nkey_elements=1\nlen=2\n"
+            "key\x00\x0072\x00data\n"
             "reference\x00\x00\x00data\n"
             "\n", contents)
 
@@ -173,8 +191,9 @@
         builder.add_node(('key', ), 'data', ([('reference', ), ('reference2', )], ))
         stream = builder.finish()
         contents = stream.read()
-        self.assertEqual("Bazaar Graph Index 1\nnode_ref_lists=1\nkey_elements=1\n"
-            "key\x00\x00071\r088\x00data\n"
+        self.assertEqual(
+            "Bazaar Graph Index 1\nnode_ref_lists=1\nkey_elements=1\nlen=3\n"
+            "key\x00\x00077\r094\x00data\n"
             "reference\x00\x00\x00data\n"
             "reference2\x00\x00\x00data\n"
             "\n", contents)
@@ -185,9 +204,10 @@
         builder.add_node(('rey', ), 'data', ([('keference', )], [('keference', )]))
         stream = builder.finish()
         contents = stream.read()
-        self.assertEqual("Bazaar Graph Index 1\nnode_ref_lists=2\nkey_elements=1\n"
+        self.assertEqual(
+            "Bazaar Graph Index 1\nnode_ref_lists=2\nkey_elements=1\nlen=2\n"
             "keference\x00\x00\t\x00data\n"
-            "rey\x00\x0053\t53\x00data\n"
+            "rey\x00\x0059\t59\x00data\n"
             "\n", contents)
 
     def test_add_node_referencing_missing_key_makes_absent(self):
@@ -195,10 +215,11 @@
         builder.add_node(('rey', ), 'data', ([('beference', ), ('aeference2', )], ))
         stream = builder.finish()
         contents = stream.read()
-        self.assertEqual("Bazaar Graph Index 1\nnode_ref_lists=1\nkey_elements=1\n"
+        self.assertEqual(
+            "Bazaar Graph Index 1\nnode_ref_lists=1\nkey_elements=1\nlen=1\n"
             "aeference2\x00a\x00\x00\n"
             "beference\x00a\x00\x00\n"
-            "rey\x00\x0068\r53\x00data\n"
+            "rey\x00\x00074\r059\x00data\n"
             "\n", contents)
 
     def test_node_references_three_digits(self):
@@ -208,11 +229,12 @@
         builder.add_node(('2-key', ), '', (references, ))
         stream = builder.finish()
         contents = stream.read()
-        self.assertEqual("Bazaar Graph Index 1\nnode_ref_lists=1\nkey_elements=1\n"
+        self.assertEqual(
+            "Bazaar Graph Index 1\nnode_ref_lists=1\nkey_elements=1\nlen=1\n"
             "0\x00a\x00\x00\n"
             "1\x00a\x00\x00\n"
             "2\x00a\x00\x00\n"
-            "2-key\x00\x00145\r139\r133\r127\r121\r115\r065\r059\r053\x00\n"
+            "2-key\x00\x00151\r145\r139\r133\r127\r121\r071\r065\r059\x00\n"
             "3\x00a\x00\x00\n"
             "4\x00a\x00\x00\n"
             "5\x00a\x00\x00\n"
@@ -228,9 +250,10 @@
         builder.add_node(('parent', ), '', ([('aail', ), ('zther', )], []))
         stream = builder.finish()
         contents = stream.read()
-        self.assertEqual("Bazaar Graph Index 1\nnode_ref_lists=2\nkey_elements=1\n"
+        self.assertEqual(
+            "Bazaar Graph Index 1\nnode_ref_lists=2\nkey_elements=1\nlen=1\n"
             "aail\x00a\x00\x00\n"
-            "parent\x00\x0053\r78\t\x00\n"
+            "parent\x00\x0059\r84\t\x00\n"
             "zther\x00a\x00\x00\n"
             "\n", contents)
 
@@ -331,8 +354,8 @@
 
     def make_index(self, ref_lists=0, key_elements=1, nodes=[]):
         builder = GraphIndexBuilder(ref_lists, key_elements=key_elements)
-        for node, value, references in nodes:
-            builder.add_node(node, value, references)
+        for key, value, references in nodes:
+            builder.add_node(key, value, references)
         stream = builder.finish()
         trans = self.get_transport()
         trans.put_file('index', stream)
@@ -455,6 +478,19 @@
             (index, ('name', 'fin2'), 'beta', ((), ))]),
             set(index.iter_entries_prefix([('name', None)])))
 
+    def test_key_count_empty(self):
+        index = self.make_index()
+        self.assertEqual(0, index.key_count())
+
+    def test_key_count_one(self):
+        index = self.make_index(nodes=[(('name', ), '', ())])
+        self.assertEqual(1, index.key_count())
+
+    def test_key_count_two(self):
+        index = self.make_index(nodes=[
+            (('name', ), '', ()), (('foo', ), '', ())])
+        self.assertEqual(2, index.key_count())
+
     def test_validate_bad_index_errors(self):
         trans = self.get_transport()
         trans.put_bytes('name', "not an index\n")
@@ -499,8 +535,8 @@
 
     def make_index(self, name, ref_lists=0, key_elements=1, nodes=[]):
         builder = GraphIndexBuilder(ref_lists, key_elements=key_elements)
-        for node, value, references in nodes:
-            builder.add_node(node, value, references)
+        for key, value, references in nodes:
+            builder.add_node(key, value, references)
         stream = builder.finish()
         trans = self.get_transport()
         trans.put_file(name, stream)
@@ -624,6 +660,20 @@
         self.assertEqual([(index1, ('key', ), '')],
             list(index.iter_entries([('key', )])))
 
+    def test_key_count_empty(self):
+        index1 = self.make_index('1', nodes=[])
+        index2 = self.make_index('2', nodes=[])
+        index = CombinedGraphIndex([index1, index2])
+        self.assertEqual(0, index.key_count())
+
+    def test_key_count_sums_index_keys(self):
+        index1 = self.make_index('1', nodes=[
+            (('1',), '', ()),
+            (('2',), '', ())])
+        index2 = self.make_index('2', nodes=[(('1',), '', ())])
+        index = CombinedGraphIndex([index1, index2])
+        self.assertEqual(3, index.key_count())
+
     def test_validate_bad_child_index_errors(self):
         trans = self.get_transport()
         trans.put_bytes('name', "not an index\n")
@@ -745,6 +795,18 @@
         index = self.make_index()
         self.assertEqual([], list(index.iter_entries(['a'])))
 
+    def test_key_count_empty(self):
+        index = self.make_index()
+        self.assertEqual(0, index.key_count())
+
+    def test_key_count_one(self):
+        index = self.make_index(nodes=[(('name', ), '')])
+        self.assertEqual(1, index.key_count())
+
+    def test_key_count_two(self):
+        index = self.make_index(nodes=[(('name', ), ''), (('foo', ), '')])
+        self.assertEqual(2, index.key_count())
+
     def test_validate_empty(self):
         index = self.make_index()
         index.validate()
@@ -760,9 +822,9 @@
         result = InMemoryGraphIndex(ref_lists, key_elements=key_elements)
         result.add_nodes(nodes)
         if add_callback:
-            add_nodes_callback=result.add_nodes
+            add_nodes_callback = result.add_nodes
         else:
-            add_nodes_callback=None
+            add_nodes_callback = None
         adapter = GraphIndexPrefixAdapter(result, ('prefix', ), key_elements - 1,
             add_nodes_callback=add_nodes_callback)
         return result, adapter
@@ -833,6 +895,18 @@
             (index, ('prefix2', 'key2', ), 'data2', ((('prefix2', 'key1', ),),))]),
             set(adapter.iter_entries_prefix([('prefix2', None)])))
 
+    def test_key_count_no_matching_keys(self):
+        index, adapter = self.make_index(nodes=[
+            (('notprefix', 'key1'), 'data', ((), ))])
+        self.assertEqual(0, adapter.key_count())
+
+    def test_key_count_some_keys(self):
+        index, adapter = self.make_index(nodes=[
+            (('notprefix', 'key1'), 'data', ((), )),
+            (('prefix', 'key1'), 'data1', ((), )),
+            (('prefix', 'key2'), 'data2', ((('prefix', 'key1'),),))])
+        self.assertEqual(2, adapter.key_count())
+
     def test_validate(self):
         index, adapter = self.make_index()
         calls = []




More information about the bazaar-commits mailing list