/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.21 by Robert Collins
BROKEN: multi level CHKMap tries, unfinished, subsystem in flux.
19
from bzrlib.chk_map import (
20
    CHKMap,
21
    InternalNode,
22
    LeafNode,
23
    _deserialise,
24
    )
3735.2.8 by Robert Collins
New chk_map module for use in tree based inventory storage.
25
from bzrlib.tests import TestCaseWithTransport
26
27
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
28
class TestCaseWithStore(TestCaseWithTransport):
3735.2.8 by Robert Collins
New chk_map module for use in tree based inventory storage.
29
30
    def get_chk_bytes(self):
31
        # The eassiest way to get a CHK store is a development3 repository and
32
        # then work with the chk_bytes attribute directly.
33
        repo = self.make_repository(".", format="development3")
34
        repo.lock_write()
35
        self.addCleanup(repo.unlock)
36
        repo.start_write_group()
37
        self.addCleanup(repo.abort_write_group)
38
        return repo.chk_bytes
39
3735.2.21 by Robert Collins
BROKEN: multi level CHKMap tries, unfinished, subsystem in flux.
40
    def _get_map(self, a_dict, maximum_size=0):
41
        chk_bytes = self.get_chk_bytes()
42
        root_key = CHKMap.from_dict(chk_bytes, a_dict, maximum_size=maximum_size)
43
        chkmap = CHKMap(chk_bytes, root_key)
44
        return chkmap
45
3735.2.8 by Robert Collins
New chk_map module for use in tree based inventory storage.
46
    def read_bytes(self, chk_bytes, key):
47
        stream = chk_bytes.get_record_stream([key], 'unordered', True)
48
        return stream.next().get_bytes_as("fulltext")
49
3735.2.21 by Robert Collins
BROKEN: multi level CHKMap tries, unfinished, subsystem in flux.
50
    def to_dict(self, node, *args):
51
        return dict(node.iteritems(*args))
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
52
53
54
class TestMap(TestCaseWithStore):
55
3735.2.8 by Robert Collins
New chk_map module for use in tree based inventory storage.
56
    def assertHasABMap(self, chk_bytes):
3735.2.24 by Robert Collins
test_chk_map tests all passing.
57
        root_key = ('sha1:f14dd34def95036bc06bb5c0ed95437d7383a04a',)
3735.2.8 by Robert Collins
New chk_map module for use in tree based inventory storage.
58
        self.assertEqual(
3735.2.24 by Robert Collins
test_chk_map tests all passing.
59
            'chkleaf:\n0\n1\n1\na\x00b\n',
3735.2.8 by Robert Collins
New chk_map module for use in tree based inventory storage.
60
            self.read_bytes(chk_bytes, root_key))
3735.2.24 by Robert Collins
test_chk_map tests all passing.
61
        return root_key
3735.2.8 by Robert Collins
New chk_map module for use in tree based inventory storage.
62
63
    def assertHasEmptyMap(self, chk_bytes):
3735.2.24 by Robert Collins
test_chk_map tests all passing.
64
        root_key = ('sha1:4e6482a3a5cb2d61699971ac77befe11a0ec5779',)
65
        self.assertEqual("chkleaf:\n0\n1\n0\n", self.read_bytes(chk_bytes, root_key))
66
        return root_key
3735.2.17 by Robert Collins
Cache node length to avoid full iteration on __len__ calls.
67
3735.2.8 by Robert Collins
New chk_map module for use in tree based inventory storage.
68
    def test_from_dict_empty(self):
69
        chk_bytes = self.get_chk_bytes()
70
        root_key = CHKMap.from_dict(chk_bytes, {})
3735.2.24 by Robert Collins
test_chk_map tests all passing.
71
        # Check the data was saved and inserted correctly.
72
        expected_root_key = self.assertHasEmptyMap(chk_bytes)
73
        self.assertEqual(expected_root_key, root_key)
3735.2.8 by Robert Collins
New chk_map module for use in tree based inventory storage.
74
75
    def test_from_dict_ab(self):
76
        chk_bytes = self.get_chk_bytes()
77
        root_key = CHKMap.from_dict(chk_bytes, {"a":"b"})
3735.2.24 by Robert Collins
test_chk_map tests all passing.
78
        # Check the data was saved and inserted correctly.
79
        expected_root_key = self.assertHasABMap(chk_bytes)
80
        self.assertEqual(expected_root_key, root_key)
