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