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