3735.2.8 by Robert Collins
New chk_map module for use in tree based inventory storage.
81
82
    def test_apply_empty_ab(self):
83
        # applying a delta (None, "a", "b") to an empty chkmap generates the
84
        # same map as from_dict_ab.
85
        chk_bytes = self.get_chk_bytes()
86
        root_key = CHKMap.from_dict(chk_bytes, {})
87
        chkmap = CHKMap(chk_bytes, root_key)
88
        new_root = chkmap.apply_delta([(None, "a", "b")])
3735.2.24 by Robert Collins
test_chk_map tests all passing.
89
        # Check the data was saved and inserted correctly.
90
        expected_root_key = self.assertHasABMap(chk_bytes)
91
        self.assertEqual(expected_root_key, new_root)
3735.2.8 by Robert Collins
New chk_map module for use in tree based inventory storage.
92
        # The update should have left us with an in memory root node, with an
93
        # updated key.
94
        self.assertEqual(new_root, chkmap._root_node._key)
95
96
    def test_apply_ab_empty(self):
97
        # applying a delta ("a", None, None) to an empty chkmap generates the
98
        # same map as from_dict_ab.
99
        chk_bytes = self.get_chk_bytes()
3735.2.24 by Robert Collins
test_chk_map tests all passing.
100
        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.
101
        chkmap = CHKMap(chk_bytes, root_key)
3735.2.24 by Robert Collins
test_chk_map tests all passing.
102
        new_root = chkmap.apply_delta([(("a",), None, None)])
103
        # Check the data was saved and inserted correctly.
104
        expected_root_key = self.assertHasEmptyMap(chk_bytes)
105
        self.assertEqual(expected_root_key, new_root)
3735.2.8 by Robert Collins
New chk_map module for use in tree based inventory storage.
106
        # The update should have left us with an in memory root node, with an
107
        # updated key.
108
        self.assertEqual(new_root, chkmap._root_node._key)
109
110
    def test_iteritems_empty(self):
111
        chk_bytes = self.get_chk_bytes()
112
        root_key = CHKMap.from_dict(chk_bytes, {})
113
        chkmap = CHKMap(chk_bytes, root_key)
114
        self.assertEqual([], list(chkmap.iteritems()))
115
116
    def test_iteritems_two_items(self):
117
        chk_bytes = self.get_chk_bytes()
118
        root_key = CHKMap.from_dict(chk_bytes,
119
            {"a":"content here", "b":"more content"})
120
        chkmap = CHKMap(chk_bytes, root_key)
3735.2.24 by Robert Collins
test_chk_map tests all passing.
121
        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.
122
            sorted(list(chkmap.iteritems())))
123
3735.2.9 by Robert Collins
Get a working chk_map using inventory implementation bootstrapped.
124
    def test_iteritems_selected_one_of_two_items(self):
3735.2.24 by Robert Collins
test_chk_map tests all passing.
125
        chkmap = self._get_map( {("a",):"content here", ("b",):"more content"})
126
        self.assertEqual({("a",): "content here"},
127
            self.to_dict(chkmap, [("a",)]))
3735.2.9 by Robert Collins
Get a working chk_map using inventory implementation bootstrapped.
128
3735.2.17 by Robert Collins
Cache node length to avoid full iteration on __len__ calls.
129
    def test___len__empty(self):
130
        chkmap = self._get_map({})
131
        self.assertEqual(0, len(chkmap))
132
133
    def test___len__2(self):
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
134
        chkmap = self._get_map({"foo":"bar", "gam":"quux"})
135
        self.assertEqual(2, len(chkmap))
136
3735.2.21 by Robert Collins
BROKEN: multi level CHKMap tries, unfinished, subsystem in flux.
137
    def test_max_size_100_bytes_new(self):
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
138
        # 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.
139
        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.
140
        # We expect three nodes:
141
        # A root, with two children, and with two key prefixes - k1 to one, and
142
        # k2 to the other as our node splitting is only just being developed.
143
        # The maximum size should be embedded
144
        chkmap._ensure_root()
145
        self.assertEqual(100, chkmap._root_node.maximum_size)
3735.2.21 by Robert Collins
BROKEN: multi level CHKMap tries, unfinished, subsystem in flux.
146
        self.assertEqual(1, chkmap._root_node._key_width)
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
147
        # 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.
148
        self.assertEqual(2, len(chkmap._root_node._items))
149
        self.assertEqual("k", chkmap._root_node.unique_serialised_prefix())
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
150
        # The actual nodes pointed at will change as serialisers change; so
