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