/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
3735.2.8 by Robert Collins
New chk_map module for use in tree based inventory storage.
1
# Copyright (C) 2008 Canonical Ltd
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
17
"""Tests for maps built on a CHK versionedfiles facility."""
18
3735.9.1 by John Arbash Meinel
Start working on an iter_interesting_nodes, which can find nodes to transmit
19
from itertools import izip
20
3735.16.3 by John Arbash Meinel
Add functions for _search_key_16 and _search_key_255 and some basic tests for them.
21
from bzrlib import (
22
    chk_map,
23
    osutils,
24
    tests,
25
    )
3735.2.21 by Robert Collins
BROKEN: multi level CHKMap tries, unfinished, subsystem in flux.
26
from bzrlib.chk_map import (
27
    CHKMap,
28
    InternalNode,
29
    LeafNode,
3735.2.86 by Vincent Ladeuil
Stop using assert in code.
30
    Node,
3735.2.21 by Robert Collins
BROKEN: multi level CHKMap tries, unfinished, subsystem in flux.
31
    )
3735.2.86 by Vincent Ladeuil
Stop using assert in code.
32
33
34
class TestNode(tests.TestCase):
35
3735.2.89 by Vincent Ladeuil
Fix the bogus previous fix.
36
    def assertCommonPrefix(self, expected_common, prefix, key):
3735.2.86 by Vincent Ladeuil
Stop using assert in code.
37
        common = Node.common_prefix(prefix, key)
38
        self.assertTrue(len(common) <= len(prefix))
39
        self.assertTrue(len(common) <= len(key))
40
        self.assertStartsWith(prefix, common)
41
        self.assertStartsWith(key, common)
3735.2.89 by Vincent Ladeuil
Fix the bogus previous fix.
42
        self.assertEquals(expected_common, common)
3735.2.86 by Vincent Ladeuil
Stop using assert in code.
43
44
    def test_common_prefix(self):
3735.2.89 by Vincent Ladeuil
Fix the bogus previous fix.
45
        self.assertCommonPrefix('beg', 'beg', 'begin')
3735.2.86 by Vincent Ladeuil
Stop using assert in code.
46
47
    def test_no_common_prefix(self):
3735.2.89 by Vincent Ladeuil
Fix the bogus previous fix.
48
        self.assertCommonPrefix('', 'begin', 'end')
3735.2.86 by Vincent Ladeuil
Stop using assert in code.
49
50
    def test_equal(self):
3735.2.89 by Vincent Ladeuil
Fix the bogus previous fix.
51
        self.assertCommonPrefix('begin', 'begin', 'begin')
3735.2.86 by Vincent Ladeuil
Stop using assert in code.
52
53
    def test_not_a_prefix(self):
3735.2.89 by Vincent Ladeuil
Fix the bogus previous fix.
54
        self.assertCommonPrefix('b', 'begin', 'b')
3735.2.115 by John Arbash Meinel
Fix the root cause of why _iter_nodes was not handling key_filter.
55
56
3735.2.86 by Vincent Ladeuil
Stop using assert in code.
57
class TestCaseWithStore(tests.TestCaseWithTransport):
3735.2.8 by Robert Collins
New chk_map module for use in tree based inventory storage.
58
59
    def get_chk_bytes(self):
3735.2.94 by Vincent Ladeuil
Fix typos.
60
        # The easiest way to get a CHK store is a development5 repository and
3735.2.8 by Robert Collins
New chk_map module for use in tree based inventory storage.
61
        # then work with the chk_bytes attribute directly.
3735.17.6 by John Arbash Meinel
Continue tracking down all the references to development3/4
62
        repo = self.make_repository(".", format="development5")
3735.2.8 by Robert Collins
New chk_map module for use in tree based inventory storage.
63
        repo.lock_write()
64
        self.addCleanup(repo.unlock)
65
        repo.start_write_group()
66
        self.addCleanup(repo.abort_write_group)
67
        return repo.chk_bytes
68
3735.2.115 by John Arbash Meinel
Fix the root cause of why _iter_nodes was not handling key_filter.
69
    def _get_map(self, a_dict, maximum_size=0, chk_bytes=None, key_width=1,
70
                 search_key_func=None):
3735.2.30 by Robert Collins
Start iter_changes between CHKMap instances.
71
        if chk_bytes is None:
72
            chk_bytes = self.get_chk_bytes()
3735.2.43 by Robert Collins
Teach CHKMap how to iter items in 2-tuple keyspaces.
73
        root_key = CHKMap.from_dict(chk_bytes, a_dict,
3735.2.115 by John Arbash Meinel
Fix the root cause of why _iter_nodes was not handling key_filter.
74
            maximum_size=maximum_size, key_width=key_width,
75
            search_key_func=search_key_func)
76
        chkmap = CHKMap(chk_bytes, root_key, search_key_func=search_key_func)
3735.2.21 by Robert Collins
BROKEN: multi level CHKMap tries, unfinished, subsystem in flux.
77
        return chkmap
78
3735.2.8 by Robert Collins
New chk_map module for use in tree based inventory storage.
79
    def read_bytes(self, chk_bytes, key):
80
        stream = chk_bytes.get_record_stream([key], 'unordered', True)
3735.15.13 by John Arbash Meinel
Change the term 'lookup' to the term 'search', as it is closer to what Robert envisioned.
81
        record = stream.next()
82
        if record.storage_kind == 'absent':
83
            self.fail('Store does not contain the key %s' % (key,))
84
        return record.get_bytes_as("fulltext")
3735.2.8 by Robert Collins
New chk_map module for use in tree based inventory storage.
85
3735.2.21 by Robert Collins
BROKEN: multi level CHKMap tries, unfinished, subsystem in flux.
86
    def to_dict(self, node, *args):
87
        return dict(node.iteritems(*args))
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
88
89
90
class TestMap(TestCaseWithStore):
91
3735.2.8 by Robert Collins
New chk_map module for use in tree based inventory storage.
92
    def assertHasABMap(self, chk_bytes):
3735.17.7 by John Arbash Meinel
Get all of test_chk_map passing.
93
        ab_leaf_bytes = 'chkleaf:\n0\n1\n1\na\n\x001\nb\n'
3735.15.13 by John Arbash Meinel
Change the term 'lookup' to the term 'search', as it is closer to what Robert envisioned.
94
        ab_sha1 = osutils.sha_string(ab_leaf_bytes)
3735.17.7 by John Arbash Meinel
Get all of test_chk_map passing.
95
        self.assertEqual('90986195696b177c8895d48fdb4b7f2366f798a0', ab_sha1)
3735.15.13 by John Arbash Meinel
Change the term 'lookup' to the term 'search', as it is closer to what Robert envisioned.
96
        root_key = ('sha1:' + ab_sha1,)
97
        self.assertEqual(ab_leaf_bytes, self.read_bytes(chk_bytes, root_key))
3735.2.24 by Robert Collins
test_chk_map tests all passing.
98
        return root_key
3735.2.8 by Robert Collins
New chk_map module for use in tree based inventory storage.
99
100
    def assertHasEmptyMap(self, chk_bytes):
3735.15.14 by John Arbash Meinel
Change the apply_ab_empty and apply_empty_ab tests to make them slightly more sha1 independent.
101
        empty_leaf_bytes = 'chkleaf:\n0\n1\n0\n\n'
3735.15.13 by John Arbash Meinel
Change the term 'lookup' to the term 'search', as it is closer to what Robert envisioned.
102
        empty_sha1 = osutils.sha_string(empty_leaf_bytes)
3735.15.14 by John Arbash Meinel
Change the apply_ab_empty and apply_empty_ab tests to make them slightly more sha1 independent.
103
        self.assertEqual('8571e09bf1bcc5b9621ce31b3d4c93d6e9a1ed26', empty_sha1)
3735.15.13 by John Arbash Meinel
Change the term 'lookup' to the term 'search', as it is closer to what Robert envisioned.
104
        root_key = ('sha1:' + empty_sha1,)
105
        self.assertEqual(empty_leaf_bytes, self.read_bytes(chk_bytes, root_key))
3735.2.24 by Robert Collins
test_chk_map tests all passing.
106
        return root_key
3735.2.17 by Robert Collins
Cache node length to avoid full iteration on __len__ calls.
107
3735.11.2 by John Arbash Meinel
Add some CHKMap test helpers to make sure that maps end up in 'canonical' form.
108
    def assertMapLayoutEqual(self, map_one, map_two):
109
        """Assert that the internal structure is identical between the maps."""
110
        map_one._ensure_root()
111
        node_one_stack = [map_one._root_node]
112
        map_two._ensure_root()
113
        node_two_stack = [map_two._root_node]
114
        while node_one_stack:
115
            node_one = node_one_stack.pop()
116
            node_two = node_two_stack.pop()
117
            if node_one.__class__ != node_two.__class__:
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
118
                self.assertEqualDiff(map_one._dump_tree(include_keys=True),
119
                                     map_two._dump_tree(include_keys=True))
3735.15.13 by John Arbash Meinel
Change the term 'lookup' to the term 'search', as it is closer to what Robert envisioned.
120
            self.assertEqual(node_one._search_prefix,
121
                             node_two._search_prefix)
3735.11.2 by John Arbash Meinel
Add some CHKMap test helpers to make sure that maps end up in 'canonical' form.
122
            if isinstance(node_one, InternalNode):
123
                # Internal nodes must have identical references
124
                self.assertEqual(sorted(node_one._items.keys()),
125
                                 sorted(node_two._items.keys()))
126
                node_one_stack.extend(node_one._iter_nodes(map_one._store))
127
                node_two_stack.extend(node_two._iter_nodes(map_two._store))
128
            else:
129
                # Leaf nodes must have identical contents
130
                self.assertEqual(node_one._items, node_two._items)
3735.28.3 by Vincent Ladeuil
Tighten tests.
131
        self.assertEquals([], node_two_stack)
3735.11.2 by John Arbash Meinel
Add some CHKMap test helpers to make sure that maps end up in 'canonical' form.
132
133
    def assertCanonicalForm(self, chkmap):
134
        """Assert that the chkmap is in 'canonical' form.
135
136
        We do this by adding all of the key value pairs from scratch, both in
137
        forward order and reverse order, and assert that the final tree layout
138
        is identical.
139
        """
140
        items = list(chkmap.iteritems())
141
        map_forward = chk_map.CHKMap(None, None)
142
        map_forward._root_node.set_maximum_size(chkmap._root_node.maximum_size)
143
        for key, value in items:
144
            map_forward.map(key, value)
145
        self.assertMapLayoutEqual(map_forward, chkmap)
146
        map_reverse = chk_map.CHKMap(None, None)
147
        map_reverse._root_node.set_maximum_size(chkmap._root_node.maximum_size)
148
        for key, value in reversed(items):
149
            map_reverse.map(key, value)
150
        self.assertMapLayoutEqual(map_reverse, chkmap)
151
152
    def test_assert_map_layout_equal(self):
153
        store = self.get_chk_bytes()
154
        map_one = CHKMap(store, None)
155
        map_one._root_node.set_maximum_size(20)
156
        map_two = CHKMap(store, None)
157
        map_two._root_node.set_maximum_size(20)
158
        self.assertMapLayoutEqual(map_one, map_two)
159
        map_one.map('aaa', 'value')
160
        self.assertRaises(AssertionError,
161
            self.assertMapLayoutEqual, map_one, map_two)
162
        map_two.map('aaa', 'value')
163
        self.assertMapLayoutEqual(map_one, map_two)
164
        # Split the tree, so we ensure that internal nodes and leaf nodes are
165
        # properly checked
166
        map_one.map('aab', 'value')
167
        self.assertIsInstance(map_one._root_node, InternalNode)
168
        self.assertRaises(AssertionError,
169
            self.assertMapLayoutEqual, map_one, map_two)
170
        map_two.map('aab', 'value')
171
        self.assertMapLayoutEqual(map_one, map_two)
172
        map_one.map('aac', 'value')
173
        self.assertRaises(AssertionError,
174
            self.assertMapLayoutEqual, map_one, map_two)
175
        self.assertCanonicalForm(map_one)
176
3735.2.8 by Robert Collins
New chk_map module for use in tree based inventory storage.
177
    def test_from_dict_empty(self):
178
        chk_bytes = self.get_chk_bytes()
179
        root_key = CHKMap.from_dict(chk_bytes, {})
3735.2.24 by Robert Collins
test_chk_map tests all passing.
180
        # Check the data was saved and inserted correctly.
181
        expected_root_key = self.assertHasEmptyMap(chk_bytes)
182
        self.assertEqual(expected_root_key, root_key)
3735.2.8 by Robert Collins
New chk_map module for use in tree based inventory storage.
183
184
    def test_from_dict_ab(self):
185
        chk_bytes = self.get_chk_bytes()
3735.15.24 by John Arbash Meinel
We were incorrectly using '' when we wanted to use None as the common prefix.
186
        root_key = CHKMap.from_dict(chk_bytes, {"a": "b"})
3735.2.24 by Robert Collins
test_chk_map tests all passing.
187
        # Check the data was saved and inserted correctly.
188
        expected_root_key = self.assertHasABMap(chk_bytes)
189
        self.assertEqual(expected_root_key, root_key)
3735.2.8 by Robert Collins
New chk_map module for use in tree based inventory storage.
190
191
    def test_apply_empty_ab(self):
192
        # applying a delta (None, "a", "b") to an empty chkmap generates the
193
        # same map as from_dict_ab.
194
        chk_bytes = self.get_chk_bytes()
195
        root_key = CHKMap.from_dict(chk_bytes, {})
196
        chkmap = CHKMap(chk_bytes, root_key)
197
        new_root = chkmap.apply_delta([(None, "a", "b")])
3735.2.24 by Robert Collins
test_chk_map tests all passing.
198
        # Check the data was saved and inserted correctly.
199
        expected_root_key = self.assertHasABMap(chk_bytes)
200
        self.assertEqual(expected_root_key, new_root)
3735.2.8 by Robert Collins
New chk_map module for use in tree based inventory storage.
201
        # The update should have left us with an in memory root node, with an
202
        # updated key.
203
        self.assertEqual(new_root, chkmap._root_node._key)
204
205
    def test_apply_ab_empty(self):
3735.2.30 by Robert Collins
Start iter_changes between CHKMap instances.
206
        # applying a delta ("a", None, None) to a map with 'a' in it generates
207
        # an empty map.
3735.2.8 by Robert Collins
New chk_map module for use in tree based inventory storage.
208
        chk_bytes = self.get_chk_bytes()
3735.2.24 by Robert Collins
test_chk_map tests all passing.
209
        root_key = CHKMap.from_dict(chk_bytes, {("a",):"b"})
3735.2.8 by Robert Collins
New chk_map module for use in tree based inventory storage.
210
        chkmap = CHKMap(chk_bytes, root_key)
3735.2.24 by Robert Collins
test_chk_map tests all passing.
211
        new_root = chkmap.apply_delta([(("a",), None, None)])
212
        # Check the data was saved and inserted correctly.
213
        expected_root_key = self.assertHasEmptyMap(chk_bytes)
214
        self.assertEqual(expected_root_key, new_root)
3735.2.8 by Robert Collins
New chk_map module for use in tree based inventory storage.
215
        # The update should have left us with an in memory root node, with an
216
        # updated key.
217
        self.assertEqual(new_root, chkmap._root_node._key)
218
3735.9.3 by John Arbash Meinel
Add a direct test case that shows how the map() function is not
219
    def test_apply_delta_is_deterministic(self):
220
        chk_bytes = self.get_chk_bytes()
221
        chkmap1 = CHKMap(chk_bytes, None)
222
        chkmap1._root_node.set_maximum_size(10)
223
        chkmap1.apply_delta([(None, ('aaa',), 'common'),
224
                             (None, ('bba',), 'target2'),
225
                             (None, ('bbb',), 'common')])
226
        root_key1 = chkmap1._save()
3735.11.2 by John Arbash Meinel
Add some CHKMap test helpers to make sure that maps end up in 'canonical' form.
227
        self.assertCanonicalForm(chkmap1)
228
3735.9.3 by John Arbash Meinel
Add a direct test case that shows how the map() function is not
229
        chkmap2 = CHKMap(chk_bytes, None)
230
        chkmap2._root_node.set_maximum_size(10)
231
        chkmap2.apply_delta([(None, ('bbb',), 'common'),
232
                             (None, ('bba',), 'target2'),
233
                             (None, ('aaa',), 'common')])
234
        root_key2 = chkmap2._save()
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
235
        self.assertEqualDiff(chkmap1._dump_tree(include_keys=True),
236
                             chkmap2._dump_tree(include_keys=True))
3735.9.3 by John Arbash Meinel
Add a direct test case that shows how the map() function is not
237
        self.assertEqual(root_key1, root_key2)
3735.11.2 by John Arbash Meinel
Add some CHKMap test helpers to make sure that maps end up in 'canonical' form.
238
        self.assertCanonicalForm(chkmap2)
3735.9.3 by John Arbash Meinel
Add a direct test case that shows how the map() function is not
239
3735.9.5 by John Arbash Meinel
Don't allow an InternalNode to add a key that doesn't fit.
240
    def test_stable_splitting(self):
241
        store = self.get_chk_bytes()
242
        chkmap = CHKMap(store, None)
243
        # Should fit 2 keys per LeafNode
3735.17.7 by John Arbash Meinel
Get all of test_chk_map passing.
244
        chkmap._root_node.set_maximum_size(35)
