/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.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
19
from bzrlib.chk_map import CHKMap, RootNode, InternalNode, LeafNode, ValueNode
3735.2.8 by Robert Collins
New chk_map module for use in tree based inventory storage.
20
from bzrlib.tests import TestCaseWithTransport
21
22
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
23
class TestCaseWithStore(TestCaseWithTransport):
3735.2.8 by Robert Collins
New chk_map module for use in tree based inventory storage.
24
25
    def get_chk_bytes(self):
26
        # The eassiest way to get a CHK store is a development3 repository and
27
        # then work with the chk_bytes attribute directly.
28
        repo = self.make_repository(".", format="development3")
29
        repo.lock_write()
30
        self.addCleanup(repo.unlock)
31
        repo.start_write_group()
32
        self.addCleanup(repo.abort_write_group)
33
        return repo.chk_bytes
34
35
    def read_bytes(self, chk_bytes, key):
36
        stream = chk_bytes.get_record_stream([key], 'unordered', True)
37
        return stream.next().get_bytes_as("fulltext")
38
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
39
    def to_dict(self, node):
40
        return dict(node.iteritems())
41
42
43
class TestMap(TestCaseWithStore):
44
3735.2.8 by Robert Collins
New chk_map module for use in tree based inventory storage.
45
    def assertHasABMap(self, chk_bytes):
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
46
        root_key = ('sha1:29f1da33ce2323d754485fd308abc5ff17f3856e',)
3735.2.8 by Robert Collins
New chk_map module for use in tree based inventory storage.
47
        self.assertEqual(
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
48
            "chkroot:\n0\n1\na\x00sha1:cb29f32e561a1b7f862c38ccfd6bc7c7d892f04b\n",
3735.2.8 by Robert Collins
New chk_map module for use in tree based inventory storage.
49
            self.read_bytes(chk_bytes, root_key))
50
        self.assertEqual(
51
            "chkvalue:\nb",
52
            self.read_bytes(chk_bytes,
53
                ("sha1:cb29f32e561a1b7f862c38ccfd6bc7c7d892f04b",)))
54
55
    def assertHasEmptyMap(self, chk_bytes):
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
56
        root_key = ('sha1:d0826cf765ff45bd602f602ecb0efbe375ea3b50',)
57
        self.assertEqual("chkroot:\n0\n0\n", self.read_bytes(chk_bytes, root_key))
3735.2.17 by Robert Collins
Cache node length to avoid full iteration on __len__ calls.
58
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
59
    def _get_map(self, a_dict, maximum_size=0):
3735.2.17 by Robert Collins
Cache node length to avoid full iteration on __len__ calls.
60
        chk_bytes = self.get_chk_bytes()
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
61
        root_key = CHKMap.from_dict(chk_bytes, a_dict, maximum_size=maximum_size)
3735.2.17 by Robert Collins
Cache node length to avoid full iteration on __len__ calls.
62
        chkmap = CHKMap(chk_bytes, root_key)
63
        return chkmap
3735.2.8 by Robert Collins
New chk_map module for use in tree based inventory storage.
64
65
    def test_from_dict_empty(self):
66
        chk_bytes = self.get_chk_bytes()
67
        root_key = CHKMap.from_dict(chk_bytes, {})
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
68
        self.assertEqual(('sha1:d0826cf765ff45bd602f602ecb0efbe375ea3b50',),
3735.2.8 by Robert Collins
New chk_map module for use in tree based inventory storage.
69
            root_key)
70
        self.assertHasEmptyMap(chk_bytes)
71
72
    def test_from_dict_ab(self):
73
        chk_bytes = self.get_chk_bytes()
74
        root_key = CHKMap.from_dict(chk_bytes, {"a":"b"})
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
75
        self.assertEqual(('sha1:29f1da33ce2323d754485fd308abc5ff17f3856e',),
3735.2.8 by Robert Collins
New chk_map module for use in tree based inventory storage.
76
            root_key)
77
        self.assertHasABMap(chk_bytes)
78
79
    def test_apply_empty_ab(self):
80
        # applying a delta (None, "a", "b") to an empty chkmap generates the