151
        # here we test that the key prefix is correct; then load the nodes and
152
        # check they have the right pointed at key; whether they have the
153
        # 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.
154
        # don't check that in detail - rather we just check the aggregate
155
        # value.
156
        nodes = sorted(chkmap._root_node._items.items())
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
157
        ptr1 = nodes[0]
158
        ptr2 = nodes[1]
159
        self.assertEqual('k1', ptr1[0])
160
        self.assertEqual('k2', ptr2[0])
3735.2.21 by Robert Collins
BROKEN: multi level CHKMap tries, unfinished, subsystem in flux.
161
        node1 = _deserialise(chkmap._read_bytes(ptr1[1]), ptr1[1])
162
        self.assertIsInstance(node1, LeafNode)
163
        self.assertEqual(1, len(node1))
164
        self.assertEqual({('k1'*50,): 'v1'}, self.to_dict(node1, chkmap._store))
165
        node2 = _deserialise(chkmap._read_bytes(ptr2[1]), ptr2[1])
166
        self.assertIsInstance(node2, LeafNode)
167
        self.assertEqual(1, len(node2))
168
        self.assertEqual({('k2'*50,): 'v2'}, self.to_dict(node2, chkmap._store))
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
169
        # Having checked we have a good structure, check that the content is
170
        # still accessible.
171
        self.assertEqual(2, len(chkmap))
3735.2.21 by Robert Collins
BROKEN: multi level CHKMap tries, unfinished, subsystem in flux.
172
        self.assertEqual({("k1"*50,): "v1", ("k2"*50,): "v2"},
173
            self.to_dict(chkmap))
174
175
    def test_init_root_is_LeafNode_new(self):
176
        chk_bytes = self.get_chk_bytes()
177
        chkmap = CHKMap(chk_bytes, None)
178
        self.assertIsInstance(chkmap._root_node, LeafNode)
179
        self.assertEqual({}, self.to_dict(chkmap))
180
        self.assertEqual(0, len(chkmap))
181
182
    def test_init_and_save_new(self):
183
        chk_bytes = self.get_chk_bytes()
184
        chkmap = CHKMap(chk_bytes, None)
185
        key = chkmap._save()
186
        leaf_node = LeafNode()
187
        self.assertEqual([key], leaf_node.serialise(chk_bytes))
188
189
    def test_map_first_item_new(self):
190
        chk_bytes = self.get_chk_bytes()
191
        chkmap = CHKMap(chk_bytes, None)
192
        chkmap.map(("foo,",), "bar")
193
        self.assertEqual({('foo,',): 'bar'}, self.to_dict(chkmap))
194
        self.assertEqual(1, len(chkmap))
195
        key = chkmap._save()
196
        leaf_node = LeafNode()
197
        leaf_node.map(chk_bytes, ("foo,",), "bar")
198
        self.assertEqual([key], leaf_node.serialise(chk_bytes))
199
200
    def test_unmap_last_item_root_is_leaf_new(self):
201
        chkmap = self._get_map({("k1"*50,): "v1", ("k2"*50,): "v2"})
202
        chkmap.unmap(("k1"*50,))
203
        chkmap.unmap(("k2"*50,))
204
        self.assertEqual(0, len(chkmap))
205
        self.assertEqual({}, self.to_dict(chkmap))
206
        key = chkmap._save()
207
        leaf_node = LeafNode()
208
        self.assertEqual([key], leaf_node.serialise(chkmap._store))
3735.2.17 by Robert Collins
Cache node length to avoid full iteration on __len__ calls.
209
3735.2.8 by Robert Collins
New chk_map module for use in tree based inventory storage.
210
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
211
class TestLeafNode(TestCaseWithStore):
212
213
    def test_current_size_empty(self):
214
        node = LeafNode()
215
        self.assertEqual(15, node._current_size())
216
217
    def test_current_size_size_changed(self):
218
        node = LeafNode()
219
        node.set_maximum_size(10)
220
        self.assertEqual(16, node._current_size())
221
222
    def test_current_size_width_changed(self):
223
        node = LeafNode()
224
        node._key_width = 10
225
        self.assertEqual(16, node._current_size())
226
227
    def test_current_size_items(self):
228
        node = LeafNode()
229
        base_size = node._current_size()
3735.2.24 by Robert Collins
test_chk_map tests all passing.
230
        node.map(None, ("foo bar",), "baz")
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
231
        self.assertEqual(base_size + 12, node._current_size())