3735.9.5 by John Arbash Meinel
Don't allow an InternalNode to add a key that doesn't fit.
245
        chkmap.map(('aaa',), 'v')
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
246
        self.assertEqualDiff("'' LeafNode\n"
3735.11.9 by John Arbash Meinel
Switch _dump_tree to returning trailing '\n' for nicer results
247
                             "      ('aaa',) 'v'\n",
3735.9.5 by John Arbash Meinel
Don't allow an InternalNode to add a key that doesn't fit.
248
                             chkmap._dump_tree())
249
        chkmap.map(('aab',), 'v')
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
250
        self.assertEqualDiff("'' LeafNode\n"
3735.9.5 by John Arbash Meinel
Don't allow an InternalNode to add a key that doesn't fit.
251
                             "      ('aaa',) 'v'\n"
3735.11.9 by John Arbash Meinel
Switch _dump_tree to returning trailing '\n' for nicer results
252
                             "      ('aab',) 'v'\n",
3735.9.5 by John Arbash Meinel
Don't allow an InternalNode to add a key that doesn't fit.
253
                             chkmap._dump_tree())
3735.11.3 by John Arbash Meinel
At the end of unmap() see if children can be packed into a single Leaf.
254
        self.assertCanonicalForm(chkmap)
255
3735.9.5 by John Arbash Meinel
Don't allow an InternalNode to add a key that doesn't fit.
256
        # Creates a new internal node, and splits the others into leaves
257
        chkmap.map(('aac',), 'v')
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
258
        self.assertEqualDiff("'' InternalNode\n"
259
                             "  'aaa' LeafNode\n"
3735.9.5 by John Arbash Meinel
Don't allow an InternalNode to add a key that doesn't fit.
260
                             "      ('aaa',) 'v'\n"
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
261
                             "  'aab' LeafNode\n"
3735.9.5 by John Arbash Meinel
Don't allow an InternalNode to add a key that doesn't fit.
262
                             "      ('aab',) 'v'\n"
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
263
                             "  'aac' LeafNode\n"
3735.11.9 by John Arbash Meinel
Switch _dump_tree to returning trailing '\n' for nicer results
264
                             "      ('aac',) 'v'\n",
3735.9.5 by John Arbash Meinel
Don't allow an InternalNode to add a key that doesn't fit.
265
                             chkmap._dump_tree())
3735.11.3 by John Arbash Meinel
At the end of unmap() see if children can be packed into a single Leaf.
266
        self.assertCanonicalForm(chkmap)
267
3735.9.5 by John Arbash Meinel
Don't allow an InternalNode to add a key that doesn't fit.
268
        # Splits again, because it can't fit in the current structure
269
        chkmap.map(('bbb',), 'v')
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
270
        self.assertEqualDiff("'' InternalNode\n"
271
                             "  'a' InternalNode\n"
272
                             "    'aaa' LeafNode\n"
3735.9.5 by John Arbash Meinel
Don't allow an InternalNode to add a key that doesn't fit.
273
                             "      ('aaa',) 'v'\n"
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
274
                             "    'aab' LeafNode\n"
3735.9.5 by John Arbash Meinel
Don't allow an InternalNode to add a key that doesn't fit.
275
                             "      ('aab',) 'v'\n"
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
276
                             "    'aac' LeafNode\n"
3735.9.5 by John Arbash Meinel
Don't allow an InternalNode to add a key that doesn't fit.
277
                             "      ('aac',) 'v'\n"
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
278
                             "  'b' LeafNode\n"
3735.11.9 by John Arbash Meinel
Switch _dump_tree to returning trailing '\n' for nicer results
279
                             "      ('bbb',) 'v'\n",
3735.9.5 by John Arbash Meinel
Don't allow an InternalNode to add a key that doesn't fit.
280
                             chkmap._dump_tree())
3735.11.3 by John Arbash Meinel
At the end of unmap() see if children can be packed into a single Leaf.
281
        self.assertCanonicalForm(chkmap)
3735.9.5 by John Arbash Meinel
Don't allow an InternalNode to add a key that doesn't fit.
282
3735.11.6 by John Arbash Meinel
It seems that map()'s split code fails when the new key is simply longer.
283
    def test_map_splits_with_longer_key(self):
284
        store = self.get_chk_bytes()
285
        chkmap = CHKMap(store, None)
286
        # Should fit 1 key per LeafNode
287
        chkmap._root_node.set_maximum_size(10)
288
        chkmap.map(('aaa',), 'v')
289
        chkmap.map(('aaaa',), 'v')
290
        self.assertCanonicalForm(chkmap)
291
        self.assertIsInstance(chkmap._root_node, InternalNode)
292
3735.2.72 by John Arbash Meinel
Change deserialise to properly handle when there is a '\r' in the key.
293
    def test_with_linefeed_in_key(self):
294
        store = self.get_chk_bytes()
295
        chkmap = CHKMap(store, None)
296
        # Should fit 1 key per LeafNode
297
        chkmap._root_node.set_maximum_size(10)
298
        chkmap.map(('a\ra',), 'val1')
299
        chkmap.map(('a\rb',), 'val2')
300
        chkmap.map(('ac',), 'val3')
301
        self.assertCanonicalForm(chkmap)
302
        self.assertEqualDiff("'' InternalNode\n"
303
                             "  'a\\r' InternalNode\n"
304
                             "    'a\\ra' LeafNode\n"
305
                             "      ('a\\ra',) 'val1'\n"
306
                             "    'a\\rb' LeafNode\n"
307
                             "      ('a\\rb',) 'val2'\n"
308
                             "  'ac' LeafNode\n"
309
                             "      ('ac',) 'val3'\n",
310
                             chkmap._dump_tree())
311
        # We should also successfully serialise and deserialise these items
312
        root_key = chkmap._save()
313
        chkmap = CHKMap(store, root_key)
314
        self.assertEqualDiff("'' InternalNode\n"
315
                             "  'a\\r' InternalNode\n"
316
                             "    'a\\ra' LeafNode\n"
317
                             "      ('a\\ra',) 'val1'\n"
318
                             "    'a\\rb' LeafNode\n"
319
                             "      ('a\\rb',) 'val2'\n"
320
                             "  'ac' LeafNode\n"
321
                             "      ('ac',) 'val3'\n",
322
                             chkmap._dump_tree())
323
3735.9.11 by John Arbash Meinel
Handle when an InternalNode decides it needs to split.
324
    def test_deep_splitting(self):
325
        store = self.get_chk_bytes()
326
        chkmap = CHKMap(store, None)
327
        # Should fit 2 keys per LeafNode
328
        chkmap._root_node.set_maximum_size(40)
329
        chkmap.map(('aaaaaaaa',), 'v')
330
        chkmap.map(('aaaaabaa',), 'v')
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
331
        self.assertEqualDiff("'' LeafNode\n"
3735.9.11 by John Arbash Meinel
Handle when an InternalNode decides it needs to split.
332
                             "      ('aaaaaaaa',) 'v'\n"
3735.11.9 by John Arbash Meinel
Switch _dump_tree to returning trailing '\n' for nicer results
333
                             "      ('aaaaabaa',) 'v'\n",
3735.9.11 by John Arbash Meinel
Handle when an InternalNode decides it needs to split.
334
                             chkmap._dump_tree())
335
        chkmap.map(('aaabaaaa',), 'v')
336
        chkmap.map(('aaababaa',), 'v')
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
337
        self.assertEqualDiff("'' InternalNode\n"
338
                             "  'aaaa' LeafNode\n"
3735.9.11 by John Arbash Meinel
Handle when an InternalNode decides it needs to split.
339
                             "      ('aaaaaaaa',) 'v'\n"
340
                             "      ('aaaaabaa',) 'v'\n"
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
341
                             "  'aaab' LeafNode\n"
3735.9.11 by John Arbash Meinel
Handle when an InternalNode decides it needs to split.
342
                             "      ('aaabaaaa',) 'v'\n"
3735.11.9 by John Arbash Meinel
Switch _dump_tree to returning trailing '\n' for nicer results
343
                             "      ('aaababaa',) 'v'\n",
3735.9.11 by John Arbash Meinel
Handle when an InternalNode decides it needs to split.
344
                             chkmap._dump_tree())
345
        chkmap.map(('aaabacaa',), 'v')
346
        chkmap.map(('aaabadaa',), 'v')
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
347
        self.assertEqualDiff("'' InternalNode\n"
348
                             "  'aaaa' LeafNode\n"
3735.9.11 by John Arbash Meinel
Handle when an InternalNode decides it needs to split.
349
                             "      ('aaaaaaaa',) 'v'\n"
350
                             "      ('aaaaabaa',) 'v'\n"
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
351
                             "  'aaab' InternalNode\n"
352
                             "    'aaabaa' LeafNode\n"
3735.9.11 by John Arbash Meinel
Handle when an InternalNode decides it needs to split.
353
                             "      ('aaabaaaa',) 'v'\n"
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
354
                             "    'aaabab' LeafNode\n"
3735.9.11 by John Arbash Meinel
Handle when an InternalNode decides it needs to split.
355
                             "      ('aaababaa',) 'v'\n"
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
356
                             "    'aaabac' LeafNode\n"
3735.9.11 by John Arbash Meinel
Handle when an InternalNode decides it needs to split.
357
                             "      ('aaabacaa',) 'v'\n"
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
358
                             "    'aaabad' LeafNode\n"
3735.11.9 by John Arbash Meinel
Switch _dump_tree to returning trailing '\n' for nicer results
359
                             "      ('aaabadaa',) 'v'\n",
3735.9.11 by John Arbash Meinel
Handle when an InternalNode decides it needs to split.
360
                             chkmap._dump_tree())
3735.15.25 by John Arbash Meinel
Change the deep_split code to use a bigger value so we still get a split.
361
        chkmap.map(('aaababba',), 'val')
362
        chkmap.map(('aaababca',), 'val')
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
363
        self.assertEqualDiff("'' InternalNode\n"
364
                             "  'aaaa' LeafNode\n"
3735.9.11 by John Arbash Meinel
Handle when an InternalNode decides it needs to split.
365
                             "      ('aaaaaaaa',) 'v'\n"
366
                             "      ('aaaaabaa',) 'v'\n"
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
367
                             "  'aaab' InternalNode\n"
368
                             "    'aaabaa' LeafNode\n"
3735.9.11 by John Arbash Meinel
Handle when an InternalNode decides it needs to split.
369
                             "      ('aaabaaaa',) 'v'\n"
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
370
                             "    'aaabab' InternalNode\n"
371
                             "      'aaababa' LeafNode\n"
3735.9.11 by John Arbash Meinel
Handle when an InternalNode decides it needs to split.
372
                             "      ('aaababaa',) 'v'\n"
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
373
                             "      'aaababb' LeafNode\n"
3735.15.25 by John Arbash Meinel
Change the deep_split code to use a bigger value so we still get a split.
374
                             "      ('aaababba',) 'val'\n"
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
375
                             "      'aaababc' LeafNode\n"
3735.15.25 by John Arbash Meinel
Change the deep_split code to use a bigger value so we still get a split.
376
                             "      ('aaababca',) 'val'\n"
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
377
                             "    'aaabac' LeafNode\n"
3735.9.11 by John Arbash Meinel
Handle when an InternalNode decides it needs to split.
378
                             "      ('aaabacaa',) 'v'\n"
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
379
                             "    'aaabad' LeafNode\n"
3735.11.9 by John Arbash Meinel
Switch _dump_tree to returning trailing '\n' for nicer results
380
                             "      ('aaabadaa',) 'v'\n",
3735.9.11 by John Arbash Meinel
Handle when an InternalNode decides it needs to split.
381
                             chkmap._dump_tree())
382
        # Now we add a node that should fit around an existing InternalNode,
383
        # but has a slightly different key prefix, which causes a new
384
        # InternalNode split
385
        chkmap.map(('aaabDaaa',), 'v')
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
386
        self.assertEqualDiff("'' InternalNode\n"
387
                             "  'aaaa' LeafNode\n"
3735.9.11 by John Arbash Meinel
Handle when an InternalNode decides it needs to split.
388
                             "      ('aaaaaaaa',) 'v'\n"
389
                             "      ('aaaaabaa',) 'v'\n"
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
390
                             "  'aaab' InternalNode\n"
391
                             "    'aaabD' LeafNode\n"
3735.9.11 by John Arbash Meinel
Handle when an InternalNode decides it needs to split.
392
                             "      ('aaabDaaa',) 'v'\n"
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
393
                             "    'aaaba' InternalNode\n"
394
                             "      'aaabaa' LeafNode\n"
3735.9.11 by John Arbash Meinel
Handle when an InternalNode decides it needs to split.
395
                             "      ('aaabaaaa',) 'v'\n"
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
396
                             "      'aaabab' InternalNode\n"
397
                             "        'aaababa' LeafNode\n"
3735.9.11 by John Arbash Meinel
Handle when an InternalNode decides it needs to split.
398
                             "      ('aaababaa',) 'v'\n"
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
399
                             "        'aaababb' LeafNode\n"
3735.15.25 by John Arbash Meinel
Change the deep_split code to use a bigger value so we still get a split.
400
                             "      ('aaababba',) 'val'\n"
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
401
                             "        'aaababc' LeafNode\n"
3735.15.25 by John Arbash Meinel
Change the deep_split code to use a bigger value so we still get a split.
402
                             "      ('aaababca',) 'val'\n"
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
403
                             "      'aaabac' LeafNode\n"
3735.9.11 by John Arbash Meinel
Handle when an InternalNode decides it needs to split.
404
                             "      ('aaabacaa',) 'v'\n"
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
405
                             "      'aaabad' LeafNode\n"
3735.11.9 by John Arbash Meinel
Switch _dump_tree to returning trailing '\n' for nicer results
406
                             "      ('aaabadaa',) 'v'\n",
3735.9.11 by John Arbash Meinel
Handle when an InternalNode decides it needs to split.
407
                             chkmap._dump_tree())
408
3735.11.11 by John Arbash Meinel
Add logic to map() so that it can also collapse when necessary.
409
    def test_map_collapses_if_size_changes(self):
410
        store = self.get_chk_bytes()
411
        chkmap = CHKMap(store, None)
412
        # Should fit 2 keys per LeafNode
3735.17.7 by John Arbash Meinel
Get all of test_chk_map passing.
413
        chkmap._root_node.set_maximum_size(35)
3735.11.11 by John Arbash Meinel
Add logic to map() so that it can also collapse when necessary.
414
        chkmap.map(('aaa',), 'v')
415
        chkmap.map(('aab',), 'very long value that splits')
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
416
        self.assertEqualDiff("'' InternalNode\n"
417
                             "  'aaa' LeafNode\n"
3735.11.11 by John Arbash Meinel
Add logic to map() so that it can also collapse when necessary.
418
                             "      ('aaa',) 'v'\n"
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
419
                             "  'aab' LeafNode\n"
3735.11.11 by John Arbash Meinel
Add logic to map() so that it can also collapse when necessary.
420
                             "      ('aab',) 'very long value that splits'\n",
421
                             chkmap._dump_tree())
422
        self.assertCanonicalForm(chkmap)
423
        # Now changing the value to something small should cause a rebuild
424
        chkmap.map(('aab',), 'v')
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
425
        self.assertEqualDiff("'' LeafNode\n"
3735.11.11 by John Arbash Meinel
Add logic to map() so that it can also collapse when necessary.
426
                             "      ('aaa',) 'v'\n"
427
                             "      ('aab',) 'v'\n",
428
                             chkmap._dump_tree())
429
        self.assertCanonicalForm(chkmap)
430
431
    def test_map_double_deep_collapses(self):
432
        store = self.get_chk_bytes()
433
        chkmap = CHKMap(store, None)
434
        # Should fit 3 small keys per LeafNode
435
        chkmap._root_node.set_maximum_size(40)
436
        chkmap.map(('aaa',), 'v')
437
        chkmap.map(('aab',), 'very long value that splits')
438
        chkmap.map(('abc',), 'v')
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
439
        self.assertEqualDiff("'' InternalNode\n"
440
                             "  'aa' InternalNode\n"
441
                             "    'aaa' LeafNode\n"
3735.11.11 by John Arbash Meinel
Add logic to map() so that it can also collapse when necessary.
442
                             "      ('aaa',) 'v'\n"
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
443
                             "    'aab' LeafNode\n"
3735.11.11 by John Arbash Meinel
Add logic to map() so that it can also collapse when necessary.
444
                             "      ('aab',) 'very long value that splits'\n"
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
445
                             "  'ab' LeafNode\n"
3735.11.11 by John Arbash Meinel
Add logic to map() so that it can also collapse when necessary.
446
                             "      ('abc',) 'v'\n",
447
                             chkmap._dump_tree())
448
        chkmap.map(('aab',), 'v')
449
        self.assertCanonicalForm(chkmap)
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
450
        self.assertEqualDiff("'' LeafNode\n"
3735.11.11 by John Arbash Meinel
Add logic to map() so that it can also collapse when necessary.
451
                             "      ('aaa',) 'v'\n"
452
                             "      ('aab',) 'v'\n"
453
                             "      ('abc',) 'v'\n",
454
                             chkmap._dump_tree())
455
3735.11.3 by John Arbash Meinel
At the end of unmap() see if children can be packed into a single Leaf.
456
    def test_stable_unmap(self):
457
        store = self.get_chk_bytes()
458
        chkmap = CHKMap(store, None)
459
        # Should fit 2 keys per LeafNode
3735.17.7 by John Arbash Meinel
Get all of test_chk_map passing.
460
        chkmap._root_node.set_maximum_size(35)
3735.11.3 by John Arbash Meinel
At the end of unmap() see if children can be packed into a single Leaf.
461
        chkmap.map(('aaa',), 'v')