81
        # same map as from_dict_ab.
82
        chk_bytes = self.get_chk_bytes()
83
        root_key = CHKMap.from_dict(chk_bytes, {})
84
        chkmap = CHKMap(chk_bytes, root_key)
85
        new_root = chkmap.apply_delta([(None, "a", "b")])
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
86
        self.assertEqual(('sha1:29f1da33ce2323d754485fd308abc5ff17f3856e',),
3735.2.8 by Robert Collins
New chk_map module for use in tree based inventory storage.
87
            new_root)
88
        self.assertHasABMap(chk_bytes)
89
        # The update should have left us with an in memory root node, with an
90
        # updated key.
91
        self.assertEqual(new_root, chkmap._root_node._key)
92
93
    def test_apply_ab_empty(self):
94
        # applying a delta ("a", None, None) to an empty chkmap generates the
95
        # same map as from_dict_ab.
96
        chk_bytes = self.get_chk_bytes()
97
        root_key = CHKMap.from_dict(chk_bytes, {"a":"b"})
98
        chkmap = CHKMap(chk_bytes, root_key)
99
        new_root = chkmap.apply_delta([("a", None, None)])
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
100
        self.assertEqual(('sha1:d0826cf765ff45bd602f602ecb0efbe375ea3b50',),
3735.2.8 by Robert Collins
New chk_map module for use in tree based inventory storage.
101
            new_root)
102
        self.assertHasEmptyMap(chk_bytes)
103
        # The update should have left us with an in memory root node, with an
104
        # updated key.
105
        self.assertEqual(new_root, chkmap._root_node._key)
106
107
    def test_iteritems_empty(self):
108
        chk_bytes = self.get_chk_bytes()
109
        root_key = CHKMap.from_dict(chk_bytes, {})
110
        chkmap = CHKMap(chk_bytes, root_key)
111
        self.assertEqual([], list(chkmap.iteritems()))
112
113
    def test_iteritems_two_items(self):
114
        chk_bytes = self.get_chk_bytes()
115
        root_key = CHKMap.from_dict(chk_bytes,
116
            {"a":"content here", "b":"more content"})
117
        chkmap = CHKMap(chk_bytes, root_key)
118
        self.assertEqual([("a", "content here"), ("b", "more content")],
119
            sorted(list(chkmap.iteritems())))
120
3735.2.9 by Robert Collins
Get a working chk_map using inventory implementation bootstrapped.
121
    def test_iteritems_selected_one_of_two_items(self):
3735.2.17 by Robert Collins
Cache node length to avoid full iteration on __len__ calls.
122
        chkmap = self._get_map( {"a":"content here", "b":"more content"})
3735.2.9 by Robert Collins
Get a working chk_map using inventory implementation bootstrapped.
123
        self.assertEqual([("a", "content here")],
124
            sorted(list(chkmap.iteritems(["a"]))))
125
3735.2.17 by Robert Collins
Cache node length to avoid full iteration on __len__ calls.
126
    def test___len__empty(self):
127
        chkmap = self._get_map({})
128
        self.assertEqual(0, len(chkmap))
129
130
    def test___len__2(self):
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
131
        chkmap = self._get_map({"foo":"bar", "gam":"quux"})
132
        self.assertEqual(2, len(chkmap))
133
134
    def test_max_size_100_bytes(self):
135
        # When there is a 100 byte upper node limit, a tree is formed.
136
        chkmap = self._get_map({"k1"*50:"v1", "k2"*50:"v2"}, maximum_size=100)
137
        # We expect three nodes:
138
        # A root, with two children, and with two key prefixes - k1 to one, and
139
        # k2 to the other as our node splitting is only just being developed.
140
        # The maximum size should be embedded
141
        chkmap._ensure_root()
142
        self.assertEqual(100, chkmap._root_node.maximum_size)
143
        # There should be two child nodes, and prefix of 2(bytes):
144
        self.assertEqual(2, len(chkmap._root_node._nodes))
145
        self.assertEqual(2, chkmap._root_node.prefix_length)
146
        # The actual nodes pointed at will change as serialisers change; so
147
        # here we test that the key prefix is correct; then load the nodes and