232
233
    def test_deserialise_empty(self):
234
        node = LeafNode.deserialise("chkleaf:\n10\n1\n0\n", ("sha1:1234",))
235
        self.assertEqual(0, len(node))
236
        self.assertEqual(10, node.maximum_size)
237
        self.assertEqual(("sha1:1234",), node.key())
238
239
    def test_deserialise_items(self):
240
        node = LeafNode.deserialise(
241
            "chkleaf:\n0\n1\n2\nfoo bar\x00baz\nquux\x00blarh\n", ("sha1:1234",))
242
        self.assertEqual(2, len(node))
243
        self.assertEqual([(("foo bar",), "baz"), (("quux",), "blarh")],
3735.2.24 by Robert Collins
test_chk_map tests all passing.
244
            sorted(node.iteritems(None)))
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
245
3735.2.25 by Robert Collins
CHKInventory core tests passing.
246
    def test_deserialise_item_with_null_width_1(self):
247
        node = LeafNode.deserialise(
248
            "chkleaf:\n0\n1\n2\nfoo\x00bar\x00baz\nquux\x00blarh\n",
249
            ("sha1:1234",))
250
        self.assertEqual(2, len(node))
251
        self.assertEqual([(("foo",), "bar\x00baz"), (("quux",), "blarh")],
252
            sorted(node.iteritems(None)))
253
254
    def test_deserialise_item_with_null_width_2(self):
255
        node = LeafNode.deserialise(
256
            "chkleaf:\n0\n2\n2\nfoo\x001\x00bar\x00baz\nquux\x00\x00blarh\n",
257
            ("sha1:1234",))
258
        self.assertEqual(2, len(node))
259
        self.assertEqual([(("foo", "1"), "bar\x00baz"), (("quux", ""), "blarh")],
260
            sorted(node.iteritems(None)))
261
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
262
    def test_iteritems_selected_one_of_two_items(self):
263
        node = LeafNode.deserialise(
264
            "chkleaf:\n0\n1\n2\nfoo bar\x00baz\nquux\x00blarh\n", ("sha1:1234",))
265
        self.assertEqual(2, len(node))
266
        self.assertEqual([(("quux",), "blarh")],
3735.2.24 by Robert Collins
test_chk_map tests all passing.
267
            sorted(node.iteritems(None, [("quux",), ("qaz",)])))
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
268
269
    def test_key_new(self):
270
        node = LeafNode()
271
        self.assertEqual(None, node.key())
272
273
    def test_key_after_map(self):
274
        node = LeafNode.deserialise("chkleaf:\n10\n1\n0\n", ("sha1:1234",))
3735.2.24 by Robert Collins
test_chk_map tests all passing.
275
        node.map(None, ("foo bar",), "baz quux")
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
276
        self.assertEqual(None, node.key())
277
278
    def test_key_after_unmap(self):
279
        node = LeafNode.deserialise(
280
            "chkleaf:\n0\n1\n2\nfoo bar\x00baz\nquux\x00blarh\n", ("sha1:1234",))
3735.2.24 by Robert Collins
test_chk_map tests all passing.
281
        node.unmap(None, ("foo bar",))
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
282
        self.assertEqual(None, node.key())
283
3735.2.21 by Robert Collins
BROKEN: multi level CHKMap tries, unfinished, subsystem in flux.
284
    def test_map_exceeding_max_size_only_entry_new(self):
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
285
        node = LeafNode()
286
        node.set_maximum_size(10)
3735.2.21 by Robert Collins
BROKEN: multi level CHKMap tries, unfinished, subsystem in flux.
287
        result = node.map(None, ("foo bar",), "baz quux")
288
        self.assertEqual(("foo bar", [("", node)]), result)
289
        self.assertTrue(10 < node._current_size())
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
290
3735.2.21 by Robert Collins
BROKEN: multi level CHKMap tries, unfinished, subsystem in flux.
291
    def test_map_exceeding_max_size_second_entry_early_difference_new(self):
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
292
        node = LeafNode()
293
        node.set_maximum_size(10)
3735.2.21 by Robert Collins
BROKEN: multi level CHKMap tries, unfinished, subsystem in flux.
294
        node.map(None, ("foo bar",), "baz quux")
295
        prefix, result = list(node.map(None, ("blue",), "red"))
296
        self.assertEqual("", prefix)
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
297
        self.assertEqual(2, len(result))
3735.2.21 by Robert Collins
BROKEN: multi level CHKMap tries, unfinished, subsystem in flux.
298
        split_chars = set([result[0][0], result[1][0]])