462
        chkmap.map(('aab',), 'v')
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
463
        self.assertEqualDiff("'' LeafNode\n"
3735.11.3 by John Arbash Meinel
At the end of unmap() see if children can be packed into a single Leaf.
464
                             "      ('aaa',) 'v'\n"
3735.11.9 by John Arbash Meinel
Switch _dump_tree to returning trailing '\n' for nicer results
465
                             "      ('aab',) 'v'\n",
3735.11.3 by John Arbash Meinel
At the end of unmap() see if children can be packed into a single Leaf.
466
                             chkmap._dump_tree())
467
        # Creates a new internal node, and splits the others into leaves
468
        chkmap.map(('aac',), 'v')
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
469
        self.assertEqualDiff("'' InternalNode\n"
470
                             "  'aaa' LeafNode\n"
3735.11.3 by John Arbash Meinel
At the end of unmap() see if children can be packed into a single Leaf.
471
                             "      ('aaa',) 'v'\n"
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
472
                             "  'aab' LeafNode\n"
3735.11.3 by John Arbash Meinel
At the end of unmap() see if children can be packed into a single Leaf.
473
                             "      ('aab',) 'v'\n"
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
474
                             "  'aac' LeafNode\n"
3735.11.9 by John Arbash Meinel
Switch _dump_tree to returning trailing '\n' for nicer results
475
                             "      ('aac',) 'v'\n",
3735.11.3 by John Arbash Meinel
At the end of unmap() see if children can be packed into a single Leaf.
476
                             chkmap._dump_tree())
477
        self.assertCanonicalForm(chkmap)
478
        # Now lets unmap one of the keys, and assert that we collapse the
479
        # structures.
480
        chkmap.unmap(('aac',))
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
481
        self.assertEqualDiff("'' LeafNode\n"
3735.11.3 by John Arbash Meinel
At the end of unmap() see if children can be packed into a single Leaf.
482
                             "      ('aaa',) 'v'\n"
3735.11.9 by John Arbash Meinel
Switch _dump_tree to returning trailing '\n' for nicer results
483
                             "      ('aab',) 'v'\n",
3735.11.3 by John Arbash Meinel
At the end of unmap() see if children can be packed into a single Leaf.
484
                             chkmap._dump_tree())
485
        self.assertCanonicalForm(chkmap)
486
3735.11.10 by John Arbash Meinel
Change how _check_remap works so it doesn't have to load all keys.
487
    def test_unmap_double_deep(self):
3735.11.4 by John Arbash Meinel
Add a test that unmap() properly chains back up multiple levels.
488
        store = self.get_chk_bytes()
489
        chkmap = CHKMap(store, None)
3735.11.5 by John Arbash Meinel
Ensure that unmap() work even when the LeafNode isn't empty.
490
        # Should fit 3 keys per LeafNode
491
        chkmap._root_node.set_maximum_size(40)
3735.11.4 by John Arbash Meinel
Add a test that unmap() properly chains back up multiple levels.
492
        chkmap.map(('aaa',), 'v')
3735.11.5 by John Arbash Meinel
Ensure that unmap() work even when the LeafNode isn't empty.
493
        chkmap.map(('aaab',), 'v')
3735.11.4 by John Arbash Meinel
Add a test that unmap() properly chains back up multiple levels.
494
        chkmap.map(('aab',), 'very long value')
495
        chkmap.map(('abc',), 'v')
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
496
        self.assertEqualDiff("'' InternalNode\n"
497
                             "  'aa' InternalNode\n"
498
                             "    'aaa' LeafNode\n"
3735.11.4 by John Arbash Meinel
Add a test that unmap() properly chains back up multiple levels.
499
                             "      ('aaa',) 'v'\n"
3735.11.5 by John Arbash Meinel
Ensure that unmap() work even when the LeafNode isn't empty.
500
                             "      ('aaab',) 'v'\n"
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
501
                             "    'aab' LeafNode\n"
3735.11.4 by John Arbash Meinel
Add a test that unmap() properly chains back up multiple levels.
502
                             "      ('aab',) 'very long value'\n"
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
503
                             "  'ab' LeafNode\n"
3735.11.9 by John Arbash Meinel
Switch _dump_tree to returning trailing '\n' for nicer results
504
                             "      ('abc',) 'v'\n",
3735.11.4 by John Arbash Meinel
Add a test that unmap() properly chains back up multiple levels.
505
                             chkmap._dump_tree())
506
        # Removing the 'aab' key should cause everything to collapse back to a
507
        # single node
508
        chkmap.unmap(('aab',))
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
509
        self.assertEqualDiff("'' LeafNode\n"
3735.11.4 by John Arbash Meinel
Add a test that unmap() properly chains back up multiple levels.
510
                             "      ('aaa',) 'v'\n"
3735.11.5 by John Arbash Meinel
Ensure that unmap() work even when the LeafNode isn't empty.
511
                             "      ('aaab',) 'v'\n"
3735.11.9 by John Arbash Meinel
Switch _dump_tree to returning trailing '\n' for nicer results
512
                             "      ('abc',) 'v'\n",
3735.11.5 by John Arbash Meinel
Ensure that unmap() work even when the LeafNode isn't empty.
513
                             chkmap._dump_tree())
514
515
    def test_unmap_double_deep_non_empty_leaf(self):
516
        store = self.get_chk_bytes()
517
        chkmap = CHKMap(store, None)
518
        # Should fit 3 keys per LeafNode
519
        chkmap._root_node.set_maximum_size(40)
520
        chkmap.map(('aaa',), 'v')
521
        chkmap.map(('aab',), 'long value')
522
        chkmap.map(('aabb',), 'v')
523
        chkmap.map(('abc',), 'v')
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
524
        self.assertEqualDiff("'' InternalNode\n"
525
                             "  'aa' InternalNode\n"
526
                             "    'aaa' LeafNode\n"
3735.11.5 by John Arbash Meinel
Ensure that unmap() work even when the LeafNode isn't empty.
527
                             "      ('aaa',) 'v'\n"
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
528
                             "    'aab' LeafNode\n"
3735.11.5 by John Arbash Meinel
Ensure that unmap() work even when the LeafNode isn't empty.
529
                             "      ('aab',) 'long value'\n"
530
                             "      ('aabb',) 'v'\n"
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
531
                             "  'ab' LeafNode\n"
3735.11.9 by John Arbash Meinel
Switch _dump_tree to returning trailing '\n' for nicer results
532
                             "      ('abc',) 'v'\n",
3735.11.5 by John Arbash Meinel
Ensure that unmap() work even when the LeafNode isn't empty.
533
                             chkmap._dump_tree())
534
        # Removing the 'aab' key should cause everything to collapse back to a
535
        # single node
536
        chkmap.unmap(('aab',))
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
537
        self.assertEqualDiff("'' LeafNode\n"
3735.11.5 by John Arbash Meinel
Ensure that unmap() work even when the LeafNode isn't empty.
538
                             "      ('aaa',) 'v'\n"
539
                             "      ('aabb',) 'v'\n"
3735.11.9 by John Arbash Meinel
Switch _dump_tree to returning trailing '\n' for nicer results
540
                             "      ('abc',) 'v'\n",
3735.11.4 by John Arbash Meinel
Add a test that unmap() properly chains back up multiple levels.
541
                             chkmap._dump_tree())
542
3735.11.10 by John Arbash Meinel
Change how _check_remap works so it doesn't have to load all keys.
543
    def test_unmap_with_known_internal_node_doesnt_page(self):
544
        store = self.get_chk_bytes()
545
        chkmap = CHKMap(store, None)
546
        # Should fit 3 keys per LeafNode
547
        chkmap._root_node.set_maximum_size(30)
548
        chkmap.map(('aaa',), 'v')
549
        chkmap.map(('aab',), 'v')
550
        chkmap.map(('aac',), 'v')
551
        chkmap.map(('abc',), 'v')
552
        chkmap.map(('acd',), 'v')
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
553
        self.assertEqualDiff("'' InternalNode\n"
554
                             "  'aa' InternalNode\n"
555
                             "    'aaa' LeafNode\n"
3735.11.10 by John Arbash Meinel
Change how _check_remap works so it doesn't have to load all keys.
556
                             "      ('aaa',) 'v'\n"
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
557
                             "    'aab' LeafNode\n"
3735.11.10 by John Arbash Meinel
Change how _check_remap works so it doesn't have to load all keys.
558
                             "      ('aab',) 'v'\n"
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
559
                             "    'aac' LeafNode\n"
3735.11.10 by John Arbash Meinel
Change how _check_remap works so it doesn't have to load all keys.
560
                             "      ('aac',) 'v'\n"
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
561
                             "  'ab' LeafNode\n"
3735.11.10 by John Arbash Meinel
Change how _check_remap works so it doesn't have to load all keys.
562
                             "      ('abc',) 'v'\n"
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
563
                             "  'ac' LeafNode\n"
3735.11.10 by John Arbash Meinel
Change how _check_remap works so it doesn't have to load all keys.
564
                             "      ('acd',) 'v'\n",
565
                             chkmap._dump_tree())
566
        # Save everything to the map, and start over
567
        chkmap = CHKMap(store, chkmap._save())
568
        # Mapping an 'aa' key loads the internal node, but should not map the
569
        # 'ab' and 'ac' nodes
570
        chkmap.map(('aad',), 'v')
571
        self.assertIsInstance(chkmap._root_node._items['aa'], InternalNode)
572
        self.assertIsInstance(chkmap._root_node._items['ab'], tuple)
573
        self.assertIsInstance(chkmap._root_node._items['ac'], tuple)
574
        # Unmapping 'acd' can notice that 'aa' is an InternalNode and not have
575
        # to map in 'ab'
576
        chkmap.unmap(('acd',))
577
        self.assertIsInstance(chkmap._root_node._items['aa'], InternalNode)
578
        self.assertIsInstance(chkmap._root_node._items['ab'], tuple)
579
580
    def test_unmap_without_fitting_doesnt_page_in(self):
581
        store = self.get_chk_bytes()
582
        chkmap = CHKMap(store, None)
583
        # Should fit 2 keys per LeafNode
584
        chkmap._root_node.set_maximum_size(20)
585
        chkmap.map(('aaa',), 'v')
586
        chkmap.map(('aab',), 'v')
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
587
        self.assertEqualDiff("'' InternalNode\n"
588
                             "  'aaa' LeafNode\n"
3735.11.10 by John Arbash Meinel
Change how _check_remap works so it doesn't have to load all keys.
589
                             "      ('aaa',) 'v'\n"
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
590
                             "  'aab' LeafNode\n"
3735.11.10 by John Arbash Meinel
Change how _check_remap works so it doesn't have to load all keys.
591
                             "      ('aab',) 'v'\n",
592
                             chkmap._dump_tree())
593
        # Save everything to the map, and start over
594
        chkmap = CHKMap(store, chkmap._save())
595
        chkmap.map(('aac',), 'v')
596
        chkmap.map(('aad',), 'v')
597
        chkmap.map(('aae',), 'v')
598
        chkmap.map(('aaf',), 'v')
599
        # At this point, the previous nodes should not be paged in, but the
600
        # newly added nodes would be
601
        self.assertIsInstance(chkmap._root_node._items['aaa'], tuple)
602
        self.assertIsInstance(chkmap._root_node._items['aab'], tuple)
603
        self.assertIsInstance(chkmap._root_node._items['aac'], LeafNode)
604
        self.assertIsInstance(chkmap._root_node._items['aad'], LeafNode)
605
        self.assertIsInstance(chkmap._root_node._items['aae'], LeafNode)
606
        self.assertIsInstance(chkmap._root_node._items['aaf'], LeafNode)
607
        # Now unmapping one of the new nodes will use only the already-paged-in
608
        # nodes to determine that we don't need to do more.
609
        chkmap.unmap(('aaf',))
610
        self.assertIsInstance(chkmap._root_node._items['aaa'], tuple)
611
        self.assertIsInstance(chkmap._root_node._items['aab'], tuple)
612
        self.assertIsInstance(chkmap._root_node._items['aac'], LeafNode)
613
        self.assertIsInstance(chkmap._root_node._items['aad'], LeafNode)
614
        self.assertIsInstance(chkmap._root_node._items['aae'], LeafNode)
615
616
    def test_unmap_pages_in_if_necessary(self):
617
        store = self.get_chk_bytes()
618
        chkmap = CHKMap(store, None)
619
        # Should fit 2 keys per LeafNode
3735.15.26 by John Arbash Meinel
Update the unmap_pages_in to also handle specific page-cache edge cases.
620
        chkmap._root_node.set_maximum_size(30)
621
        chkmap.map(('aaa',), 'val')
622
        chkmap.map(('aab',), 'val')
623
        chkmap.map(('aac',), 'val')
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
624
        self.assertEqualDiff("'' InternalNode\n"
625
                             "  'aaa' LeafNode\n"
3735.15.26 by John Arbash Meinel
Update the unmap_pages_in to also handle specific page-cache edge cases.
626
                             "      ('aaa',) 'val'\n"
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
627
                             "  'aab' LeafNode\n"
3735.15.26 by John Arbash Meinel
Update the unmap_pages_in to also handle specific page-cache edge cases.
628
                             "      ('aab',) 'val'\n"
3735.15.17 by John Arbash Meinel
Update a bunch of tests that were using None rather than not displaying keys.
629
                             "  'aac' LeafNode\n"
3735.15.26 by John Arbash Meinel
Update the unmap_pages_in to also handle specific page-cache edge cases.
630
                             "      ('aac',) 'val'\n",
3735.11.10 by John Arbash Meinel
Change how _check_remap works so it doesn't have to load all keys.
631
                             chkmap._dump_tree())
3735.15.26 by John Arbash Meinel
Update the unmap_pages_in to also handle specific page-cache edge cases.
632
        root_key = chkmap._save()
3735.11.10 by John Arbash Meinel
Change how _check_remap works so it doesn't have to load all keys.
633
        # Save everything to the map, and start over
3735.15.26 by John Arbash Meinel
Update the unmap_pages_in to also handle specific page-cache edge cases.
634
        chkmap = CHKMap(store, root_key)
3735.11.10 by John Arbash Meinel
Change how _check_remap works so it doesn't have to load all keys.
635
        chkmap.map(('aad',), 'v')
636
        # At this point, the previous nodes should not be paged in, but the
637
        # newly added node would be
638
        self.assertIsInstance(chkmap._root_node._items['aaa'], tuple)
639
        self.assertIsInstance(chkmap._root_node._items['aab'], tuple)
640
        self.assertIsInstance(chkmap._root_node._items['aac'], tuple)
641
        self.assertIsInstance(chkmap._root_node._items['aad'], LeafNode)
642
        # Unmapping the new node will check the existing nodes to see if they
3735.15.26 by John Arbash Meinel
Update the unmap_pages_in to also handle specific page-cache edge cases.
643
        # would fit.
644
        # Clear the page cache so we ensure we have to read all the children
645
        chk_map._page_cache.clear()
3735.11.10 by John Arbash Meinel
Change how _check_remap works so it doesn't have to load all keys.
646
        chkmap.unmap(('aad',))
647
        self.assertIsInstance(chkmap._root_node._items['aaa'], LeafNode)
648
        self.assertIsInstance(chkmap._root_node._items['aab'], LeafNode)
3735.15.26 by John Arbash Meinel
Update the unmap_pages_in to also handle specific page-cache edge cases.
649
        self.assertIsInstance(chkmap._root_node._items['aac'], LeafNode)
650
3735.15.27 by John Arbash Meinel
Split out the unmap_pages_in tests and add one that shows if we
651
    def test_unmap_pages_in_from_page_cache(self):
652
        store = self.get_chk_bytes()
653
        chkmap = CHKMap(store, None)
654
        # Should fit 2 keys per LeafNode
655
        chkmap._root_node.set_maximum_size(30)
656
        chkmap.map(('aaa',), 'val')
657
        chkmap.map(('aab',), 'val')
658
        chkmap.map(('aac',), 'val')
659
        root_key = chkmap._save()
660
        # Save everything to the map, and start over
661
        chkmap = CHKMap(store, root_key)
662
        chkmap.map(('aad',), 'val')
663
        self.assertEqualDiff("'' InternalNode\n"
664
                             "  'aaa' LeafNode\n"
665
                             "      ('aaa',) 'val'\n"
666
                             "  'aab' LeafNode\n"
667
                             "      ('aab',) 'val'\n"
668
                             "  'aac' LeafNode\n"
669
                             "      ('aac',) 'val'\n"
670
                             "  'aad' LeafNode\n"
671
                             "      ('aad',) 'val'\n",
672
                             chkmap._dump_tree())
673
        # Save everything to the map, start over after _dump_tree
674
        chkmap = CHKMap(store, root_key)
675
        chkmap.map(('aad',), 'v')
676
        # At this point, the previous nodes should not be paged in, but the
677
        # newly added node would be
678
        self.assertIsInstance(chkmap._root_node._items['aaa'], tuple)
679
        self.assertIsInstance(chkmap._root_node._items['aab'], tuple)
680
        self.assertIsInstance(chkmap._root_node._items['aac'], tuple)
681
        self.assertIsInstance(chkmap._root_node._items['aad'], LeafNode)
682
        # Now clear the page cache, and only include 2 of the children in the
683
        # cache
684
        aab_key = chkmap._root_node._items['aab']
3735.15.26 by John Arbash Meinel
Update the unmap_pages_in to also handle specific page-cache edge cases.
685
        aab_bytes = chk_map._page_cache[aab_key]
3735.15.27 by John Arbash Meinel
Split out the unmap_pages_in tests and add one that shows if we
686
        aac_key = chkmap._root_node._items['aac']