148
        # check they have the right pointed at key; whether they have the
149
        # pointed at value inline or not is also unrelated to this test so we
150
        # don't check that.
151
        nodes = sorted(chkmap._root_node._nodes.items())
152
        ptr1 = nodes[0]
153
        ptr2 = nodes[1]
154
        self.assertEqual('k1', ptr1[0])
155
        self.assertEqual('k2', ptr2[0])
156
        node1 = chk_map._deserialise(chkmap._read_bytes(ptr1[1]), ptr1[1])
157
        self.assertEqual(1, len(node1._nodes))
158
        self.assertEqual(['k1'*50], node1._nodes.keys())
159
        node2 = chk_map._deserialise(chkmap._read_bytes(ptr2[1]), ptr2[1])
160
        self.assertEqual(1, len(node2._nodes))
161
        self.assertEqual(['k2'*50], node2._nodes.keys())
162
        # Having checked we have a good structure, check that the content is
163
        # still accessible.
164
        self.assertEqual(2, len(chkmap))
165
        self.assertEqual([("k1"*50, "v1"), ("k2"*50, "v2")],
166
            sorted(list(chkmap.iteritems())))
3735.2.17 by Robert Collins
Cache node length to avoid full iteration on __len__ calls.
167
3735.2.8 by Robert Collins
New chk_map module for use in tree based inventory storage.
168
169
class TestRootNode(TestCaseWithTransport):
170
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
171
    def test__current_size(self):
172
        node = RootNode()
173
        self.assertEqual(15, node._current_size())
174
        node.add_child("cd", ("sha1:12345",))
175
        self.assertEqual(29, node._current_size())
176
        self.assertEqual(29, len(node.serialise()))
177
        node.add_child("cd", ("sha1:123456",))
178
        self.assertEqual(30, node._current_size())
179
        self.assertEqual(30, len(node.serialise()))
180
        node.remove_child("cd")
181
        self.assertEqual(15, node._current_size())
182
        self.assertEqual(15, len(node.serialise()))
183
        node.set_maximum_size(100)
184
        self.assertEqual(17, node._current_size())
185
3735.2.8 by Robert Collins
New chk_map module for use in tree based inventory storage.
186
    def test_serialise_empty(self):
187
        node = RootNode()
188
        bytes = node.serialise()
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
189
        self.assertEqual("chkroot:\n0\n0\n0\n", bytes)
190
191
    def test_add_child_over_limit(self):
192
        node = RootNode()
193
        node.set_maximum_size(20)
194
        node.add_child("abcdef", ("sha1:12345",))
195
        size = node._current_size()
196
        self.assertTrue(20 < size)
197
        self.assertEqual(False, node.add_child("12345", ("sha1:34",)))
198
        # Nothing should have changed
199
        self.assertEqual(size, node._current_size())
200
        self.assertEqual(1, len(node))
3735.2.8 by Robert Collins
New chk_map module for use in tree based inventory storage.
201
202
    def test_add_child_resets_key(self):
203
        node = RootNode()
204
        node._key = ("something",)
205
        node.add_child("c", ("sha1:1234",))
206
        self.assertEqual(None, node._key)
207
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
208
    def test_add_child_returns_True(self):
209
        node = RootNode()
210
        node._key = ("something",)
211
        self.assertEqual(True, node.add_child("c", ("sha1:1234",)))
212
3735.2.17 by Robert Collins
Cache node length to avoid full iteration on __len__ calls.
213
    def test_add_child_increases_len(self):
214
        node = RootNode()
215
        node._key = ("something",)
216
        node.add_child("c", ("sha1:1234",))
217
        self.assertEqual(1, len(node))
218
219
    def test_remove_child_decreases_len(self):
220
        node = RootNode()
221
        node.add_child("c", ("sha1:1234",))
222
        node._key = ("something",)
223
        node.remove_child("c")
224
        self.assertEqual(0, len(node))
225
3735.2.8 by Robert Collins
New chk_map module for use in tree based inventory storage.
226
    def test_remove_child_removes_child(self):
227
        node = RootNode()
228
        node.add_child("a", ("sha1:4321",))