299
        self.assertEqual(set(["f", "b"]), split_chars)
300
        nodes = dict(result)
301
        node = nodes["f"]
302
        self.assertEqual({("foo bar",): "baz quux"}, self.to_dict(node, None))
303
        self.assertEqual(10, node.maximum_size)
304
        self.assertEqual(1, node._key_width)
305
        node = nodes["b"]
306
        self.assertEqual({("blue",): "red"}, self.to_dict(node, None))
307
        self.assertEqual(10, node.maximum_size)
308
        self.assertEqual(1, node._key_width)
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
309
310
    def test_map_first(self):
311
        node = LeafNode()
3735.2.24 by Robert Collins
test_chk_map tests all passing.
312
        result = node.map(None, ("foo bar",), "baz quux")
313
        self.assertEqual(("foo bar", [("", node)]), result)
314
        self.assertEqual({("foo bar",):"baz quux"}, self.to_dict(node, None))
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
315
        self.assertEqual(1, len(node))
316
317
    def test_map_second(self):
318
        node = LeafNode()
3735.2.24 by Robert Collins
test_chk_map tests all passing.
319
        node.map(None, ("foo bar",), "baz quux")
320
        result = node.map(None, ("bingo",), "bango")
321
        self.assertEqual(("", [("", node)]), result)
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
322
        self.assertEqual({("foo bar",):"baz quux", ("bingo",):"bango"},
3735.2.24 by Robert Collins
test_chk_map tests all passing.
323
            self.to_dict(node, None))
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
324
        self.assertEqual(2, len(node))
325
326
    def test_map_replacement(self):
327
        node = LeafNode()
3735.2.24 by Robert Collins
test_chk_map tests all passing.
328
        node.map(None, ("foo bar",), "baz quux")
329
        result = node.map(None, ("foo bar",), "bango")
330
        self.assertEqual(("foo bar", [("", node)]), result)
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
331
        self.assertEqual({("foo bar",): "bango"},
3735.2.24 by Robert Collins
test_chk_map tests all passing.
332
            self.to_dict(node, None))
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
333
        self.assertEqual(1, len(node))
334
335
    def test_serialise_empty(self):
336
        store = self.get_chk_bytes()
337
        node = LeafNode()
338
        node.set_maximum_size(10)
339
        expected_key = ("sha1:62cc3565b48b0e830216e652cf99c6bd6b05b4b9",)
340
        self.assertEqual([expected_key],
341
            list(node.serialise(store)))
342
        self.assertEqual("chkleaf:\n10\n1\n0\n", self.read_bytes(store, expected_key))
343
        self.assertEqual(expected_key, node.key())
344
345
    def test_serialise_items(self):
346
        store = self.get_chk_bytes()
347
        node = LeafNode()
348
        node.set_maximum_size(10)
3735.2.24 by Robert Collins
test_chk_map tests all passing.
349
        node.map(None, ("foo bar",), "baz quux")
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
350
        expected_key = ("sha1:d44cb6f0299b7e047da7f9e98f810e98f1dce1a7",)
351
        self.assertEqual([expected_key],
352
            list(node.serialise(store)))
353
        self.assertEqual("chkleaf:\n10\n1\n1\nfoo bar\x00baz quux\n",
354
            self.read_bytes(store, expected_key))
355
        self.assertEqual(expected_key, node.key())
356
3735.2.21 by Robert Collins
BROKEN: multi level CHKMap tries, unfinished, subsystem in flux.
357
    def test_unique_serialised_prefix_empty_new(self):
358
        node = LeafNode()
359
        self.assertEqual("", node.unique_serialised_prefix())
360
361
    def test_unique_serialised_prefix_one_item_new(self):
362
        node = LeafNode()
3735.2.24 by Robert Collins
test_chk_map tests all passing.
363
        node.map(None, ("foo bar", "baz"), "baz quux")
3735.2.21 by Robert Collins
BROKEN: multi level CHKMap tries, unfinished, subsystem in flux.
364
        self.assertEqual("foo bar\x00baz", node.unique_serialised_prefix())
365
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
366
    def test_unmap_missing(self):
367
        node = LeafNode()
3735.2.24 by Robert Collins
test_chk_map tests all passing.
368
        self.assertRaises(KeyError, node.unmap, None, ("foo bar",))
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
369
370
    def test_unmap_present(self):
371
        node = LeafNode()