3735.15.26 by John Arbash Meinel
Update the unmap_pages_in to also handle specific page-cache edge cases.
687
        aac_bytes = chk_map._page_cache[aac_key]
688
        chk_map._page_cache.clear()
689
        chk_map._page_cache[aab_key] = aab_bytes
690
        chk_map._page_cache[aac_key] = aac_bytes
3735.15.27 by John Arbash Meinel
Split out the unmap_pages_in tests and add one that shows if we
691
692
        # Unmapping the new node will check the nodes from the page cache
693
        # first, and not have to read in 'aaa'
3735.15.26 by John Arbash Meinel
Update the unmap_pages_in to also handle specific page-cache edge cases.
694
        chkmap.unmap(('aad',))
695
        self.assertIsInstance(chkmap._root_node._items['aaa'], tuple)
696
        self.assertIsInstance(chkmap._root_node._items['aab'], LeafNode)
697
        self.assertIsInstance(chkmap._root_node._items['aac'], LeafNode)
3735.11.10 by John Arbash Meinel
Change how _check_remap works so it doesn't have to load all keys.
698
3735.15.27 by John Arbash Meinel
Split out the unmap_pages_in tests and add one that shows if we
699
    def test_unmap_uses_existing_items(self):
700
        store = self.get_chk_bytes()
701
        chkmap = CHKMap(store, None)
702
        # Should fit 2 keys per LeafNode
703
        chkmap._root_node.set_maximum_size(30)
704
        chkmap.map(('aaa',), 'val')
705
        chkmap.map(('aab',), 'val')
706
        chkmap.map(('aac',), 'val')
707
        root_key = chkmap._save()
708
        # Save everything to the map, and start over
709
        chkmap = CHKMap(store, root_key)
710
        chkmap.map(('aad',), 'val')
711
        chkmap.map(('aae',), 'val')
712
        chkmap.map(('aaf',), 'val')
713
        # At this point, the previous nodes should not be paged in, but the
714
        # newly added node would be
715
        self.assertIsInstance(chkmap._root_node._items['aaa'], tuple)
716
        self.assertIsInstance(chkmap._root_node._items['aab'], tuple)
717
        self.assertIsInstance(chkmap._root_node._items['aac'], tuple)
718
        self.assertIsInstance(chkmap._root_node._items['aad'], LeafNode)
719
        self.assertIsInstance(chkmap._root_node._items['aae'], LeafNode)
720
        self.assertIsInstance(chkmap._root_node._items['aaf'], LeafNode)
721
722
        # Unmapping a new node will see the other nodes that are already in
723
        # memory, and not need to page in anything else
724
        chkmap.unmap(('aad',))
725
        self.assertIsInstance(chkmap._root_node._items['aaa'], tuple)
726
        self.assertIsInstance(chkmap._root_node._items['aab'], tuple)
727
        self.assertIsInstance(chkmap._root_node._items['aac'], tuple)
728
        self.assertIsInstance(chkmap._root_node._items['aae'], LeafNode)
729
        self.assertIsInstance(chkmap._root_node._items['aaf'], LeafNode)
730
3735.2.30 by Robert Collins
Start iter_changes between CHKMap instances.
731
    def test_iter_changes_empty_ab(self):
732
        # Asking for changes between an empty dict to a dict with keys returns
733
        # all the keys.
3735.2.31 by Robert Collins
CHKMap.iter_changes
734
        basis = self._get_map({}, maximum_size=10)
3735.2.30 by Robert Collins
Start iter_changes between CHKMap instances.
735
        target = self._get_map(
736
            {('a',): 'content here', ('b',): 'more content'},
3735.2.31 by Robert Collins
CHKMap.iter_changes
737
            chk_bytes=basis._store, maximum_size=10)
3735.2.30 by Robert Collins
Start iter_changes between CHKMap instances.
738
        self.assertEqual([(('a',), None, 'content here'),
3735.2.31 by Robert Collins
CHKMap.iter_changes
739
            (('b',), None, 'more content')],
740
            sorted(list(target.iter_changes(basis))))
741
742
    def test_iter_changes_ab_empty(self):
743
        # Asking for changes between a dict with keys to an empty dict returns
744
        # all the keys.
745
        basis = self._get_map({('a',): 'content here', ('b',): 'more content'},
746
            maximum_size=10)
747
        target = self._get_map({}, chk_bytes=basis._store, maximum_size=10)
748
        self.assertEqual([(('a',), 'content here', None),
749
            (('b',), 'more content', None)],
750
            sorted(list(target.iter_changes(basis))))
751
752
    def test_iter_changes_empty_empty_is_empty(self):
753
        basis = self._get_map({}, maximum_size=10)
754
        target = self._get_map({}, chk_bytes=basis._store, maximum_size=10)
755
        self.assertEqual([], sorted(list(target.iter_changes(basis))))
756
757
    def test_iter_changes_ab_ab_is_empty(self):
758
        basis = self._get_map({('a',): 'content here', ('b',): 'more content'},
759
            maximum_size=10)
760
        target = self._get_map(
761
            {('a',): 'content here', ('b',): 'more content'},
762
            chk_bytes=basis._store, maximum_size=10)
763
        self.assertEqual([], sorted(list(target.iter_changes(basis))))
764
765
    def test_iter_changes_ab_ab_nodes_not_loaded(self):
766
        basis = self._get_map({('a',): 'content here', ('b',): 'more content'},
767
            maximum_size=10)
768
        target = self._get_map(
769
            {('a',): 'content here', ('b',): 'more content'},
770
            chk_bytes=basis._store, maximum_size=10)
771
        list(target.iter_changes(basis))
772
        self.assertIsInstance(target._root_node, tuple)
773
        self.assertIsInstance(basis._root_node, tuple)
774
775
    def test_iter_changes_ab_ab_changed_values_shown(self):
776
        basis = self._get_map({('a',): 'content here', ('b',): 'more content'},
777
            maximum_size=10)
778
        target = self._get_map(
779
            {('a',): 'content here', ('b',): 'different content'},
780
            chk_bytes=basis._store, maximum_size=10)
781
        result = sorted(list(target.iter_changes(basis)))
782
        self.assertEqual([(('b',), 'more content', 'different content')],
783
            result)
784
785
    def test_iter_changes_mixed_node_length(self):
786
        # When one side has different node lengths than the other, common
787
        # but different keys still need to be show, and new-and-old included
788
        # appropriately.
789
        # aaa - common unaltered
790
        # aab - common altered
791
        # b - basis only
792
        # at - target only
3735.15.15 by John Arbash Meinel
Change child_child to use _dump_tree,
793
        # we expect:
3735.2.31 by Robert Collins
CHKMap.iter_changes
794
        # aaa to be not loaded (later test)
795
        # aab, b, at to be returned.
796
        # basis splits at byte 0,1,2, aaa is commonb is basis only
797
        basis_dict = {('aaa',): 'foo bar',
798
            ('aab',): 'common altered a', ('b',): 'foo bar b'}
799
        # target splits at byte 1,2, at is target only
800
        target_dict = {('aaa',): 'foo bar',
801
            ('aab',): 'common altered b', ('at',): 'foo bar t'}
802
        changes = [
803
            (('aab',), 'common altered a', 'common altered b'),
804
            (('at',), None, 'foo bar t'),
805
            (('b',), 'foo bar b', None),
806
            ]
807
        basis = self._get_map(basis_dict, maximum_size=10)
808
        target = self._get_map(target_dict, maximum_size=10,
809
            chk_bytes=basis._store)
810
        self.assertEqual(changes, sorted(list(target.iter_changes(basis))))
811
812
    def test_iter_changes_common_pages_not_loaded(self):
813
        # aaa - common unaltered
814
        # aab - common altered
815
        # b - basis only
816
        # at - target only
3735.15.15 by John Arbash Meinel
Change child_child to use _dump_tree,
817
        # we expect:
3735.2.31 by Robert Collins
CHKMap.iter_changes
818
        # aaa to be not loaded
819
        # aaa not to be in result.
820
        basis_dict = {('aaa',): 'foo bar',
821
            ('aab',): 'common altered a', ('b',): 'foo bar b'}
822
        # target splits at byte 1, at is target only
823
        target_dict = {('aaa',): 'foo bar',
824
            ('aab',): 'common altered b', ('at',): 'foo bar t'}
825
        basis = self._get_map(basis_dict, maximum_size=10)
826
        target = self._get_map(target_dict, maximum_size=10,
827
            chk_bytes=basis._store)
3735.2.32 by Robert Collins
Activate test for common node skipping. - 50 times performance improvement.
828
        basis_get = basis._store.get_record_stream
829
        def get_record_stream(keys, order, fulltext):
830
            if ('sha1:1adf7c0d1b9140ab5f33bb64c6275fa78b1580b7',) in keys:
831
                self.fail("'aaa' pointer was followed %r" % keys)
832
            return basis_get(keys, order, fulltext)
833
        basis._store.get_record_stream = get_record_stream
3735.2.31 by Robert Collins
CHKMap.iter_changes
834
        result = sorted(list(target.iter_changes(basis)))
835
        for change in result:
836
            if change[0] == ('aaa',):
837
                self.fail("Found unexpected change: %s" % change)
838
839
    def test_iter_changes_unchanged_keys_in_multi_key_leafs_ignored(self):
840
        # Within a leaf there are no hash's to exclude keys, make sure multi
841
        # value leaf nodes are handled well.
842
        basis_dict = {('aaa',): 'foo bar',
843
            ('aab',): 'common altered a', ('b',): 'foo bar b'}
844
        target_dict = {('aaa',): 'foo bar',
845
            ('aab',): 'common altered b', ('at',): 'foo bar t'}
846
        changes = [
847
            (('aab',), 'common altered a', 'common altered b'),
848
            (('at',), None, 'foo bar t'),
849
            (('b',), 'foo bar b', None),
850
            ]
851
        basis = self._get_map(basis_dict)
852
        target = self._get_map(target_dict, chk_bytes=basis._store)
853
        self.assertEqual(changes, sorted(list(target.iter_changes(basis))))
3735.2.30 by Robert Collins
Start iter_changes between CHKMap instances.
854
3735.2.8 by Robert Collins
New chk_map module for use in tree based inventory storage.
855
    def test_iteritems_empty(self):
856
        chk_bytes = self.get_chk_bytes()
857
        root_key = CHKMap.from_dict(chk_bytes, {})
858
        chkmap = CHKMap(chk_bytes, root_key)
859
        self.assertEqual([], list(chkmap.iteritems()))
860
861
    def test_iteritems_two_items(self):
862
        chk_bytes = self.get_chk_bytes()
863
        root_key = CHKMap.from_dict(chk_bytes,
864
            {"a":"content here", "b":"more content"})
865
        chkmap = CHKMap(chk_bytes, root_key)
3735.2.24 by Robert Collins
test_chk_map tests all passing.
866
        self.assertEqual([(("a",), "content here"), (("b",), "more content")],
3735.2.8 by Robert Collins
New chk_map module for use in tree based inventory storage.
867
            sorted(list(chkmap.iteritems())))
868
3735.2.9 by Robert Collins
Get a working chk_map using inventory implementation bootstrapped.
869
    def test_iteritems_selected_one_of_two_items(self):
3735.2.24 by Robert Collins
test_chk_map tests all passing.
870
        chkmap = self._get_map( {("a",):"content here", ("b",):"more content"})
871
        self.assertEqual({("a",): "content here"},
872
            self.to_dict(chkmap, [("a",)]))
3735.2.9 by Robert Collins
Get a working chk_map using inventory implementation bootstrapped.
873
3735.2.43 by Robert Collins
Teach CHKMap how to iter items in 2-tuple keyspaces.
874
    def test_iteritems_keys_prefixed_by_2_width_nodes(self):
875
        chkmap = self._get_map(
876
            {("a","a"):"content here", ("a", "b",):"more content",
877
             ("b", ""): 'boring content'},
878
            maximum_size=10, key_width=2)
879
        self.assertEqual(
880
            {("a", "a"): "content here", ("a", "b"): 'more content'},
881
            self.to_dict(chkmap, [("a",)]))
882
3735.2.115 by John Arbash Meinel
Fix the root cause of why _iter_nodes was not handling key_filter.
883
    def test_iteritems_keys_prefixed_by_2_width_nodes_hashed(self):
884
        search_key_func = chk_map.search_key_registry.get('hash-16-way')
885
        self.assertEqual('E8B7BE43\x00E8B7BE43', search_key_func(('a', 'a')))
886
        self.assertEqual('E8B7BE43\x0071BEEFF9', search_key_func(('a', 'b')))
887
        self.assertEqual('71BEEFF9\x0000000000', search_key_func(('b', '')))
888
        chkmap = self._get_map(
889
            {("a","a"):"content here", ("a", "b",):"more content",
890
             ("b", ""): 'boring content'},
891
            maximum_size=10, key_width=2, search_key_func=search_key_func)
892
        self.assertEqual(
893
            {("a", "a"): "content here", ("a", "b"): 'more content'},
894
            self.to_dict(chkmap, [("a",)]))
895
3735.2.43 by Robert Collins
Teach CHKMap how to iter items in 2-tuple keyspaces.
896
    def test_iteritems_keys_prefixed_by_2_width_one_leaf(self):
897
        chkmap = self._get_map(
898
            {("a","a"):"content here", ("a", "b",):"more content",
899
             ("b", ""): 'boring content'}, key_width=2)
900
        self.assertEqual(
901
            {("a", "a"): "content here", ("a", "b"): 'more content'},
902
            self.to_dict(chkmap, [("a",)]))
903
3735.2.17 by Robert Collins
Cache node length to avoid full iteration on __len__ calls.
904
    def test___len__empty(self):
905
        chkmap = self._get_map({})
906
        self.assertEqual(0, len(chkmap))
907
908
    def test___len__2(self):
3735.17.7 by John Arbash Meinel
Get all of test_chk_map passing.
909
        chkmap = self._get_map({("foo",):"bar", ("gam",):"quux"})
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
910
        self.assertEqual(2, len(chkmap))
911
3735.2.21 by Robert Collins
BROKEN: multi level CHKMap tries, unfinished, subsystem in flux.
912
    def test_max_size_100_bytes_new(self):
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
913
        # When there is a 100 byte upper node limit, a tree is formed.
3735.2.21 by Robert Collins
BROKEN: multi level CHKMap tries, unfinished, subsystem in flux.
914
        chkmap = self._get_map({("k1"*50,):"v1", ("k2"*50,):"v2"}, maximum_size=100)
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
915
        # We expect three nodes:
916
        # A root, with two children, and with two key prefixes - k1 to one, and
917
        # k2 to the other as our node splitting is only just being developed.
918
        # The maximum size should be embedded
919
        chkmap._ensure_root()
920
        self.assertEqual(100, chkmap._root_node.maximum_size)
3735.2.21 by Robert Collins
BROKEN: multi level CHKMap tries, unfinished, subsystem in flux.
921
        self.assertEqual(1, chkmap._root_node._key_width)
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
922
        # There should be two child nodes, and prefix of 2(bytes):
3735.2.21 by Robert Collins
BROKEN: multi level CHKMap tries, unfinished, subsystem in flux.
923
        self.assertEqual(2, len(chkmap._root_node._items))
3735.15.13 by John Arbash Meinel
Change the term 'lookup' to the term 'search', as it is closer to what Robert envisioned.
924
        self.assertEqual("k", chkmap._root_node._compute_search_prefix())
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
925
        # The actual nodes pointed at will change as serialisers change; so
926
        # here we test that the key prefix is correct; then load the nodes and
927
        # check they have the right pointed at key; whether they have the
928
        # pointed at value inline or not is also unrelated to this test so we
3735.2.21 by Robert Collins
BROKEN: multi level CHKMap tries, unfinished, subsystem in flux.
929
        # don't check that in detail - rather we just check the aggregate
930
        # value.
931
        nodes = sorted(chkmap._root_node._items.items())
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
932
        ptr1 = nodes[0]
933
        ptr2 = nodes[1]
934
        self.assertEqual('k1', ptr1[0])
935
        self.assertEqual('k2', ptr2[0])
3735.16.2 by John Arbash Meinel
Start passing around the search_key_func in more places.
936
        node1 = chk_map._deserialise(chkmap._read_bytes(ptr1[1]), ptr1[1], None)
3735.2.21 by Robert Collins
BROKEN: multi level CHKMap tries, unfinished, subsystem in flux.
937
        self.assertIsInstance(node1, LeafNode)
938
        self.assertEqual(1, len(node1))
939
        self.assertEqual({('k1'*50,): 'v1'}, self.to_dict(node1, chkmap._store))
3735.16.2 by John Arbash Meinel
Start passing around the search_key_func in more places.
940
        node2 = chk_map._deserialise(chkmap._read_bytes(ptr2[1]), ptr2[1], None)
3735.2.21 by Robert Collins
BROKEN: multi level CHKMap tries, unfinished, subsystem in flux.
941
        self.assertIsInstance(node2, LeafNode)
942
        self.assertEqual(1, len(node2))
943
        self.assertEqual({('k2'*50,): 'v2'}, self.to_dict(node2, chkmap._store))
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
944
        # Having checked we have a good structure, check that the content is
945
        # still accessible.
946
        self.assertEqual(2, len(chkmap))
3735.2.21 by Robert Collins
BROKEN: multi level CHKMap tries, unfinished, subsystem in flux.
947
        self.assertEqual({("k1"*50,): "v1", ("k2"*50,): "v2"},
948
            self.to_dict(chkmap))
949
950
    def test_init_root_is_LeafNode_new(self):
