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