3735.2.24 by Robert Collins
test_chk_map tests all passing.
372
        node.map(None, ("foo bar",), "baz quux")
373
        result = node.unmap(None, ("foo bar",))
374
        self.assertEqual(node, result)
375
        self.assertEqual({}, self.to_dict(node, None))
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
376
        self.assertEqual(0, len(node))
377
378
379
class TestInternalNode(TestCaseWithStore):
380
3735.2.21 by Robert Collins
BROKEN: multi level CHKMap tries, unfinished, subsystem in flux.
381
    def test_add_node_empty_new(self):
382
        node = InternalNode()
383
        child = LeafNode()
384
        child.set_maximum_size(100)
385
        child.map(None, ("foo",), "bar")
386
        node.add_node("foo", child)
387
        # Note that node isn't strictly valid now as a tree (only one child),
388
        # but thats ok for this test.
389
        # The first child defines the node's width:
390
        self.assertEqual(3, node._node_width)
391
        # We should be able to iterate over the contents without doing IO.
392
        self.assertEqual({('foo',): 'bar'}, self.to_dict(node, None))
393
        # The length should be known:
394
        self.assertEqual(1, len(node))
395
        # serialising the node should serialise the child and the node.
396
        chk_bytes = self.get_chk_bytes()
397
        keys = list(node.serialise(chk_bytes))
398
        child_key = child.serialise(chk_bytes)[0]
399
        self.assertEqual(
400
            [child_key, ('sha1:db23b260c2bf46bf7446c39f91668900a2491610',)],
401
            keys)
402
        # We should be able to access deserialised content.
403
        bytes = self.read_bytes(chk_bytes, keys[1])
404
        node = _deserialise(bytes, keys[1])
405
        self.assertEqual(1, len(node))
406
        self.assertEqual({('foo',): 'bar'}, self.to_dict(node, chk_bytes))
407
        self.assertEqual(3, node._node_width)
408
409
    def test_add_node_resets_key_new(self):
410
        node = InternalNode()
411
        child = LeafNode()
412
        child.set_maximum_size(100)
413
        child.map(None, ("foo",), "bar")
414
        node.add_node("foo", child)
415
        chk_bytes = self.get_chk_bytes()
416
        keys = list(node.serialise(chk_bytes))
417
        self.assertEqual(keys[1], node._key)
418
        node.add_node("fos", child)
419
        self.assertEqual(None, node._key)
420
421
#    def test_add_node_empty_oversized_one_ok_new(self):
422
#    def test_add_node_one_oversized_second_kept_minimum_fan(self):
423
#    def test_add_node_two_oversized_third_kept_minimum_fan(self):
424
#    def test_add_node_one_oversized_second_splits_errors(self):
425
426
    def test_iteritems_empty_new(self):
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
427
        node = InternalNode()
3735.2.21 by Robert Collins
BROKEN: multi level CHKMap tries, unfinished, subsystem in flux.
428
        self.assertEqual([], sorted(node.iteritems(None)))
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
429
430
    def test_iteritems_two_children(self):
431
        node = InternalNode()
432
        leaf1 = LeafNode()
3735.2.21 by Robert Collins
BROKEN: multi level CHKMap tries, unfinished, subsystem in flux.
433
        leaf1.map(None, ('foo bar',), 'quux')
434
        leaf2 = LeafNode()
435
        leaf2.map(None, ('strange',), 'beast')
3735.2.24 by Robert Collins
test_chk_map tests all passing.
436
        node.add_node("f", leaf1)
437
        node.add_node("s", leaf2)
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
438
        self.assertEqual([(('foo bar',), 'quux'), (('strange',), 'beast')],
3735.2.24 by Robert Collins
test_chk_map tests all passing.
439
            sorted(node.iteritems(None)))
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
440
441
    def test_iteritems_two_children_partial(self):
442
        node = InternalNode()
3735.2.24 by Robert Collins
test_chk_map tests all passing.
443
        leaf1 = LeafNode()
444
        leaf1.map(None, ('foo bar',), 'quux')
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
445
        leaf2 = LeafNode()
3735.2.21 by Robert Collins
BROKEN: multi level CHKMap tries, unfinished, subsystem in flux.
446
        leaf2.map(None, ('strange',), 'beast')
3735.2.24 by Robert Collins
test_chk_map tests all passing.
447
        node.add_node("f", leaf1)
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
448
        # This sets up a path that should not be followed - it will error if
449
        # the code tries to.
3735.2.24 by Robert Collins
test_chk_map tests all passing.
450
        node._items['f'] = None