951
        chk_bytes = self.get_chk_bytes()
952
        chkmap = CHKMap(chk_bytes, None)
953
        self.assertIsInstance(chkmap._root_node, LeafNode)
954
        self.assertEqual({}, self.to_dict(chkmap))
955
        self.assertEqual(0, len(chkmap))
956
957
    def test_init_and_save_new(self):
958
        chk_bytes = self.get_chk_bytes()
959
        chkmap = CHKMap(chk_bytes, None)
960
        key = chkmap._save()
961
        leaf_node = LeafNode()
962
        self.assertEqual([key], leaf_node.serialise(chk_bytes))
963
964
    def test_map_first_item_new(self):
965
        chk_bytes = self.get_chk_bytes()
966
        chkmap = CHKMap(chk_bytes, None)
967
        chkmap.map(("foo,",), "bar")
968
        self.assertEqual({('foo,',): 'bar'}, self.to_dict(chkmap))
969
        self.assertEqual(1, len(chkmap))
970
        key = chkmap._save()
971
        leaf_node = LeafNode()
972
        leaf_node.map(chk_bytes, ("foo,",), "bar")
973
        self.assertEqual([key], leaf_node.serialise(chk_bytes))
974
975
    def test_unmap_last_item_root_is_leaf_new(self):
976
        chkmap = self._get_map({("k1"*50,): "v1", ("k2"*50,): "v2"})
977
        chkmap.unmap(("k1"*50,))
978
        chkmap.unmap(("k2"*50,))
979
        self.assertEqual(0, len(chkmap))
980
        self.assertEqual({}, self.to_dict(chkmap))
981
        key = chkmap._save()
982
        leaf_node = LeafNode()
983
        self.assertEqual([key], leaf_node.serialise(chkmap._store))
3735.2.17 by Robert Collins
Cache node length to avoid full iteration on __len__ calls.
984
3735.9.2 by John Arbash Meinel
Add a _dump_tree helper that assists in debugging what is going on.
985
    def test__dump_tree(self):
986
        chkmap = self._get_map({("aaa",): "value1", ("aab",): "value2",
987
                                ("bbb",): "value3",},
3735.17.7 by John Arbash Meinel
Get all of test_chk_map passing.
988
                               maximum_size=15)
3735.15.16 by John Arbash Meinel
Properly fix up the dump_tree tests, we now suppress the keys by default.
989
        self.assertEqualDiff("'' InternalNode\n"
990
                             "  'a' InternalNode\n"
991
                             "    'aaa' LeafNode\n"
992
                             "      ('aaa',) 'value1'\n"
993
                             "    'aab' LeafNode\n"
994
                             "      ('aab',) 'value2'\n"
995
                             "  'b' LeafNode\n"
996
                             "      ('bbb',) 'value3'\n",
3735.15.18 by John Arbash Meinel
Fix up the k1_from_k1 test.
997
                             chkmap._dump_tree())
3735.15.16 by John Arbash Meinel
Properly fix up the dump_tree tests, we now suppress the keys by default.
998
        self.assertEqualDiff("'' InternalNode\n"
999
                             "  'a' InternalNode\n"
1000
                             "    'aaa' LeafNode\n"
1001
                             "      ('aaa',) 'value1'\n"
1002
                             "    'aab' LeafNode\n"
1003
                             "      ('aab',) 'value2'\n"
1004
                             "  'b' LeafNode\n"
1005
                             "      ('bbb',) 'value3'\n",
1006
                             chkmap._dump_tree())
1007
        self.assertEqualDiff(
3735.17.7 by John Arbash Meinel
Get all of test_chk_map passing.
1008
            "'' InternalNode sha1:0690d471eb0a624f359797d0ee4672bd68f4e236\n"
1009
            "  'a' InternalNode sha1:1514c35503da9418d8fd90c1bed553077cb53673\n"
1010
            "    'aaa' LeafNode sha1:4cc5970454d40b4ce297a7f13ddb76f63b88fefb\n"
3735.15.16 by John Arbash Meinel
Properly fix up the dump_tree tests, we now suppress the keys by default.
1011
            "      ('aaa',) 'value1'\n"
3735.17.7 by John Arbash Meinel
Get all of test_chk_map passing.
1012
            "    'aab' LeafNode sha1:1d68bc90914ef8a3edbcc8bb28b00cb4fea4b5e2\n"
3735.15.16 by John Arbash Meinel
Properly fix up the dump_tree tests, we now suppress the keys by default.
1013
            "      ('aab',) 'value2'\n"
3735.17.7 by John Arbash Meinel
Get all of test_chk_map passing.
1014
            "  'b' LeafNode sha1:3686831435b5596515353364eab0399dc45d49e7\n"
3735.15.16 by John Arbash Meinel
Properly fix up the dump_tree tests, we now suppress the keys by default.
1015
            "      ('bbb',) 'value3'\n",
1016
            chkmap._dump_tree(include_keys=True))
3735.9.4 by John Arbash Meinel
Some small cleanups, and fix _dump_tree to handle in-progress nodes.
1017
1018
    def test__dump_tree_in_progress(self):
1019
        chkmap = self._get_map({("aaa",): "value1", ("aab",): "value2"},
1020
                               maximum_size=10)
1021
        chkmap.map(('bbb',), 'value3')
3735.15.16 by John Arbash Meinel
Properly fix up the dump_tree tests, we now suppress the keys by default.
1022
        self.assertEqualDiff("'' InternalNode\n"
1023
                             "  'a' InternalNode\n"
1024
                             "    'aaa' LeafNode\n"
1025
                             "      ('aaa',) 'value1'\n"
1026
                             "    'aab' LeafNode\n"
1027
                             "      ('aab',) 'value2'\n"
1028
                             "  'b' LeafNode\n"
1029
                             "      ('bbb',) 'value3'\n",
1030
                             chkmap._dump_tree())
1031
        # For things that are updated by adding 'bbb', we don't have a sha key
1032
        # for them yet, so they are listed as None
1033
        self.assertEqualDiff(
1034
            "'' InternalNode None\n"
3735.17.7 by John Arbash Meinel
Get all of test_chk_map passing.
1035
            "  'a' InternalNode sha1:6b0d881dd739a66f733c178b24da64395edfaafd\n"
1036
            "    'aaa' LeafNode sha1:40b39a08d895babce17b20ae5f62d187eaa4f63a\n"
3735.15.16 by John Arbash Meinel
Properly fix up the dump_tree tests, we now suppress the keys by default.
1037
            "      ('aaa',) 'value1'\n"
3735.17.7 by John Arbash Meinel
Get all of test_chk_map passing.
1038
            "    'aab' LeafNode sha1:ad1dc7c4e801302c95bf1ba7b20bc45e548cd51a\n"
3735.15.16 by John Arbash Meinel
Properly fix up the dump_tree tests, we now suppress the keys by default.
1039
            "      ('aab',) 'value2'\n"
1040
            "  'b' LeafNode None\n"
1041
            "      ('bbb',) 'value3'\n",
1042
            chkmap._dump_tree(include_keys=True))
3735.9.2 by John Arbash Meinel
Add a _dump_tree helper that assists in debugging what is going on.
1043
3735.2.8 by Robert Collins
New chk_map module for use in tree based inventory storage.
1044
3735.16.9 by John Arbash Meinel
Handle collisions.
1045
def _search_key_single(key):
1046
    """A search key function that maps all nodes to the same value"""
1047
    return 'value'
1048
3735.16.1 by John Arbash Meinel
(broken) Start tracking down more code that needs to pass around the 'search_key_func'
1049
def _test_search_key(key):
1050
    return 'test:' + '\x00'.join(key)
1051
1052
1053
class TestMapSearchKeys(TestCaseWithStore):
1054
1055
    def test_default_chk_map_uses_flat_search_key(self):
1056
        chkmap = chk_map.CHKMap(self.get_chk_bytes(), None)
1057
        self.assertEqual('1',
1058
                         chkmap._search_key_func(('1',)))
1059
        self.assertEqual('1\x002',
1060
                         chkmap._search_key_func(('1', '2')))
1061
        self.assertEqual('1\x002\x003',
1062
                         chkmap._search_key_func(('1', '2', '3')))
1063
1064
    def test_search_key_is_passed_to_root_node(self):
1065
        chkmap = chk_map.CHKMap(self.get_chk_bytes(), None,
1066
                                search_key_func=_test_search_key)
1067
        self.assertIs(_test_search_key, chkmap._search_key_func)
1068
        self.assertEqual('test:1\x002\x003',
1069
                         chkmap._search_key_func(('1', '2', '3')))
1070
        self.assertEqual('test:1\x002\x003',
1071
                         chkmap._root_node._search_key(('1', '2', '3')))
1072
1073
    def test_search_key_passed_via__ensure_root(self):
1074
        chk_bytes = self.get_chk_bytes()
1075
        chkmap = chk_map.CHKMap(chk_bytes, None,
1076
                                search_key_func=_test_search_key)
1077
        root_key = chkmap._save()
1078
        chkmap = chk_map.CHKMap(chk_bytes, root_key,
1079
                                search_key_func=_test_search_key)
1080
        chkmap._ensure_root()
1081
        self.assertEqual('test:1\x002\x003',
1082
                         chkmap._root_node._search_key(('1', '2', '3')))
1083
1084
    def test_search_key_with_internal_node(self):
1085
        chk_bytes = self.get_chk_bytes()
1086
        chkmap = chk_map.CHKMap(chk_bytes, None,
1087
                                search_key_func=_test_search_key)
1088
        chkmap._root_node.set_maximum_size(10)
1089
        chkmap.map(('1',), 'foo')
1090
        chkmap.map(('2',), 'bar')
1091
        chkmap.map(('3',), 'baz')
1092
        self.assertEqualDiff("'' InternalNode\n"
1093
                             "  'test:1' LeafNode\n"
1094
                             "      ('1',) 'foo'\n"
1095
                             "  'test:2' LeafNode\n"
1096
                             "      ('2',) 'bar'\n"
1097
                             "  'test:3' LeafNode\n"
1098
                             "      ('3',) 'baz'\n"
1099
                             , chkmap._dump_tree())
1100
        root_key = chkmap._save()
1101
        chkmap = chk_map.CHKMap(chk_bytes, root_key,
1102
                                search_key_func=_test_search_key)
1103
        self.assertEqualDiff("'' InternalNode\n"
1104
                             "  'test:1' LeafNode\n"
1105
                             "      ('1',) 'foo'\n"
1106
                             "  'test:2' LeafNode\n"
1107
                             "      ('2',) 'bar'\n"
1108
                             "  'test:3' LeafNode\n"
1109
                             "      ('3',) 'baz'\n"
1110
                             , chkmap._dump_tree())
1111
3735.16.4 by John Arbash Meinel
Add some tests that we can use the search keys as proper mappings.
1112
    def test_search_key_16(self):
1113
        chk_bytes = self.get_chk_bytes()
1114
        chkmap = chk_map.CHKMap(chk_bytes, None,
1115
                                search_key_func=chk_map._search_key_16)
1116
        chkmap._root_node.set_maximum_size(10)
1117
        chkmap.map(('1',), 'foo')
1118
        chkmap.map(('2',), 'bar')
1119
        chkmap.map(('3',), 'baz')
1120
        self.assertEqualDiff("'' InternalNode\n"
1121
                             "  '1' LeafNode\n"
1122
                             "      ('2',) 'bar'\n"
1123
                             "  '6' LeafNode\n"
1124
                             "      ('3',) 'baz'\n"
3735.2.87 by Vincent Ladeuil
Same player shoots again, zlib.crc32, we'll get you.
1125
                             "  '8' LeafNode\n"
3735.16.4 by John Arbash Meinel
Add some tests that we can use the search keys as proper mappings.
1126
                             "      ('1',) 'foo'\n"
1127
                             , chkmap._dump_tree())
3735.16.5 by John Arbash Meinel
Add tests that we can lookup things after being serialized.
1128
        root_key = chkmap._save()
1129
        chkmap = chk_map.CHKMap(chk_bytes, root_key,
1130
                                search_key_func=chk_map._search_key_16)
1131
        # We can get the values back correctly
1132
        self.assertEqual([(('1',), 'foo')],
1133
                         list(chkmap.iteritems([('1',)])))
1134
        self.assertEqualDiff("'' InternalNode\n"
1135
                             "  '1' LeafNode\n"
1136
                             "      ('2',) 'bar'\n"
1137
                             "  '6' LeafNode\n"
1138
                             "      ('3',) 'baz'\n"
3735.2.87 by Vincent Ladeuil
Same player shoots again, zlib.crc32, we'll get you.
1139
                             "  '8' LeafNode\n"
3735.16.5 by John Arbash Meinel
Add tests that we can lookup things after being serialized.
1140
                             "      ('1',) 'foo'\n"
1141
                             , chkmap._dump_tree())
3735.16.4 by John Arbash Meinel
Add some tests that we can use the search keys as proper mappings.
1142
1143
    def test_search_key_255(self):
1144
        chk_bytes = self.get_chk_bytes()
1145
        chkmap = chk_map.CHKMap(chk_bytes, None,
1146
                                search_key_func=chk_map._search_key_255)
1147
        chkmap._root_node.set_maximum_size(10)
1148
        chkmap.map(('1',), 'foo')
1149
        chkmap.map(('2',), 'bar')
1150
        chkmap.map(('3',), 'baz')
1151
        self.assertEqualDiff("'' InternalNode\n"
1152
                             "  '\\x1a' LeafNode\n"
1153
                             "      ('2',) 'bar'\n"
1154
                             "  'm' LeafNode\n"
1155
                             "      ('3',) 'baz'\n"
1156
                             "  '\\x83' LeafNode\n"
1157
                             "      ('1',) 'foo'\n"
1158
                             , chkmap._dump_tree())
3735.16.5 by John Arbash Meinel
Add tests that we can lookup things after being serialized.
1159
        root_key = chkmap._save()
1160
        chkmap = chk_map.CHKMap(chk_bytes, root_key,
1161
                                search_key_func=chk_map._search_key_255)
1162
        # We can get the values back correctly
1163
        self.assertEqual([(('1',), 'foo')],
1164
                         list(chkmap.iteritems([('1',)])))
1165
        self.assertEqualDiff("'' InternalNode\n"
1166
                             "  '\\x1a' LeafNode\n"
1167
                             "      ('2',) 'bar'\n"
1168
                             "  'm' LeafNode\n"
1169
                             "      ('3',) 'baz'\n"
1170
                             "  '\\x83' LeafNode\n"
1171
                             "      ('1',) 'foo'\n"
1172
                             , chkmap._dump_tree())
3735.16.4 by John Arbash Meinel
Add some tests that we can use the search keys as proper mappings.
1173
3735.16.9 by John Arbash Meinel
Handle collisions.
1174
    def test_search_key_collisions(self):
1175
        chkmap = chk_map.CHKMap(self.get_chk_bytes(), None,
1176
                                search_key_func=_search_key_single)
1177
        # The node will want to expand, but it cannot, because it knows that
1178
        # all the keys must map to this node
1179
        chkmap._root_node.set_maximum_size(20)
1180
        chkmap.map(('1',), 'foo')
1181
        chkmap.map(('2',), 'bar')
1182
        chkmap.map(('3',), 'baz')
1183
        self.assertEqualDiff("'' LeafNode\n"
1184
                             "      ('1',) 'foo'\n"
1185
                             "      ('2',) 'bar'\n"
1186
                             "      ('3',) 'baz'\n"
1187
                             , chkmap._dump_tree())
1188
3735.16.1 by John Arbash Meinel
(broken) Start tracking down more code that needs to pass around the 'search_key_func'
1189
3735.16.3 by John Arbash Meinel
Add functions for _search_key_16 and _search_key_255 and some basic tests for them.
1190
class TestSearchKeyFuncs(tests.TestCase):
1191
1192
    def assertSearchKey16(self, expected, key):
1193
        self.assertEqual(expected, chk_map._search_key_16(key))
1194
1195
    def assertSearchKey255(self, expected, key):
1196
        actual = chk_map._search_key_255(key)
1197
        self.assertEqual(expected, actual, 'actual: %r' % (actual,))
1198
1199
    def test_simple_16(self):
3735.2.91 by John Arbash Meinel
Update the test suite for the new crc32 code.
1200
        self.assertSearchKey16('8C736521', ('foo',))
1201
        self.assertSearchKey16('8C736521\x008C736521', ('foo', 'foo'))
1202
        self.assertSearchKey16('8C736521\x0076FF8CAA', ('foo', 'bar'))
1203
        self.assertSearchKey16('ED82CD11', ('abcd',))
3735.16.3 by John Arbash Meinel
Add functions for _search_key_16 and _search_key_255 and some basic tests for them.
1204
1205
    def test_simple_255(self):
1206
        self.assertSearchKey255('\x8cse!', ('foo',))
1207
        self.assertSearchKey255('\x8cse!\x00\x8cse!', ('foo', 'foo'))
1208
        self.assertSearchKey255('\x8cse!\x00v\xff\x8c\xaa', ('foo', 'bar'))
1209
        # The standard mapping for these would include '\n', so it should be
1210
        # mapped to '_'
1211
        self.assertSearchKey255('\xfdm\x93_\x00P_\x1bL', ('<', 'V'))
1212
1213
    def test_255_does_not_include_newline(self):
1214
        # When mapping via _search_key_255, we should never have the '\n'
1215
        # character, but all other 255 values should be present
1216
        chars_used = set()
1217
        for char_in in range(256):
1218
            search_key = chk_map._search_key_255((chr(char_in),))
1219
            chars_used.update(search_key)