229
        node.add_child("c", ("sha1:1234",))
230
        node._key = ("something",)
231
        node.remove_child("a")
232
        self.assertEqual({"c":("sha1:1234",)}, node._nodes)
233
234
    def test_remove_child_resets_key(self):
235
        node = RootNode()
236
        node.add_child("c", ("sha1:1234",))
237
        node._key = ("something",)
238
        node.remove_child("c")
239
        self.assertEqual(None, node._key)
240
241
    def test_deserialise(self):
242
        # deserialising from a bytestring & key sets the nodes and the known
243
        # key.
244
        node = RootNode()
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
245
        node.deserialise("chkroot:\n0\n0\n1\nc\x00sha1:1234\n", ("foo",))
3735.2.8 by Robert Collins
New chk_map module for use in tree based inventory storage.
246
        self.assertEqual({"c": ("sha1:1234",)}, node._nodes)
247
        self.assertEqual(("foo",), node._key)
3735.2.17 by Robert Collins
Cache node length to avoid full iteration on __len__ calls.
248
        self.assertEqual(1, len(node))
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
249
        self.assertEqual(0, node.maximum_size)
3735.2.8 by Robert Collins
New chk_map module for use in tree based inventory storage.
250
251
    def test_serialise_with_child(self):
252
        node = RootNode()
253
        node.add_child("c", ("sha1:1234",))
254
        bytes = node.serialise()
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
255
        # type 0-max-length 1-value key\x00CHK
256
        self.assertEqual("chkroot:\n0\n0\n1\nc\x00sha1:1234\n", bytes)
257
258
    def test_deserialise_max_size(self):
259
        node = RootNode()
260
        node.deserialise("chkroot:\n100\n0\n1\nc\x00sha1:1234\n", ("foo",))
261
        self.assertEqual(100, node.maximum_size)
262
263
    def test_deserialise_key_prefix(self):
264
        node = RootNode()
265
        node.deserialise("chkroot:\n100\n10\n1\nc\x00sha1:1234\n", ("foo",))
266
        self.assertEqual(10, node.prefix_width)
3735.2.8 by Robert Collins
New chk_map module for use in tree based inventory storage.
267
268
269
class TestValueNode(TestCaseWithTransport):
270
271
    def test_deserialise(self):
272
        node = ValueNode.deserialise("chkvalue:\nfoo bar baz\n")
273
        self.assertEqual("foo bar baz\n", node.value)
274
275
    def test_serialise(self):
276
        node = ValueNode("b")
277
        bytes = node.serialise()
278
        self.assertEqual("chkvalue:\nb", bytes)
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
279
280
281
class TestLeafNode(TestCaseWithStore):
282
283
    def test_current_size_empty(self):
284
        node = LeafNode()
285
        self.assertEqual(15, node._current_size())
286
287
    def test_current_size_size_changed(self):
288
        node = LeafNode()
289
        node.set_maximum_size(10)
290
        self.assertEqual(16, node._current_size())
291
292
    def test_current_size_width_changed(self):
293
        node = LeafNode()
294
        node._key_width = 10
295
        self.assertEqual(16, node._current_size())
296
297
    def test_current_size_items(self):
298
        node = LeafNode()
299
        base_size = node._current_size()
300
        node = node.map(("foo bar",), "baz")
301
        self.assertEqual(base_size + 12, node._current_size())
302
303
    def test_deserialise_empty(self):
304
        node = LeafNode.deserialise("chkleaf:\n10\n1\n0\n", ("sha1:1234",))
305
        self.assertEqual(0, len(node))
306
        self.assertEqual(10, node.maximum_size)
307
        self.assertEqual(("sha1:1234",), node.key())
308
309
    def test_deserialise_items(self):
310
        node = LeafNode.deserialise(
311
            "chkleaf:\n0\n1\n2\nfoo bar\x00baz\nquux\x00blarh\n", ("sha1:1234",))
312
        self.assertEqual(2, len(node))
313
        self.assertEqual([(("foo bar",), "baz"), (("quux",), "blarh")],
314
            sorted(node.iteritems()))
315
316
    def test_iteritems_selected_one_of_two_items(self):
