/brz/remove-bazaar

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