451
        node.add_node("s", leaf2)
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
452
        self.assertEqual([(('strange',), 'beast')],
3735.2.24 by Robert Collins
test_chk_map tests all passing.
453
            sorted(node.iteritems(None, [('strange',), ('weird',)])))
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
454
455
    def test_iteritems_partial_empty(self):
456
        node = InternalNode()
457
        self.assertEqual([], sorted(node.iteritems([('missing',)])))
458
3735.2.21 by Robert Collins
BROKEN: multi level CHKMap tries, unfinished, subsystem in flux.
459
    def test_map_to_new_child_new(self):
460
        chkmap = self._get_map({('k1',):'foo', ('k2',):'bar'}, maximum_size=10)
461
        chkmap._ensure_root()
462
        node = chkmap._root_node
463
        # Ensure test validity: nothing paged in below the root.
464
        self.assertEqual(2,
465
            len([value for value in node._items.values()
466
                if type(value) == tuple]))
467
        # now, mapping to k3 should add a k3 leaf
468
        prefix, nodes = node.map(None, ('k3',), 'quux')
469
        self.assertEqual("k", prefix)
470
        self.assertEqual([("", node)], nodes)
471
        # check new child details
472
        child = node._items['k3']
473
        self.assertIsInstance(child, LeafNode)
474
        self.assertEqual(1, len(child))
475
        self.assertEqual({('k3',): 'quux'}, self.to_dict(child, None))
476
        self.assertEqual(None, child._key)
477
        self.assertEqual(10, child.maximum_size)
478
        self.assertEqual(1, child._key_width)
479
        # Check overall structure:
480
        self.assertEqual(3, len(chkmap))
481
        self.assertEqual({('k1',): 'foo', ('k2',): 'bar', ('k3',): 'quux'},
482
            self.to_dict(chkmap))
483
        # serialising should only serialise the new data - k3 and the internal
484
        # node.
485
        keys = list(node.serialise(chkmap._store))
486
        child_key = child.serialise(chkmap._store)[0]
487
        self.assertEqual([child_key, keys[1]], keys)
488
489
    def test_map_to_child_child_splits_new(self):
490
        chkmap = self._get_map({('k1',):'foo', ('k22',):'bar'}, maximum_size=10)
491
        # Check for the canonical root value for this tree:
492
        self.assertEqual(('sha1:d3f06fc03d8f50845894d8d04cc5a3f47e62948d',),
493
            chkmap._root_node)
494
        chkmap._ensure_root()
495
        node = chkmap._root_node
496
        # Ensure test validity: nothing paged in below the root.
497
        self.assertEqual(2,
498
            len([value for value in node._items.values()
499
                if type(value) == tuple]))
500
        # now, mapping to k23 causes k22 ('k2' in node) to split into k22 and
501
        # k23, which for simplicity in the current implementation generates
502
        # a new internal node between node, and k22/k23.
503
        prefix, nodes = node.map(chkmap._store, ('k23',), 'quux')
504
        self.assertEqual("k", prefix)
505
        self.assertEqual([("", node)], nodes)
506
        # check new child details
507
        child = node._items['k2']
508
        self.assertIsInstance(child, InternalNode)
509
        self.assertEqual(2, len(child))
510
        self.assertEqual({('k22',): 'bar', ('k23',): 'quux'},
511
            self.to_dict(child, None))
512
        self.assertEqual(None, child._key)
513
        self.assertEqual(10, child.maximum_size)
514
        self.assertEqual(1, child._key_width)
515
        self.assertEqual(3, child._node_width)
516
        # Check overall structure:
517
        self.assertEqual(3, len(chkmap))
518
        self.assertEqual({('k1',): 'foo', ('k22',): 'bar', ('k23',): 'quux'},
519
            self.to_dict(chkmap))
520
        # serialising should only serialise the new data - although k22 hasn't
521
        # changed because its a special corner case (splitting on with only one
522
        # key leaves one node unaltered), in general k22 is serialised, so we
523
        # expect k22, k23, the new internal node, and node, to be serialised.
524
        keys = list(node.serialise(chkmap._store))
525
        child_key = child._key
526
        k22_key = child._items['k22']._key
527
        k23_key = child._items['k23']._key
528
        self.assertEqual([k22_key, k23_key, child_key, keys[-1]], keys)
529
        self.assertEqual(('sha1:d68cd97c95e847d3dc58c05537aa5fdcdf2cf5da',),
530
            keys[-1])