317
        node = LeafNode.deserialise(
318
            "chkleaf:\n0\n1\n2\nfoo bar\x00baz\nquux\x00blarh\n", ("sha1:1234",))
319
        self.assertEqual(2, len(node))
320
        self.assertEqual([(("quux",), "blarh")],
321
            sorted(node.iteritems([("quux",), ("qaz",)])))
322
323
    def test_key_new(self):
324
        node = LeafNode()
325
        self.assertEqual(None, node.key())
326
327
    def test_key_after_map(self):
328
        node = LeafNode.deserialise("chkleaf:\n10\n1\n0\n", ("sha1:1234",))
329
        node = node.map(("foo bar",), "baz quux")
330
        self.assertEqual(None, node.key())
331
332
    def test_key_after_unmap(self):
333
        node = LeafNode.deserialise(
334
            "chkleaf:\n0\n1\n2\nfoo bar\x00baz\nquux\x00blarh\n", ("sha1:1234",))
335
        node = node.unmap(("foo bar",))
336
        self.assertEqual(None, node.key())
337
338
    def test_map_exceeding_max_size_only_entry(self):
339
        node = LeafNode()
340
        node.set_maximum_size(10)
341
        result = node.map(("foo bar",), "baz quux")
342
        self.assertEqual(result, node)
343
        self.assertTrue(10 < result._current_size())
344
345
    def test_map_exceeding_max_size_second_entry_early_difference(self):
346
        node = LeafNode()
347
        node.set_maximum_size(10)
348
        node = node.map(("foo bar",), "baz quux")
349
        result = node.map(("blue",), "red")
350
        self.assertIsInstance(result, InternalNode)
351
        # should have copied the data in:
352
        self.assertEqual(2, len(result))
353
        self.assertEqual({('blue',): 'red', ('foo bar',): 'baz quux'},
354
            self.to_dict(result))
355
        self.assertEqual(10, result.maximum_size)
356
        self.assertEqual(1, result._key_width)
357
358
    def test_map_exceeding_max_size_second_entry_last_octect_changed(self):
359
        node = LeafNode()
360
        node.set_maximum_size(10)
361
        node = node.map(("foo bar",), "baz quux")
362
        result = node.map(("foo baz",), "red")
363
        self.assertIsInstance(result, InternalNode)
364
        # should have copied the data in:
365
        self.assertEqual(2, len(result))
366
        self.assertEqual({('foo baz',): 'red', ('foo bar',): 'baz quux'},
367
            self.to_dict(result))
368
        self.assertEqual(10, result.maximum_size)
369
        self.assertEqual(1, result._key_width)
370
371
    def test_map_first(self):
372
        node = LeafNode()
373
        result = node.map(("foo bar",), "baz quux")
374
        self.assertEqual(result, node)
375
        self.assertEqual({("foo bar",):"baz quux"}, self.to_dict(node))
376
        self.assertEqual(1, len(node))
377
378
    def test_map_second(self):
379
        node = LeafNode()
380
        node = node.map(("foo bar",), "baz quux")
381
        result = node.map(("bingo",), "bango")
382
        self.assertEqual(result, node)
383
        self.assertEqual({("foo bar",):"baz quux", ("bingo",):"bango"},
384
            self.to_dict(node))
385
        self.assertEqual(2, len(node))
386
387
    def test_map_replacement(self):
388
        node = LeafNode()
389
        node = node.map(("foo bar",), "baz quux")
390
        result = node.map(("foo bar",), "bango")
391
        self.assertEqual(result, node)
392
        self.assertEqual({("foo bar",): "bango"},
393
            self.to_dict(node))
394
        self.assertEqual(1, len(node))
395
396
    def test_serialise_empty(self):
397
        store = self.get_chk_bytes()
398
        node = LeafNode()
399
        node.set_maximum_size(10)
400
        expected_key = ("sha1:62cc3565b48b0e830216e652cf99c6bd6b05b4b9",)
401
        self.assertEqual([expected_key],
402
            list(node.serialise(store)))
403
        self.assertEqual("chkleaf:\n10\n1\n0\n", self.read_bytes(store, expected_key))