1220
        all_chars = set([chr(x) for x in range(256)])
1221
        unused_chars = all_chars.symmetric_difference(chars_used)
1222
        self.assertEqual(set('\n'), unused_chars)
1223
1224
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
1225
class TestLeafNode(TestCaseWithStore):
1226
1227
    def test_current_size_empty(self):
1228
        node = LeafNode()
3735.15.23 by John Arbash Meinel
Fix up the LeafNode direct tests, and add some tests with serialized
1229
        self.assertEqual(16, node._current_size())
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
1230
1231
    def test_current_size_size_changed(self):
1232
        node = LeafNode()
1233
        node.set_maximum_size(10)
3735.15.23 by John Arbash Meinel
Fix up the LeafNode direct tests, and add some tests with serialized
1234
        self.assertEqual(17, node._current_size())
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
1235
1236
    def test_current_size_width_changed(self):
1237
        node = LeafNode()
1238
        node._key_width = 10
3735.15.23 by John Arbash Meinel
Fix up the LeafNode direct tests, and add some tests with serialized
1239
        self.assertEqual(17, node._current_size())
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
1240
1241
    def test_current_size_items(self):
1242
        node = LeafNode()
1243
        base_size = node._current_size()
3735.2.24 by Robert Collins
test_chk_map tests all passing.
1244
        node.map(None, ("foo bar",), "baz")
3735.17.7 by John Arbash Meinel
Get all of test_chk_map passing.
1245
        self.assertEqual(base_size + 14, node._current_size())
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
1246
1247
    def test_deserialise_empty(self):
3735.15.23 by John Arbash Meinel
Fix up the LeafNode direct tests, and add some tests with serialized
1248
        node = LeafNode.deserialise("chkleaf:\n10\n1\n0\n\n", ("sha1:1234",))
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
1249
        self.assertEqual(0, len(node))
1250
        self.assertEqual(10, node.maximum_size)
1251
        self.assertEqual(("sha1:1234",), node.key())
3735.15.24 by John Arbash Meinel
We were incorrectly using '' when we wanted to use None as the common prefix.
1252
        self.assertIs(None, node._search_prefix)
1253
        self.assertIs(None, node._common_serialised_prefix)
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
1254
1255
    def test_deserialise_items(self):
1256
        node = LeafNode.deserialise(
3735.17.7 by John Arbash Meinel
Get all of test_chk_map passing.
1257
            "chkleaf:\n0\n1\n2\n\nfoo bar\x001\nbaz\nquux\x001\nblarh\n",
1258
            ("sha1:1234",))
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
1259
        self.assertEqual(2, len(node))
1260
        self.assertEqual([(("foo bar",), "baz"), (("quux",), "blarh")],
3735.2.24 by Robert Collins
test_chk_map tests all passing.
1261
            sorted(node.iteritems(None)))
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
1262
3735.2.25 by Robert Collins
CHKInventory core tests passing.
1263
    def test_deserialise_item_with_null_width_1(self):
1264
        node = LeafNode.deserialise(
3735.17.7 by John Arbash Meinel
Get all of test_chk_map passing.
1265
            "chkleaf:\n0\n1\n2\n\nfoo\x001\nbar\x00baz\nquux\x001\nblarh\n",
3735.2.25 by Robert Collins
CHKInventory core tests passing.
1266
            ("sha1:1234",))
1267
        self.assertEqual(2, len(node))
1268
        self.assertEqual([(("foo",), "bar\x00baz"), (("quux",), "blarh")],
1269
            sorted(node.iteritems(None)))
1270
1271
    def test_deserialise_item_with_null_width_2(self):
1272
        node = LeafNode.deserialise(
3735.17.7 by John Arbash Meinel
Get all of test_chk_map passing.
1273
            "chkleaf:\n0\n2\n2\n\nfoo\x001\x001\nbar\x00baz\n"
1274
            "quux\x00\x001\nblarh\n",
3735.2.25 by Robert Collins
CHKInventory core tests passing.
1275
            ("sha1:1234",))
1276
        self.assertEqual(2, len(node))
1277
        self.assertEqual([(("foo", "1"), "bar\x00baz"), (("quux", ""), "blarh")],
1278
            sorted(node.iteritems(None)))
1279
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
1280
    def test_iteritems_selected_one_of_two_items(self):
1281
        node = LeafNode.deserialise(
3735.17.7 by John Arbash Meinel
Get all of test_chk_map passing.
1282
            "chkleaf:\n0\n1\n2\n\nfoo bar\x001\nbaz\nquux\x001\nblarh\n",
1283
            ("sha1:1234",))
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
1284
        self.assertEqual(2, len(node))
1285
        self.assertEqual([(("quux",), "blarh")],
3735.2.24 by Robert Collins
test_chk_map tests all passing.
1286
            sorted(node.iteritems(None, [("quux",), ("qaz",)])))
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
1287
3735.15.23 by John Arbash Meinel
Fix up the LeafNode direct tests, and add some tests with serialized
1288
    def test_deserialise_item_with_common_prefix(self):
1289
        node = LeafNode.deserialise(
3735.17.7 by John Arbash Meinel
Get all of test_chk_map passing.
1290
            "chkleaf:\n0\n2\n2\nfoo\x00\n1\x001\nbar\x00baz\n2\x001\nblarh\n",
3735.15.23 by John Arbash Meinel
Fix up the LeafNode direct tests, and add some tests with serialized
1291
            ("sha1:1234",))
1292
        self.assertEqual(2, len(node))
1293
        self.assertEqual([(("foo", "1"), "bar\x00baz"), (("foo", "2"), "blarh")],
1294
            sorted(node.iteritems(None)))
3735.2.131 by John Arbash Meinel
Only compute LeafNode._search_prefix when we will use it.
1295
        self.assertIs(chk_map._unknown, node._search_prefix)
3735.15.24 by John Arbash Meinel
We were incorrectly using '' when we wanted to use None as the common prefix.
1296
        self.assertEqual('foo\x00', node._common_serialised_prefix)
3735.15.23 by John Arbash Meinel
Fix up the LeafNode direct tests, and add some tests with serialized
1297
3735.17.7 by John Arbash Meinel
Get all of test_chk_map passing.
1298
    def test_deserialise_multi_line(self):
1299
        node = LeafNode.deserialise(
1300
            "chkleaf:\n0\n2\n2\nfoo\x00\n1\x002\nbar\nbaz\n2\x002\nblarh\n\n",
1301
            ("sha1:1234",))
1302
        self.assertEqual(2, len(node))
1303
        self.assertEqual([(("foo", "1"), "bar\nbaz"),
1304
                          (("foo", "2"), "blarh\n"),
1305
                         ], sorted(node.iteritems(None)))
3735.2.131 by John Arbash Meinel
Only compute LeafNode._search_prefix when we will use it.
1306
        self.assertIs(chk_map._unknown, node._search_prefix)
3735.17.7 by John Arbash Meinel
Get all of test_chk_map passing.
1307
        self.assertEqual('foo\x00', node._common_serialised_prefix)
1308
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
1309
    def test_key_new(self):
1310
        node = LeafNode()
1311
        self.assertEqual(None, node.key())
1312
1313
    def test_key_after_map(self):
3735.15.23 by John Arbash Meinel
Fix up the LeafNode direct tests, and add some tests with serialized
1314
        node = LeafNode.deserialise("chkleaf:\n10\n1\n0\n\n", ("sha1:1234",))
3735.2.24 by Robert Collins
test_chk_map tests all passing.
1315
        node.map(None, ("foo bar",), "baz quux")
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
1316
        self.assertEqual(None, node.key())
1317
1318
    def test_key_after_unmap(self):
1319
        node = LeafNode.deserialise(
3735.17.7 by John Arbash Meinel
Get all of test_chk_map passing.
1320
            "chkleaf:\n0\n1\n2\n\nfoo bar\x001\nbaz\nquux\x001\nblarh\n",
1321
            ("sha1:1234",))
3735.2.24 by Robert Collins
test_chk_map tests all passing.
1322
        node.unmap(None, ("foo bar",))
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
1323
        self.assertEqual(None, node.key())
1324
3735.2.21 by Robert Collins
BROKEN: multi level CHKMap tries, unfinished, subsystem in flux.
1325
    def test_map_exceeding_max_size_only_entry_new(self):
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
1326
        node = LeafNode()
1327
        node.set_maximum_size(10)
3735.2.21 by Robert Collins
BROKEN: multi level CHKMap tries, unfinished, subsystem in flux.
1328
        result = node.map(None, ("foo bar",), "baz quux")
1329
        self.assertEqual(("foo bar", [("", node)]), result)
1330
        self.assertTrue(10 < node._current_size())
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
1331
3735.2.21 by Robert Collins
BROKEN: multi level CHKMap tries, unfinished, subsystem in flux.
1332
    def test_map_exceeding_max_size_second_entry_early_difference_new(self):
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
1333
        node = LeafNode()
1334
        node.set_maximum_size(10)
3735.2.21 by Robert Collins
BROKEN: multi level CHKMap tries, unfinished, subsystem in flux.
1335
        node.map(None, ("foo bar",), "baz quux")
1336
        prefix, result = list(node.map(None, ("blue",), "red"))
1337
        self.assertEqual("", prefix)
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
1338
        self.assertEqual(2, len(result))
3735.2.21 by Robert Collins
BROKEN: multi level CHKMap tries, unfinished, subsystem in flux.
1339
        split_chars = set([result[0][0], result[1][0]])
1340
        self.assertEqual(set(["f", "b"]), split_chars)
1341
        nodes = dict(result)
1342
        node = nodes["f"]
1343
        self.assertEqual({("foo bar",): "baz quux"}, self.to_dict(node, None))
1344
        self.assertEqual(10, node.maximum_size)
1345
        self.assertEqual(1, node._key_width)
1346
        node = nodes["b"]
1347
        self.assertEqual({("blue",): "red"}, self.to_dict(node, None))
1348
        self.assertEqual(10, node.maximum_size)
1349
        self.assertEqual(1, node._key_width)
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
1350
1351
    def test_map_first(self):
1352
        node = LeafNode()
3735.2.24 by Robert Collins
test_chk_map tests all passing.
1353
        result = node.map(None, ("foo bar",), "baz quux")
1354
        self.assertEqual(("foo bar", [("", node)]), result)
1355
        self.assertEqual({("foo bar",):"baz quux"}, self.to_dict(node, None))
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
1356
        self.assertEqual(1, len(node))
1357
1358
    def test_map_second(self):
1359
        node = LeafNode()
3735.2.24 by Robert Collins
test_chk_map tests all passing.
1360
        node.map(None, ("foo bar",), "baz quux")
1361
        result = node.map(None, ("bingo",), "bango")
1362
        self.assertEqual(("", [("", node)]), result)
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
1363
        self.assertEqual({("foo bar",):"baz quux", ("bingo",):"bango"},
3735.2.24 by Robert Collins
test_chk_map tests all passing.
1364
            self.to_dict(node, None))
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
1365
        self.assertEqual(2, len(node))
1366
1367
    def test_map_replacement(self):
1368
        node = LeafNode()
3735.2.24 by Robert Collins
test_chk_map tests all passing.
1369
        node.map(None, ("foo bar",), "baz quux")
1370
        result = node.map(None, ("foo bar",), "bango")
1371
        self.assertEqual(("foo bar", [("", node)]), result)
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
1372
        self.assertEqual({("foo bar",): "bango"},
3735.2.24 by Robert Collins
test_chk_map tests all passing.
1373
            self.to_dict(node, None))
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
1374
        self.assertEqual(1, len(node))
1375
1376
    def test_serialise_empty(self):
1377
        store = self.get_chk_bytes()
1378
        node = LeafNode()
1379
        node.set_maximum_size(10)
3735.15.23 by John Arbash Meinel
Fix up the LeafNode direct tests, and add some tests with serialized
1380
        expected_key = ("sha1:f34c3f0634ea3f85953dffa887620c0a5b1f4a51",)
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
1381
        self.assertEqual([expected_key],
1382
            list(node.serialise(store)))
3735.15.23 by John Arbash Meinel
Fix up the LeafNode direct tests, and add some tests with serialized
1383
        self.assertEqual("chkleaf:\n10\n1\n0\n\n", self.read_bytes(store, expected_key))
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
1384
        self.assertEqual(expected_key, node.key())
1385
1386
    def test_serialise_items(self):
1387
        store = self.get_chk_bytes()
1388
        node = LeafNode()
1389
        node.set_maximum_size(10)
3735.2.24 by Robert Collins
test_chk_map tests all passing.
1390
        node.map(None, ("foo bar",), "baz quux")
3735.17.7 by John Arbash Meinel
Get all of test_chk_map passing.
1391
        expected_key = ("sha1:f89fac7edfc6bdb1b1b54a556012ff0c646ef5e0",)
3735.15.23 by John Arbash Meinel
Fix up the LeafNode direct tests, and add some tests with serialized
1392
        self.assertEqual('foo bar', node._common_serialised_prefix)
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
1393
        self.assertEqual([expected_key],
1394
            list(node.serialise(store)))
3735.17.7 by John Arbash Meinel
Get all of test_chk_map passing.
1395
        self.assertEqual("chkleaf:\n10\n1\n1\nfoo bar\n\x001\nbaz quux\n",
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
1396
            self.read_bytes(store, expected_key))
1397
        self.assertEqual(expected_key, node.key())
1398
3735.2.21 by Robert Collins
BROKEN: multi level CHKMap tries, unfinished, subsystem in flux.
1399
    def test_unique_serialised_prefix_empty_new(self):
1400
        node = LeafNode()
3735.15.24 by John Arbash Meinel
We were incorrectly using '' when we wanted to use None as the common prefix.
1401
        self.assertIs(None, node._compute_search_prefix())
3735.2.21 by Robert Collins
BROKEN: multi level CHKMap tries, unfinished, subsystem in flux.
1402
1403
    def test_unique_serialised_prefix_one_item_new(self):
1404
        node = LeafNode()
3735.2.24 by Robert Collins
test_chk_map tests all passing.
1405
        node.map(None, ("foo bar", "baz"), "baz quux")
3735.15.13 by John Arbash Meinel
Change the term 'lookup' to the term 'search', as it is closer to what Robert envisioned.
1406
        self.assertEqual("foo bar\x00baz", node._compute_search_prefix())
3735.2.21 by Robert Collins
BROKEN: multi level CHKMap tries, unfinished, subsystem in flux.
1407
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
1408
    def test_unmap_missing(self):
1409
        node = LeafNode()
3735.2.24 by Robert Collins
test_chk_map tests all passing.
1410
        self.assertRaises(KeyError, node.unmap, None, ("foo bar",))
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
1411
1412
    def test_unmap_present(self):
1413
        node = LeafNode()
3735.2.24 by Robert Collins
test_chk_map tests all passing.
1414
        node.map(None, ("foo bar",), "baz quux")
1415
        result = node.unmap(None, ("foo bar",))
1416
        self.assertEqual(node, result)
1417
        self.assertEqual({}, self.to_dict(node, None))
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
1418
        self.assertEqual(0, len(node))
1419
3735.15.6 by John Arbash Meinel
Add tests that LeafNodes track the common prefix for both their lookup keys
1420
    def test_map_maintains_common_prefixes(self):
1421
        node = LeafNode()
1422
        node._key_width = 2
1423
        node.map(None, ("foo bar", "baz"), "baz quux")
3735.15.13 by John Arbash Meinel
Change the term 'lookup' to the term 'search', as it is closer to what Robert envisioned.
1424
        self.assertEqual('foo bar\x00baz', node._search_prefix)
3735.15.6 by John Arbash Meinel
Add tests that LeafNodes track the common prefix for both their lookup keys
1425
        self.assertEqual('foo bar\x00baz', node._common_serialised_prefix)
1426
        node.map(None, ("foo bar", "bing"), "baz quux")
3735.15.13 by John Arbash Meinel
Change the term 'lookup' to the term 'search', as it is closer to what Robert envisioned.
1427
        self.assertEqual('foo bar\x00b', node._search_prefix)
3735.15.6 by John Arbash Meinel
Add tests that LeafNodes track the common prefix for both their lookup keys
1428
        self.assertEqual('foo bar\x00b', node._common_serialised_prefix)
1429
        node.map(None, ("fool", "baby"), "baz quux")
3735.15.13 by John Arbash Meinel
Change the term 'lookup' to the term 'search', as it is closer to what Robert envisioned.
1430
        self.assertEqual('foo', node._search_prefix)
3735.15.6 by John Arbash Meinel
Add tests that LeafNodes track the common prefix for both their lookup keys
1431
        self.assertEqual('foo', node._common_serialised_prefix)
1432
        node.map(None, ("foo bar", "baz"), "replaced")
3735.15.13 by John Arbash Meinel
Change the term 'lookup' to the term 'search', as it is closer to what Robert envisioned.
1433
        self.assertEqual('foo', node._search_prefix)
3735.15.6 by John Arbash Meinel
Add tests that LeafNodes track the common prefix for both their lookup keys
1434
        self.assertEqual('foo', node._common_serialised_prefix)
1435
        node.map(None, ("very", "different"), "value")
3735.15.13 by John Arbash Meinel
Change the term 'lookup' to the term 'search', as it is closer to what Robert envisioned.
1436
        self.assertEqual('', node._search_prefix)
3735.15.6 by John Arbash Meinel
Add tests that LeafNodes track the common prefix for both their lookup keys
1437
        self.assertEqual('', node._common_serialised_prefix)
1438
1439
    def test_unmap_maintains_common_prefixes(self):
1440
        node = LeafNode()
1441
        node._key_width = 2