531
532
    def test_unmap_k23_from_k1_k22_k23_gives_k1_k22_tree_new(self):
533
        chkmap = self._get_map(
534
            {('k1',):'foo', ('k22',):'bar', ('k23',): 'quux'}, maximum_size=10)
535
        # Check we have the expected tree.
536
        self.assertEqual(('sha1:d68cd97c95e847d3dc58c05537aa5fdcdf2cf5da',),
537
            chkmap._root_node)
538
        chkmap._ensure_root()
539
        node = chkmap._root_node
540
        # unmapping k23 should give us a root, with k1 and k22 as direct
541
        # children.
542
        result = node.unmap(chkmap._store, ('k23',))
543
        # check the pointed-at object within node - k2 should now point at the
544
        # k22 leaf (which should not even have been paged in).
545
        ptr = node._items['k2']
546
        self.assertIsInstance(ptr, tuple)
547
        child = _deserialise(self.read_bytes(chkmap._store, ptr), ptr)
548
        self.assertIsInstance(child, LeafNode)
549
        self.assertEqual(1, len(child))
550
        self.assertEqual({('k22',): 'bar'},
551
            self.to_dict(child, None))
552
        # Check overall structure is instact:
553
        self.assertEqual(2, len(chkmap))
554
        self.assertEqual({('k1',): 'foo', ('k22',): 'bar'},
555
            self.to_dict(chkmap))
556
        # serialising should only serialise the new data - the root node.
557
        keys = list(node.serialise(chkmap._store))
558
        self.assertEqual([keys[-1]], keys)
559
        self.assertEqual(('sha1:d3f06fc03d8f50845894d8d04cc5a3f47e62948d',), keys[-1])
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
560
3735.2.23 by Robert Collins
Test unmapping with one child left but multiple keys.
561
    def test_unmap_k1_from_k1_k22_k23_gives_k22_k23_tree_new(self):
562
        chkmap = self._get_map(
563
            {('k1',):'foo', ('k22',):'bar', ('k23',): 'quux'}, maximum_size=10)
564
        # Check we have the expected tree.
565
        self.assertEqual(('sha1:d68cd97c95e847d3dc58c05537aa5fdcdf2cf5da',),
566
            chkmap._root_node)
567
        chkmap._ensure_root()
568
        node = chkmap._root_node
569
        k2_ptr = node._items['k2']
570
        # unmapping k21 should give us a root, with k22 and k23 as direct
571
        # children, and should not have needed to page in the subtree.
572
        result = node.unmap(chkmap._store, ('k1',))
573
        self.assertEqual(k2_ptr, result)
574
3735.2.21 by Robert Collins
BROKEN: multi level CHKMap tries, unfinished, subsystem in flux.
575
576
# leaf:
577
# map -> fits - done
578
# map -> doesn't fit - shrink from left till fits
579
#        key data to return: the common prefix, new nodes.
580
581
# unmap -> how to tell if siblings can be combined.
582
#          combing leaf nodes means expanding the prefix to the left; so gather the size of
583
#          all the leaf nodes addressed by expanding the prefix by 1; if any adjacent node
584
#          is an internal node, we know that that is a dense subtree - can't combine.
585
#          otherwise as soon as the sum of serialised values exceeds the split threshold
586
#          we know we can't combine - stop.
587
# unmap -> key return data - space in node, common prefix length? and key count
588
# internal: 
589
# variable length prefixes? -> later start with fixed width to get something going
590
# map -> fits - update pointer to leaf
591
#        return [prefix and node] - seems sound.
592
# map -> doesn't fit - find unique prefix and shift right
593
#        create internal nodes for all the partitions, return list of unique
594
#        prefixes and nodes.
595
# map -> new prefix - create a leaf
596
# unmap -> if child key count 0, remove
597
# unmap -> return space in node, common prefix length? (why?), key count
598
# map:
599
# map, if 1 node returned, use it, otherwise make an internal and populate.
600
# map - unmap - if empty, use empty leafnode (avoids special cases in driver
601
# code)
602
# map inits as empty leafnode.
603
# tools: 
604
# visualiser
605
606
607
# how to handle:
608
# AA, AB, AC, AD, BA
609
# packed internal node - ideal:
610
# AA, AB, AC, AD, BA
611
# single byte fanout - A,B,   AA,AB,AC,AD,     BA
612
# build order's:
613
# BA
614
# AB - split, but we want to end up with AB, BA, in one node, with 
615
# 1-4K get0