404
        self.assertEqual(expected_key, node.key())
405
406
    def test_serialise_items(self):
407
        store = self.get_chk_bytes()
408
        node = LeafNode()
409
        node.set_maximum_size(10)
410
        node = node.map(("foo bar",), "baz quux")
411
        expected_key = ("sha1:d44cb6f0299b7e047da7f9e98f810e98f1dce1a7",)
412
        self.assertEqual([expected_key],
413
            list(node.serialise(store)))
414
        self.assertEqual("chkleaf:\n10\n1\n1\nfoo bar\x00baz quux\n",
415
            self.read_bytes(store, expected_key))
416
        self.assertEqual(expected_key, node.key())
417
418
    def test_unmap_missing(self):
419
        node = LeafNode()
420
        self.assertRaises(KeyError, node.unmap, ("foo bar",))
421
422
    def test_unmap_present(self):
423
        node = LeafNode()
424
        node = node.map(("foo bar",), "baz quux")
425
        result = node.unmap(("foo bar",))
426
        self.assertEqual(result, node)
427
        self.assertEqual({}, self.to_dict(node))
428
        self.assertEqual(0, len(node))
429
430
431
class TestInternalNode(TestCaseWithStore):
432
433
    def test_add_node_empty_oversized_no_common_sets_prefix(self):
434
        # adding a node with two children that is oversized will generate two
435
        # new leaf nodes, and a prefix width that cuts one byte off the longest
436
        # key (because that is sufficient to guarantee a split
437
        overpacked = LeafNode()
438
        overpacked.set_maximum_size(10)
439
        overpacked.map(("foo bar",), "baz")
440
        overpacked.map(("strange thing",), "it is")
441
        # at this point, map returned a new internal node that is already
442
        # packed, but that should have preserved the old node due to the 
443
        # functional idioms.. check to be sure:
444
        self.assertTrue(overpacked.maximum_size < overpacked._current_size())
445
        node = InternalNode()
446
        # We're not testing that the internal node rebalances yet
447
        node.set_maximum_size(0)
448
        node._add_node(overpacked)
449
        # 13 is the length of strange_thing serialised; as there is no node size
450
        # set, we pack the internal node as densely as possible.
451
        self.assertEqual(13, node._node_width)
452
        self.assertEqual(set(["strange thing", "foo bar\x00\x00\x00\x00\x00\x00"]),
453
            set(node._items.keys()))
454
        self.assertEqual(2, len(node))
455
        self.assertEqual({('strange thing',): 'it is'},
456
            self.to_dict(node._items["strange thing"]))
457
        self.assertEqual({('foo bar',): 'baz'},
458
            self.to_dict(node._items["foo bar\x00\x00\x00\x00\x00\x00"]))
459
460
    def test_iteritems_empty(self):
461
        node = InternalNode()
462
        self.assertEqual([], sorted(node.iteritems()))
463
464
    def test_iteritems_two_children(self):
465
        node = InternalNode()
466
        leaf1 = LeafNode()
467
        leaf1.map(('foo bar',), 'quux')
468
        leaf2 = LeafNode()
469
        leaf2 = LeafNode()
470
        leaf2.map(('strange',), 'beast')
471
        node._items['foo ba'] = leaf1
472
        node._items['strang'] = leaf2
473
        self.assertEqual([(('foo bar',), 'quux'), (('strange',), 'beast')],
474
            sorted(node.iteritems()))
475
476
    def test_iteritems_two_children_partial(self):
477
        node = InternalNode()
478
        leaf2 = LeafNode()
479
        leaf2 = LeafNode()
480
        leaf2.map(('strange',), 'beast')
481
        # This sets up a path that should not be followed - it will error if
482
        # the code tries to.
483
        node._items['foo ba'] = None
484
        node._items['strang'] = leaf2
485
        node._node_width = 6
486
        self.assertEqual([(('strange',), 'beast')],
487
            sorted(node.iteritems([('strange',), ('weird',)])))
488
489
    def test_iteritems_partial_empty(self):
490
        node = InternalNode()
491
        self.assertEqual([], sorted(node.iteritems([('missing',)])))