1442
        node.map(None, ("foo bar", "baz"), "baz quux")
1443
        node.map(None, ("foo bar", "bing"), "baz quux")
1444
        node.map(None, ("fool", "baby"), "baz quux")
1445
        node.map(None, ("very", "different"), "value")
3735.15.13 by John Arbash Meinel
Change the term 'lookup' to the term 'search', as it is closer to what Robert envisioned.
1446
        self.assertEqual('', node._search_prefix)
3735.15.6 by John Arbash Meinel
Add tests that LeafNodes track the common prefix for both their lookup keys
1447
        self.assertEqual('', node._common_serialised_prefix)
1448
        node.unmap(None, ("very", "different"))
3735.15.13 by John Arbash Meinel
Change the term 'lookup' to the term 'search', as it is closer to what Robert envisioned.
1449
        self.assertEqual("foo", node._search_prefix)
3735.15.6 by John Arbash Meinel
Add tests that LeafNodes track the common prefix for both their lookup keys
1450
        self.assertEqual("foo", node._common_serialised_prefix)
1451
        node.unmap(None, ("fool", "baby"))
3735.15.13 by John Arbash Meinel
Change the term 'lookup' to the term 'search', as it is closer to what Robert envisioned.
1452
        self.assertEqual('foo bar\x00b', node._search_prefix)
3735.15.6 by John Arbash Meinel
Add tests that LeafNodes track the common prefix for both their lookup keys
1453
        self.assertEqual('foo bar\x00b', node._common_serialised_prefix)
1454
        node.unmap(None, ("foo bar", "baz"))
3735.15.13 by John Arbash Meinel
Change the term 'lookup' to the term 'search', as it is closer to what Robert envisioned.
1455
        self.assertEqual('foo bar\x00bing', node._search_prefix)
3735.15.6 by John Arbash Meinel
Add tests that LeafNodes track the common prefix for both their lookup keys
1456
        self.assertEqual('foo bar\x00bing', node._common_serialised_prefix)
1457
        node.unmap(None, ("foo bar", "bing"))
3735.15.24 by John Arbash Meinel
We were incorrectly using '' when we wanted to use None as the common prefix.
1458
        self.assertEqual(None, node._search_prefix)
1459
        self.assertEqual(None, node._common_serialised_prefix)
3735.15.6 by John Arbash Meinel
Add tests that LeafNodes track the common prefix for both their lookup keys
1460
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
1461
1462
class TestInternalNode(TestCaseWithStore):
1463
3735.2.21 by Robert Collins
BROKEN: multi level CHKMap tries, unfinished, subsystem in flux.
1464
    def test_add_node_empty_new(self):
3735.9.5 by John Arbash Meinel
Don't allow an InternalNode to add a key that doesn't fit.
1465
        node = InternalNode('fo')
3735.2.21 by Robert Collins
BROKEN: multi level CHKMap tries, unfinished, subsystem in flux.
1466
        child = LeafNode()
1467
        child.set_maximum_size(100)
1468
        child.map(None, ("foo",), "bar")
1469
        node.add_node("foo", child)
1470
        # Note that node isn't strictly valid now as a tree (only one child),
1471
        # but thats ok for this test.
1472
        # The first child defines the node's width:
1473
        self.assertEqual(3, node._node_width)
1474
        # We should be able to iterate over the contents without doing IO.
1475
        self.assertEqual({('foo',): 'bar'}, self.to_dict(node, None))
1476
        # The length should be known:
1477
        self.assertEqual(1, len(node))
1478
        # serialising the node should serialise the child and the node.
1479
        chk_bytes = self.get_chk_bytes()
1480
        keys = list(node.serialise(chk_bytes))
1481
        child_key = child.serialise(chk_bytes)[0]
1482
        self.assertEqual(
3735.17.7 by John Arbash Meinel
Get all of test_chk_map passing.
1483
            [child_key, ('sha1:cf67e9997d8228a907c1f5bfb25a8bd9cd916fac',)],
3735.2.21 by Robert Collins
BROKEN: multi level CHKMap tries, unfinished, subsystem in flux.
1484
            keys)
1485
        # We should be able to access deserialised content.
1486
        bytes = self.read_bytes(chk_bytes, keys[1])
3735.16.2 by John Arbash Meinel
Start passing around the search_key_func in more places.
1487
        node = chk_map._deserialise(bytes, keys[1], None)
3735.2.21 by Robert Collins
BROKEN: multi level CHKMap tries, unfinished, subsystem in flux.
1488
        self.assertEqual(1, len(node))
1489
        self.assertEqual({('foo',): 'bar'}, self.to_dict(node, chk_bytes))
1490
        self.assertEqual(3, node._node_width)
1491
1492
    def test_add_node_resets_key_new(self):
3735.9.5 by John Arbash Meinel
Don't allow an InternalNode to add a key that doesn't fit.
1493
        node = InternalNode('fo')
3735.2.21 by Robert Collins
BROKEN: multi level CHKMap tries, unfinished, subsystem in flux.
1494
        child = LeafNode()
1495
        child.set_maximum_size(100)
1496
        child.map(None, ("foo",), "bar")
1497
        node.add_node("foo", child)
1498
        chk_bytes = self.get_chk_bytes()
1499
        keys = list(node.serialise(chk_bytes))
1500
        self.assertEqual(keys[1], node._key)
1501
        node.add_node("fos", child)
1502
        self.assertEqual(None, node._key)
1503
1504
#    def test_add_node_empty_oversized_one_ok_new(self):
1505
#    def test_add_node_one_oversized_second_kept_minimum_fan(self):
1506
#    def test_add_node_two_oversized_third_kept_minimum_fan(self):
1507
#    def test_add_node_one_oversized_second_splits_errors(self):
1508
1509
    def test_iteritems_empty_new(self):
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
1510
        node = InternalNode()
3735.2.21 by Robert Collins
BROKEN: multi level CHKMap tries, unfinished, subsystem in flux.
1511
        self.assertEqual([], sorted(node.iteritems(None)))
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
1512
1513
    def test_iteritems_two_children(self):
1514
        node = InternalNode()
1515
        leaf1 = LeafNode()
3735.2.21 by Robert Collins
BROKEN: multi level CHKMap tries, unfinished, subsystem in flux.
1516
        leaf1.map(None, ('foo bar',), 'quux')
1517
        leaf2 = LeafNode()
1518
        leaf2.map(None, ('strange',), 'beast')
3735.2.24 by Robert Collins
test_chk_map tests all passing.
1519
        node.add_node("f", leaf1)
1520
        node.add_node("s", leaf2)
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
1521
        self.assertEqual([(('foo bar',), 'quux'), (('strange',), 'beast')],
3735.2.24 by Robert Collins
test_chk_map tests all passing.
1522
            sorted(node.iteritems(None)))
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
1523
1524
    def test_iteritems_two_children_partial(self):
1525
        node = InternalNode()
3735.2.24 by Robert Collins
test_chk_map tests all passing.
1526
        leaf1 = LeafNode()
1527
        leaf1.map(None, ('foo bar',), 'quux')
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
1528
        leaf2 = LeafNode()
3735.2.21 by Robert Collins
BROKEN: multi level CHKMap tries, unfinished, subsystem in flux.
1529
        leaf2.map(None, ('strange',), 'beast')
3735.2.24 by Robert Collins
test_chk_map tests all passing.
1530
        node.add_node("f", leaf1)
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
1531
        # This sets up a path that should not be followed - it will error if
1532
        # the code tries to.
3735.2.24 by Robert Collins
test_chk_map tests all passing.
1533
        node._items['f'] = None
1534
        node.add_node("s", leaf2)
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
1535
        self.assertEqual([(('strange',), 'beast')],
3735.2.24 by Robert Collins
test_chk_map tests all passing.
1536
            sorted(node.iteritems(None, [('strange',), ('weird',)])))
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
1537
3735.2.115 by John Arbash Meinel
Fix the root cause of why _iter_nodes was not handling key_filter.
1538
    def test_iteritems_two_children_with_hash(self):
1539
        search_key_func = chk_map.search_key_registry.get('hash-255-way')
1540
        node = InternalNode(search_key_func=search_key_func)
1541
        leaf1 = LeafNode(search_key_func=search_key_func)
1542
        leaf1.map(None, ('foo bar',), 'quux')
1543
        leaf2 = LeafNode(search_key_func=search_key_func)
1544
        leaf2.map(None, ('strange',), 'beast')
1545
        self.assertEqual('\xbeF\x014', search_key_func(('foo bar',)))
1546
        self.assertEqual('\x85\xfa\xf7K', search_key_func(('strange',)))
1547
        node.add_node("\xbe", leaf1)
1548
        # This sets up a path that should not be followed - it will error if
1549
        # the code tries to.
1550
        node._items['\xbe'] = None
1551
        node.add_node("\x85", leaf2)
1552
        self.assertEqual([(('strange',), 'beast')],
1553
            sorted(node.iteritems(None, [('strange',), ('weird',)])))
1554
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
1555
    def test_iteritems_partial_empty(self):
1556
        node = InternalNode()
1557
        self.assertEqual([], sorted(node.iteritems([('missing',)])))
1558
3735.2.21 by Robert Collins
BROKEN: multi level CHKMap tries, unfinished, subsystem in flux.
1559
    def test_map_to_new_child_new(self):
1560
        chkmap = self._get_map({('k1',):'foo', ('k2',):'bar'}, maximum_size=10)
1561
        chkmap._ensure_root()
1562
        node = chkmap._root_node
1563
        # Ensure test validity: nothing paged in below the root.
1564
        self.assertEqual(2,
1565
            len([value for value in node._items.values()
1566
                if type(value) == tuple]))
1567
        # now, mapping to k3 should add a k3 leaf
1568
        prefix, nodes = node.map(None, ('k3',), 'quux')
1569
        self.assertEqual("k", prefix)
1570
        self.assertEqual([("", node)], nodes)
1571
        # check new child details
1572
        child = node._items['k3']
1573
        self.assertIsInstance(child, LeafNode)
1574
        self.assertEqual(1, len(child))
1575
        self.assertEqual({('k3',): 'quux'}, self.to_dict(child, None))
1576
        self.assertEqual(None, child._key)
1577
        self.assertEqual(10, child.maximum_size)
1578
        self.assertEqual(1, child._key_width)
1579
        # Check overall structure:
1580
        self.assertEqual(3, len(chkmap))
1581
        self.assertEqual({('k1',): 'foo', ('k2',): 'bar', ('k3',): 'quux'},
1582
            self.to_dict(chkmap))
1583
        # serialising should only serialise the new data - k3 and the internal
1584
        # node.
1585
        keys = list(node.serialise(chkmap._store))
1586
        child_key = child.serialise(chkmap._store)[0]
1587
        self.assertEqual([child_key, keys[1]], keys)
1588
1589
    def test_map_to_child_child_splits_new(self):
1590
        chkmap = self._get_map({('k1',):'foo', ('k22',):'bar'}, maximum_size=10)
1591
        # Check for the canonical root value for this tree:
3735.15.15 by John Arbash Meinel
Change child_child to use _dump_tree,
1592
        self.assertEqualDiff("'' InternalNode\n"
1593
                             "  'k1' LeafNode\n"
1594
                             "      ('k1',) 'foo'\n"
1595
                             "  'k2' LeafNode\n"
1596
                             "      ('k22',) 'bar'\n"
3735.15.18 by John Arbash Meinel
Fix up the k1_from_k1 test.
1597
                             , chkmap._dump_tree())
3735.15.15 by John Arbash Meinel
Change child_child to use _dump_tree,
1598
        # _dump_tree pages everything in, so reload using just the root
1599
        chkmap = CHKMap(chkmap._store, chkmap._root_node)
3735.2.21 by Robert Collins
BROKEN: multi level CHKMap tries, unfinished, subsystem in flux.
1600
        chkmap._ensure_root()
1601
        node = chkmap._root_node
1602
        # Ensure test validity: nothing paged in below the root.
1603
        self.assertEqual(2,
1604
            len([value for value in node._items.values()
1605
                if type(value) == tuple]))
1606
        # now, mapping to k23 causes k22 ('k2' in node) to split into k22 and
1607
        # k23, which for simplicity in the current implementation generates
1608
        # a new internal node between node, and k22/k23.
1609
        prefix, nodes = node.map(chkmap._store, ('k23',), 'quux')
1610
        self.assertEqual("k", prefix)
1611
        self.assertEqual([("", node)], nodes)
1612
        # check new child details
1613
        child = node._items['k2']
1614
        self.assertIsInstance(child, InternalNode)
1615
        self.assertEqual(2, len(child))
1616
        self.assertEqual({('k22',): 'bar', ('k23',): 'quux'},
1617
            self.to_dict(child, None))
1618
        self.assertEqual(None, child._key)
1619
        self.assertEqual(10, child.maximum_size)
1620
        self.assertEqual(1, child._key_width)
1621
        self.assertEqual(3, child._node_width)
1622
        # Check overall structure:
1623
        self.assertEqual(3, len(chkmap))
1624
        self.assertEqual({('k1',): 'foo', ('k22',): 'bar', ('k23',): 'quux'},
1625
            self.to_dict(chkmap))
1626
        # serialising should only serialise the new data - although k22 hasn't
1627
        # changed because its a special corner case (splitting on with only one
1628
        # key leaves one node unaltered), in general k22 is serialised, so we
1629
        # expect k22, k23, the new internal node, and node, to be serialised.
1630
        keys = list(node.serialise(chkmap._store))
1631
        child_key = child._key
1632
        k22_key = child._items['k22']._key
1633
        k23_key = child._items['k23']._key
3735.15.15 by John Arbash Meinel
Change child_child to use _dump_tree,
1634
        self.assertEqual([k22_key, k23_key, child_key, node.key()], keys)
1635
        self.assertEqualDiff("'' InternalNode\n"
1636
                             "  'k1' LeafNode\n"
1637
                             "      ('k1',) 'foo'\n"
1638
                             "  'k2' InternalNode\n"
1639
                             "    'k22' LeafNode\n"
1640
                             "      ('k22',) 'bar'\n"
1641
                             "    'k23' LeafNode\n"
1642
                             "      ('k23',) 'quux'\n"
3735.15.18 by John Arbash Meinel
Fix up the k1_from_k1 test.
1643
                             , chkmap._dump_tree())
3735.2.21 by Robert Collins
BROKEN: multi level CHKMap tries, unfinished, subsystem in flux.
1644
3735.2.115 by John Arbash Meinel
Fix the root cause of why _iter_nodes was not handling key_filter.
1645
    def test__search_prefix_filter_with_hash(self):
1646
        search_key_func = chk_map.search_key_registry.get('hash-16-way')
1647
        node = InternalNode(search_key_func=search_key_func)
1648
        node._key_width = 2
1649
        node._node_width = 4
1650
        self.assertEqual('E8B7BE43\x0071BEEFF9', search_key_func(('a', 'b')))
1651
        self.assertEqual('E8B7', node._search_prefix_filter(('a', 'b')))
1652
        self.assertEqual('E8B7', node._search_prefix_filter(('a',)))
1653
3735.2.21 by Robert Collins
BROKEN: multi level CHKMap tries, unfinished, subsystem in flux.
1654
    def test_unmap_k23_from_k1_k22_k23_gives_k1_k22_tree_new(self):
1655
        chkmap = self._get_map(
1656
            {('k1',):'foo', ('k22',):'bar', ('k23',): 'quux'}, maximum_size=10)
1657
        # Check we have the expected tree.
3735.15.18 by John Arbash Meinel
Fix up the k1_from_k1 test.
1658
        self.assertEqualDiff("'' InternalNode\n"
1659
                             "  'k1' LeafNode\n"
1660
                             "      ('k1',) 'foo'\n"
3735.15.19 by John Arbash Meinel
Fix the other k1_k22_k23 test
1661
                             "  'k2' InternalNode\n"
1662
                             "    'k22' LeafNode\n"
3735.15.18 by John Arbash Meinel
Fix up the k1_from_k1 test.
1663
                             "      ('k22',) 'bar'\n"
3735.15.19 by John Arbash Meinel
Fix the other k1_k22_k23 test
1664
                             "    'k23' LeafNode\n"
1665
                             "      ('k23',) 'quux'\n"
3735.15.18 by John Arbash Meinel
Fix up the k1_from_k1 test.
1666
                             , chkmap._dump_tree())
1667
        chkmap = CHKMap(chkmap._store, chkmap._root_node)
3735.2.21 by Robert Collins
BROKEN: multi level CHKMap tries, unfinished, subsystem in flux.
1668
        chkmap._ensure_root()
1669
        node = chkmap._root_node
1670
        # unmapping k23 should give us a root, with k1 and k22 as direct
1671
        # children.
1672
        result = node.unmap(chkmap._store, ('k23',))
1673
        # check the pointed-at object within node - k2 should now point at the
3735.11.3 by John Arbash Meinel
At the end of unmap() see if children can be packed into a single Leaf.
1674
        # k22 leaf (which has been paged in to see if we can collapse the tree)
1675
        child = node._items['k2']
3735.2.21 by Robert Collins
BROKEN: multi level CHKMap tries, unfinished, subsystem in flux.
1676
        self.assertIsInstance(child, LeafNode)
1677
        self.assertEqual(1, len(child))
1678
        self.assertEqual({('k22',): 'bar'},
1679
            self.to_dict(child, None))
1680
        # Check overall structure is instact:
1681
        self.assertEqual(2, len(chkmap))
1682
        self.assertEqual({('k1',): 'foo', ('k22',): 'bar'},
1683
            self.to_dict(chkmap))