492
493
    def test_map_to_existing_child(self):
494
        # mapping a new key which is in a child of an internal node maps
495
        # recursively.
496
        overpacked = LeafNode()
497
        overpacked.set_maximum_size(10)
498
        overpacked.map(("foo bar",), "baz")
499
        node = overpacked.map(("foo baz",), "it is")
500
        self.assertIsInstance(node, InternalNode)
501
        # Now, increase the maximum size limit on the subnode for foo bar
502
        child = node._items[node._serialised_key(("foo bar",))]
503
        child.set_maximum_size(200)
504
        # And map a new key into node, which will land in the same child node
505
        result = node.map(("foo bar baz",), "new value")
506
        self.assertTrue(result is node)
507
        self.assertEqual(3, len(result))
508
        self.assertEqual(2, len(child))
509
        self.assertEqual({('foo bar',): 'baz',
510
            ('foo bar baz',): 'new value', ('foo baz',): 'it is'},
511
            self.to_dict(node))
512
513
    def test_map_to_existing_child_exceed_child_size_not_internal_size(self):
514
        # mapping a new key which is in a child of an internal node maps
515
        # recursively, and when the child splits that is accomodated within the
516
        # internal node if there is room for another child pointer.
517
        overpacked = LeafNode()
518
        overpacked.set_maximum_size(40)
519
        overpacked.map(("foo bar",), "baz baz baz baz baz baz baz")
520
        node = overpacked.map(("foo baz",), "it is it is it is it is it is")
521
        self.assertIsInstance(node, InternalNode)
522
        # And map a new key into node, which will land in the same child path
523
        # within node, but trigger a spill event on the child, and should end
524
        # up with 3 pointers in node (as the pointers can fit in the node
525
        # space.
526
        result = node.map(("foo bar baz",),
527
            "new value new value new value new value new value")
528
        self.assertTrue(result is node)
529
        self.assertEqual(3, len(result))
530
        # We should have one child for foo bar
531
        child = node._items[node._serialised_key(("foo bar\x00",))]
532
        self.assertIsInstance(child, LeafNode)
533
        self.assertEqual(1, len(child))
534
        # And one for 'foo bar '
535
        child = node._items[node._serialised_key(("foo bar ",))]
536
        self.assertIsInstance(child, LeafNode)
537
        self.assertEqual(1, len(child))
538
        self.assertEqual({('foo bar',): 'baz baz baz baz baz baz baz',
539
            ('foo bar baz',): 'new value new value new value',
540
            ('foo baz',): 'it is it is it is it is it is'},
541
            self.to_dict(node))
542
543
    def test_map_to_new_child(self):
544
        # mapping a new key which is in a child of an internal node maps
545
        # recursively.
546
        overpacked = LeafNode()
547
        overpacked.set_maximum_size(10)
548
        overpacked.map(("foo bar",), "baz")
549
        node = overpacked.map(("foo baz",), "it is")
550
        self.assertIsInstance(node, InternalNode)
551
        # Map a new key into node, which will land in a new child node
552
        result = node.map(("quux",), "new value")
553
        # Now, increase the maximum size limit on the subnode for foo bar
554
        child = node._items[node._serialised_key(("quux",))]
555
        self.assertTrue(result is node)
556
        self.assertEqual(3, len(result))
557
        self.assertEqual(1, len(child))
558
        self.assertEqual({('foo bar',): 'baz',
559
            ('quux',): 'new value', ('foo baz',): 'it is'},
560
            self.to_dict(node))
561
562
    def test_unmap_second_last_shrinks_to_other_branch(self):
563
        # unmapping the second last child of an internal node downgrades it to
564
        # a leaf node.
565
        overpacked = LeafNode()
566
        overpacked.set_maximum_size(10)
567
        overpacked.map(("foo bar",), "baz")
568
        node = overpacked.map(("strange thing",), "it is")
569
        self.assertIsInstance(node, InternalNode)
570
        result = node.unmap(("foo bar",))
571
        self.assertIsInstance(result, LeafNode)
572
        self.assertEqual({("strange thing",): "it is"}, self.to_dict(result))