1684
        # serialising should only serialise the new data - the root node.
1685
        keys = list(node.serialise(chkmap._store))
1686
        self.assertEqual([keys[-1]], keys)
3735.15.19 by John Arbash Meinel
Fix the other k1_k22_k23 test
1687
        chkmap = CHKMap(chkmap._store, keys[-1])
1688
        self.assertEqualDiff("'' InternalNode\n"
1689
                             "  'k1' LeafNode\n"
1690
                             "      ('k1',) 'foo'\n"
1691
                             "  'k2' LeafNode\n"
1692
                             "      ('k22',) 'bar'\n"
1693
                             , chkmap._dump_tree())
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
1694
3735.2.23 by Robert Collins
Test unmapping with one child left but multiple keys.
1695
    def test_unmap_k1_from_k1_k22_k23_gives_k22_k23_tree_new(self):
1696
        chkmap = self._get_map(
1697
            {('k1',):'foo', ('k22',):'bar', ('k23',): 'quux'}, maximum_size=10)
3735.15.18 by John Arbash Meinel
Fix up the k1_from_k1 test.
1698
        self.assertEqualDiff("'' InternalNode\n"
1699
                             "  'k1' LeafNode\n"
1700
                             "      ('k1',) 'foo'\n"
1701
                             "  'k2' InternalNode\n"
1702
                             "    'k22' LeafNode\n"
1703
                             "      ('k22',) 'bar'\n"
1704
                             "    'k23' LeafNode\n"
1705
                             "      ('k23',) 'quux'\n"
1706
                             , chkmap._dump_tree())
1707
        orig_root = chkmap._root_node
1708
        chkmap = CHKMap(chkmap._store, orig_root)
3735.2.23 by Robert Collins
Test unmapping with one child left but multiple keys.
1709
        chkmap._ensure_root()
1710
        node = chkmap._root_node
1711
        k2_ptr = node._items['k2']
3735.15.15 by John Arbash Meinel
Change child_child to use _dump_tree,
1712
        # unmapping k1 should give us a root, with k22 and k23 as direct
3735.2.23 by Robert Collins
Test unmapping with one child left but multiple keys.
1713
        # children, and should not have needed to page in the subtree.
1714
        result = node.unmap(chkmap._store, ('k1',))
1715
        self.assertEqual(k2_ptr, result)
3735.15.18 by John Arbash Meinel
Fix up the k1_from_k1 test.
1716
        chkmap = CHKMap(chkmap._store, orig_root)
1717
        # Unmapping at the CHKMap level should switch to the new root
1718
        chkmap.unmap(('k1',))
1719
        self.assertEqual(k2_ptr, chkmap._root_node)
1720
        self.assertEqualDiff("'' InternalNode\n"
1721
                             "  'k22' LeafNode\n"
1722
                             "      ('k22',) 'bar'\n"
1723
                             "  'k23' LeafNode\n"
1724
                             "      ('k23',) 'quux'\n"
1725
                             , chkmap._dump_tree())
3735.2.23 by Robert Collins
Test unmapping with one child left but multiple keys.
1726
3735.2.21 by Robert Collins
BROKEN: multi level CHKMap tries, unfinished, subsystem in flux.
1727
1728
# leaf:
1729
# map -> fits - done
1730
# map -> doesn't fit - shrink from left till fits
1731
#        key data to return: the common prefix, new nodes.
1732
1733
# unmap -> how to tell if siblings can be combined.
1734
#          combing leaf nodes means expanding the prefix to the left; so gather the size of
1735
#          all the leaf nodes addressed by expanding the prefix by 1; if any adjacent node
1736
#          is an internal node, we know that that is a dense subtree - can't combine.
1737
#          otherwise as soon as the sum of serialised values exceeds the split threshold
1738
#          we know we can't combine - stop.
1739
# unmap -> key return data - space in node, common prefix length? and key count
3735.15.15 by John Arbash Meinel
Change child_child to use _dump_tree,
1740
# internal:
3735.2.21 by Robert Collins
BROKEN: multi level CHKMap tries, unfinished, subsystem in flux.
1741
# variable length prefixes? -> later start with fixed width to get something going
1742
# map -> fits - update pointer to leaf
1743
#        return [prefix and node] - seems sound.
1744
# map -> doesn't fit - find unique prefix and shift right
1745
#        create internal nodes for all the partitions, return list of unique
1746
#        prefixes and nodes.
1747
# map -> new prefix - create a leaf
1748
# unmap -> if child key count 0, remove
1749
# unmap -> return space in node, common prefix length? (why?), key count
1750
# map:
1751
# map, if 1 node returned, use it, otherwise make an internal and populate.
1752
# map - unmap - if empty, use empty leafnode (avoids special cases in driver
1753
# code)
1754
# map inits as empty leafnode.
3735.15.15 by John Arbash Meinel
Change child_child to use _dump_tree,
1755
# tools:
3735.2.21 by Robert Collins
BROKEN: multi level CHKMap tries, unfinished, subsystem in flux.
1756
# visualiser
1757
1758
1759
# how to handle:
1760
# AA, AB, AC, AD, BA
1761
# packed internal node - ideal:
1762
# AA, AB, AC, AD, BA
1763
# single byte fanout - A,B,   AA,AB,AC,AD,     BA
1764
# build order's:
1765
# BA
3735.15.15 by John Arbash Meinel
Change child_child to use _dump_tree,
1766
# AB - split, but we want to end up with AB, BA, in one node, with
3735.2.21 by Robert Collins
BROKEN: multi level CHKMap tries, unfinished, subsystem in flux.
1767
# 1-4K get0
3735.9.1 by John Arbash Meinel
Start working on an iter_interesting_nodes, which can find nodes to transmit
1768
1769
1770
class TestIterInterestingNodes(TestCaseWithStore):
1771
1772
    def get_chk_bytes(self):
1773
        if getattr(self, '_chk_bytes', None) is None:
1774
            self._chk_bytes = super(TestIterInterestingNodes,
1775
                                    self).get_chk_bytes()
1776
        return self._chk_bytes
1777
1778
    def get_map_key(self, a_dict):
1779
        c_map = self._get_map(a_dict, maximum_size=10,
1780
                              chk_bytes=self.get_chk_bytes())
1781
        return c_map.key()
1782
1783
    def assertIterInteresting(self, expected, interesting_keys,
1784
                              uninteresting_keys):
1785
        """Check the result of iter_interesting_nodes.
1786
1787
        :param expected: A list of (record_keys, interesting_chk_pages,
1788
                                    interesting key value pairs)
1789
        """
1790
        store = self.get_chk_bytes()
1791
        iter_nodes = chk_map.iter_interesting_nodes(store, interesting_keys,
1792
                                                    uninteresting_keys)
1793
        for count, (exp, act) in enumerate(izip(expected, iter_nodes)):
3735.9.8 by John Arbash Meinel
Simplify the interface.
1794
            exp_record_keys, exp_items = exp
1795
            records, items = act
3735.9.15 by John Arbash Meinel
Found a bug in iter_interesting_nodes and its test suite.
1796
            exp_tuple = (sorted(exp_record_keys), sorted(exp_items))
1797
            act_tuple = (sorted(records.keys()), sorted(items))
3735.9.1 by John Arbash Meinel
Start working on an iter_interesting_nodes, which can find nodes to transmit
1798
            self.assertEqual(exp_tuple, act_tuple)
1799
        self.assertEqual(len(expected), count + 1)
1800
3735.9.19 by John Arbash Meinel
Refactor iter_interesting a little bit.
1801
    def test_empty_to_one_keys(self):
1802
        target = self.get_map_key({('a',): 'content'})
1803
        self.assertIterInteresting(
1804
            [([target], [(('a',), 'content')])],
1805
            [target], [])
1806
3735.9.1 by John Arbash Meinel
Start working on an iter_interesting_nodes, which can find nodes to transmit
1807
    def test_none_to_one_key(self):
1808
        basis = self.get_map_key({})
1809
        target = self.get_map_key({('a',): 'content'})
1810
        self.assertIterInteresting(
3735.9.8 by John Arbash Meinel
Simplify the interface.
1811
            [([target], [(('a',), 'content')])],
3735.9.1 by John Arbash Meinel
Start working on an iter_interesting_nodes, which can find nodes to transmit
1812
            [target], [basis])
1813
1814
    def test_one_to_none_key(self):
1815
        basis = self.get_map_key({('a',): 'content'})
1816
        target = self.get_map_key({})
1817
        self.assertIterInteresting(
3735.9.8 by John Arbash Meinel
Simplify the interface.
1818
            [([target], [])],
3735.9.1 by John Arbash Meinel
Start working on an iter_interesting_nodes, which can find nodes to transmit
1819
            [target], [basis])
1820
1821
    def test_common_pages(self):
1822
        basis = self.get_map_key({('a',): 'content',
1823
                                  ('b',): 'content',
1824
                                  ('c',): 'content',
1825
                                 })
1826
        target = self.get_map_key({('a',): 'content',
1827
                                   ('b',): 'other content',
1828
                                   ('c',): 'content',
1829
                                  })
3735.15.21 by John Arbash Meinel
Change the IterInteresting to avoid sha hashes and be serialization agnostic.
1830
        target_map = CHKMap(self.get_chk_bytes(), target)
3735.15.20 by John Arbash Meinel
Fix the IterInteresting tests.
1831
        self.assertEqualDiff(
3735.15.21 by John Arbash Meinel
Change the IterInteresting to avoid sha hashes and be serialization agnostic.
1832
            "'' InternalNode\n"
1833
            "  'a' LeafNode\n"
3735.15.20 by John Arbash Meinel
Fix the IterInteresting tests.
1834
            "      ('a',) 'content'\n"
3735.15.21 by John Arbash Meinel
Change the IterInteresting to avoid sha hashes and be serialization agnostic.
1835
            "  'b' LeafNode\n"
3735.15.20 by John Arbash Meinel
Fix the IterInteresting tests.
1836
            "      ('b',) 'other content'\n"
3735.15.21 by John Arbash Meinel
Change the IterInteresting to avoid sha hashes and be serialization agnostic.
1837
            "  'c' LeafNode\n"
3735.15.20 by John Arbash Meinel
Fix the IterInteresting tests.
1838
            "      ('c',) 'content'\n",
3735.15.21 by John Arbash Meinel
Change the IterInteresting to avoid sha hashes and be serialization agnostic.
1839
            target_map._dump_tree())
1840
        b_key = target_map._root_node._items['b'].key()
3735.9.1 by John Arbash Meinel
Start working on an iter_interesting_nodes, which can find nodes to transmit
1841
        # This should return the root node, and the node for the 'b' key
1842
        self.assertIterInteresting(
3735.9.8 by John Arbash Meinel
Simplify the interface.
1843
            [([target], []),
1844
             ([b_key], [(('b',), 'other content')])],
3735.9.1 by John Arbash Meinel
Start working on an iter_interesting_nodes, which can find nodes to transmit
1845
            [target], [basis])
1846
1847
    def test_common_sub_page(self):
1848
        basis = self.get_map_key({('aaa',): 'common',
1849
                                  ('c',): 'common',
1850
                                 })
1851
        target = self.get_map_key({('aaa',): 'common',
1852
                                   ('aab',): 'new',
1853
                                   ('c',): 'common',
1854
                                  })
3735.15.21 by John Arbash Meinel
Change the IterInteresting to avoid sha hashes and be serialization agnostic.
1855
        target_map = CHKMap(self.get_chk_bytes(), target)
3735.9.6 by John Arbash Meinel
Add a first pass to the interesting search.
1856
        self.assertEqualDiff(
3735.15.21 by John Arbash Meinel
Change the IterInteresting to avoid sha hashes and be serialization agnostic.
1857
            "'' InternalNode\n"
1858
            "  'a' InternalNode\n"
1859
            "    'aaa' LeafNode\n"
3735.9.6 by John Arbash Meinel
Add a first pass to the interesting search.
1860
            "      ('aaa',) 'common'\n"
3735.15.21 by John Arbash Meinel
Change the IterInteresting to avoid sha hashes and be serialization agnostic.
1861
            "    'aab' LeafNode\n"
3735.9.6 by John Arbash Meinel
Add a first pass to the interesting search.
1862
            "      ('aab',) 'new'\n"
3735.15.21 by John Arbash Meinel
Change the IterInteresting to avoid sha hashes and be serialization agnostic.
1863
            "  'c' LeafNode\n"
3735.11.10 by John Arbash Meinel
Change how _check_remap works so it doesn't have to load all keys.
1864
            "      ('c',) 'common'\n",
3735.15.21 by John Arbash Meinel
Change the IterInteresting to avoid sha hashes and be serialization agnostic.
1865
            target_map._dump_tree())
3735.9.1 by John Arbash Meinel
Start working on an iter_interesting_nodes, which can find nodes to transmit
1866
        # The key for the internal aa node
3735.15.21 by John Arbash Meinel
Change the IterInteresting to avoid sha hashes and be serialization agnostic.
1867
        a_key = target_map._root_node._items['a'].key()
3735.9.1 by John Arbash Meinel
Start working on an iter_interesting_nodes, which can find nodes to transmit
1868
        # The key for the leaf aab node
3735.15.21 by John Arbash Meinel
Change the IterInteresting to avoid sha hashes and be serialization agnostic.
1869
        aab_key = target_map._root_node._items['a']._items['aab'].key()
3735.9.1 by John Arbash Meinel
Start working on an iter_interesting_nodes, which can find nodes to transmit
1870
        self.assertIterInteresting(
3735.9.8 by John Arbash Meinel
Simplify the interface.
1871
            [([target], []),
3735.15.20 by John Arbash Meinel
Fix the IterInteresting tests.
1872
             ([a_key], []),
3735.9.8 by John Arbash Meinel
Simplify the interface.
1873
             ([aab_key], [(('aab',), 'new')])],
3735.9.1 by John Arbash Meinel
Start working on an iter_interesting_nodes, which can find nodes to transmit
1874
            [target], [basis])
1875
1876
    def test_multiple_maps(self):
1877
        basis1 = self.get_map_key({('aaa',): 'common',
1878
                                   ('aab',): 'basis1',
1879
                                  })
1880
        basis2 = self.get_map_key({('bbb',): 'common',
1881
                                   ('bbc',): 'basis2',
1882
                                  })
1883
        target1 = self.get_map_key({('aaa',): 'common',
1884
                                    ('aac',): 'target1',
1885
                                    ('bbb',): 'common',
1886
                                   })
1887
        target2 = self.get_map_key({('aaa',): 'common',
1888
                                    ('bba',): 'target2',
1889
                                    ('bbb',): 'common',
1890
                                   })
3735.15.22 by John Arbash Meinel
Change the multiple_maps test to also be independent of sha hash.
1891
        target1_map = CHKMap(self.get_chk_bytes(), target1)
1892
        self.assertEqualDiff(
1893
            "'' InternalNode\n"
1894
            "  'a' InternalNode\n"
1895
            "    'aaa' LeafNode\n"
1896
            "      ('aaa',) 'common'\n"
1897
            "    'aac' LeafNode\n"
1898
            "      ('aac',) 'target1'\n"
1899
            "  'b' LeafNode\n"
1900
            "      ('bbb',) 'common'\n",
1901
            target1_map._dump_tree())
1902
        # The key for the target1 internal a node
1903
        a_key = target1_map._root_node._items['a'].key()
3735.9.1 by John Arbash Meinel
Start working on an iter_interesting_nodes, which can find nodes to transmit
1904
        # The key for the leaf aac node
3735.15.22 by John Arbash Meinel
Change the multiple_maps test to also be independent of sha hash.
1905
        aac_key = target1_map._root_node._items['a']._items['aac'].key()
1906
1907
        target2_map = CHKMap(self.get_chk_bytes(), target2)
1908
        self.assertEqualDiff(
1909
            "'' InternalNode\n"
1910
            "  'a' LeafNode\n"
1911
            "      ('aaa',) 'common'\n"
1912
            "  'b' InternalNode\n"
1913
            "    'bba' LeafNode\n"
1914
            "      ('bba',) 'target2'\n"
1915
            "    'bbb' LeafNode\n"
1916
            "      ('bbb',) 'common'\n",
1917
            target2_map._dump_tree())
3735.9.1 by John Arbash Meinel
Start working on an iter_interesting_nodes, which can find nodes to transmit
1918
        # The key for the target2 internal bb node
3735.15.22 by John Arbash Meinel
Change the multiple_maps test to also be independent of sha hash.
1919
        b_key = target2_map._root_node._items['b'].key()
3735.9.1 by John Arbash Meinel
Start working on an iter_interesting_nodes, which can find nodes to transmit
1920
        # The key for the leaf bba node
3735.15.22 by John Arbash Meinel
Change the multiple_maps test to also be independent of sha hash.
1921
        bba_key = target2_map._root_node._items['b']._items['bba'].key()
3735.9.1 by John Arbash Meinel
Start working on an iter_interesting_nodes, which can find nodes to transmit
1922
        self.assertIterInteresting(
3735.9.8 by John Arbash Meinel
Simplify the interface.
1923
            [([target1, target2], []),
3735.15.22 by John Arbash Meinel
Change the multiple_maps test to also be independent of sha hash.
1924
             ([a_key], []),
1925
             ([b_key], []),
3735.9.19 by John Arbash Meinel
Refactor iter_interesting a little bit.
1926
             ([aac_key], [(('aac',), 'target1')]),
1927
             ([bba_key], [(('bba',), 'target2')]),
3735.9.1 by John Arbash Meinel
Start working on an iter_interesting_nodes, which can find nodes to transmit
1928
            ], [target1, target2], [basis1, basis2])