/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
6614.1.3 by Vincent Ladeuil
Fix assertEquals being deprecated by using assertEqual.
1
# Copyright (C) 2008-2012, 2016 Canonical Ltd
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
2
#
3
# This program is free software; you can redistribute it and/or modify
3641.3.29 by John Arbash Meinel
Cleanup the copyright headers
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.
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
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
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
16
#
17
18
"""Tests for btree indices."""
19
3641.3.6 by John Arbash Meinel
Dropping the number of nodes to 100k from 200k
20
import pprint
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
21
import zlib
22
6624 by Jelmer Vernooij
Merge Python3 porting work ('py3 pokes')
23
from .. import (
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
24
    errors,
4634.71.1 by John Arbash Meinel
Work around bug #402623 by allowing BTreeGraphIndex(...,unlimited_cache=True).
25
    fifo_cache,
26
    lru_cache,
4593.4.6 by John Arbash Meinel
Add a test that we can walk some of the keys on a given page
27
    osutils,
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
28
    tests,
5273.1.7 by Vincent Ladeuil
No more use of the get_transport imported *symbol*, all uses are through
29
    transport,
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
30
    )
6670.4.1 by Jelmer Vernooij
Update imports.
31
from ..bzr import (
32
    btree_index,
33
    )
6624 by Jelmer Vernooij
Merge Python3 porting work ('py3 pokes')
34
from ..tests import (
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
35
    TestCaseWithTransport,
5559.2.2 by Martin Pool
Change to using standard load_tests_apply_scenarios.
36
    scenarios,
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
37
    )
6624 by Jelmer Vernooij
Merge Python3 porting work ('py3 pokes')
38
from ..tests import (
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
39
    features,
40
    )
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
41
42
5559.2.2 by Martin Pool
Change to using standard load_tests_apply_scenarios.
43
load_tests = scenarios.load_tests_apply_scenarios
44
45
46
def btreeparser_scenarios():
6670.4.1 by Jelmer Vernooij
Update imports.
47
    import breezy.bzr._btree_serializer_py as py_module
4084.5.1 by Robert Collins
Bulk update all test adaptation into a single approach, using multiply_tests rather than test adapters.
48
    scenarios = [('python', {'parse_btree': py_module})]
4913.2.20 by John Arbash Meinel
Change all of the compiled_foo to compiled_foo_feature
49
    if compiled_btreeparser_feature.available():
5559.2.2 by Martin Pool
Change to using standard load_tests_apply_scenarios.
50
        scenarios.append(('C', 
51
            {'parse_btree': compiled_btreeparser_feature.module}))
52
    return scenarios
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
53
54
5967.12.1 by Martin Pool
Move all test features into bzrlib.tests.features
55
compiled_btreeparser_feature = features.ModuleAvailableFeature(
6670.4.3 by Jelmer Vernooij
Fix more imports.
56
    'breezy.bzr._btree_serializer_pyx')
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
57
58
59
class BTreeTestCase(TestCaseWithTransport):
60
    # test names here are suffixed by the key length and reference list count
61
    # that they test.
62
63
    def setUp(self):
6552.1.4 by Vincent Ladeuil
Remaining tests matching setup(self) that can be rewritten with super().
64
        super(BTreeTestCase, self).setUp()
4985.1.5 by Vincent Ladeuil
Deploying the new overrideAttr facility further reduces the complexity
65
        self.overrideAttr(btree_index, '_RESERVED_HEADER_BYTES', 100)
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
66
67
    def make_nodes(self, count, key_elements, reference_lists):
68
        """Generate count*key_elements sample nodes."""
69
        keys = []
70
        for prefix_pos in range(key_elements):
71
            if key_elements - 1:
72
                prefix = (str(prefix_pos) * 40,)
73
            else:
74
                prefix = ()
6651.2.2 by Martin
Apply 2to3 xrange fix and fix up with sixish range
75
            for pos in range(count):
3641.3.6 by John Arbash Meinel
Dropping the number of nodes to 100k from 200k
76
                # TODO: This creates odd keys. When count == 100,000, it
77
                #       creates a 240 byte key
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
78
                key = prefix + (str(pos) * 40,)
79
                value = "value:%s" % pos
80
                if reference_lists:
81
                    # generate some references
82
                    refs = []
83
                    for list_pos in range(reference_lists):
84
                        # as many keys in each list as its index + the key depth
85
                        # mod 2 - this generates both 0 length lists and
86
                        # ones slightly longer than the number of lists.
3641.3.6 by John Arbash Meinel
Dropping the number of nodes to 100k from 200k
87
                        # It also ensures we have non homogeneous lists.
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
88
                        refs.append([])
89
                        for ref_pos in range(list_pos + pos % 2):
90
                            if pos % 2:
91
                                # refer to a nearby key
92
                                refs[-1].append(prefix + ("ref" + str(pos - 1) * 40,))
93
                            else:
94
                                # serial of this ref in the ref list
95
                                refs[-1].append(prefix + ("ref" + str(ref_pos) * 40,))
96
                        refs[-1] = tuple(refs[-1])
97
                    refs = tuple(refs)
98
                else:
99
                    refs = ()
100
                keys.append((key, value, refs))
101
        return keys
102
3641.5.9 by John Arbash Meinel
Shrink the page size so that the three_level and iter_all
103
    def shrink_page_size(self):
104
        """Shrink the default page size so that less fits in a page."""
4985.1.5 by Vincent Ladeuil
Deploying the new overrideAttr facility further reduces the complexity
105
        self.overrideAttr(btree_index, '_PAGE_SIZE')
3641.5.9 by John Arbash Meinel
Shrink the page size so that the three_level and iter_all
106
        btree_index._PAGE_SIZE = 2048
107
6614.1.3 by Vincent Ladeuil
Fix assertEquals being deprecated by using assertEqual.
108
    def assertEqualApproxCompressed(self, expected, actual, slop=6):
6015.54.1 by Martin Packman
Add assertEqualsApproxCompressed to bt.test_btree_index as newer zlib gives smaller output
109
        """Check a count of compressed bytes is approximately as expected
110
111
        Relying on compressed length being stable even with fixed inputs is
112
        slightly bogus, but zlib is stable enough that this mostly works.
113
        """
114
        if not expected - slop < actual < expected + slop:
115
            self.fail("Expected around %d bytes compressed but got %d" %
116
                (expected, actual))
117
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
118
119
class TestBTreeBuilder(BTreeTestCase):
120
4744.2.7 by John Arbash Meinel
Add .clear_cache() members to GraphIndexBuilder and BTreeBuilder.
121
    def test_clear_cache(self):
122
        builder = btree_index.BTreeBuilder(reference_lists=0, key_elements=1)
123
        # This is a no-op, but we need the api to be consistent with other
124
        # BTreeGraphIndex apis.
125
        builder.clear_cache()
126
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
127
    def test_empty_1_0(self):
128
        builder = btree_index.BTreeBuilder(key_elements=1, reference_lists=0)
129
        # NamedTemporaryFile dies on builder.finish().read(). weird.
130
        temp_file = builder.finish()
131
        content = temp_file.read()
132
        del temp_file
133
        self.assertEqual(
3641.3.3 by John Arbash Meinel
Change the header to indicate these indexes are
134
            "B+Tree Graph Index 2\nnode_ref_lists=0\nkey_elements=1\nlen=0\n"
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
135
            "row_lengths=\n",
136
            content)
137
138
    def test_empty_2_1(self):
139
        builder = btree_index.BTreeBuilder(key_elements=2, reference_lists=1)
140
        # NamedTemporaryFile dies on builder.finish().read(). weird.
141
        temp_file = builder.finish()
142
        content = temp_file.read()
143
        del temp_file
144
        self.assertEqual(
3641.3.3 by John Arbash Meinel
Change the header to indicate these indexes are
145
            "B+Tree Graph Index 2\nnode_ref_lists=1\nkey_elements=2\nlen=0\n"
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
146
            "row_lengths=\n",
147
            content)
148
149
    def test_root_leaf_1_0(self):
150
        builder = btree_index.BTreeBuilder(key_elements=1, reference_lists=0)
151
        nodes = self.make_nodes(5, 1, 0)
152
        for node in nodes:
153
            builder.add_node(*node)
154
        # NamedTemporaryFile dies on builder.finish().read(). weird.
155
        temp_file = builder.finish()
156
        content = temp_file.read()
157
        del temp_file
4771.3.1 by John Arbash Meinel
We don't have to pad 'short' records.
158
        self.assertEqual(131, len(content))
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
159
        self.assertEqual(
3641.3.3 by John Arbash Meinel
Change the header to indicate these indexes are
160
            "B+Tree Graph Index 2\nnode_ref_lists=0\nkey_elements=1\nlen=5\n"
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
161
            "row_lengths=1\n",
162
            content[:73])
163
        node_content = content[73:]
164
        node_bytes = zlib.decompress(node_content)
165
        expected_node = ("type=leaf\n"
166
            "0000000000000000000000000000000000000000\x00\x00value:0\n"
167
            "1111111111111111111111111111111111111111\x00\x00value:1\n"
168
            "2222222222222222222222222222222222222222\x00\x00value:2\n"
169
            "3333333333333333333333333333333333333333\x00\x00value:3\n"
170
            "4444444444444444444444444444444444444444\x00\x00value:4\n")
171
        self.assertEqual(expected_node, node_bytes)
172
173
    def test_root_leaf_2_2(self):
174
        builder = btree_index.BTreeBuilder(key_elements=2, reference_lists=2)
175
        nodes = self.make_nodes(5, 2, 2)
176
        for node in nodes:
177
            builder.add_node(*node)
178
        # NamedTemporaryFile dies on builder.finish().read(). weird.
179
        temp_file = builder.finish()
180
        content = temp_file.read()
181
        del temp_file
4771.3.1 by John Arbash Meinel
We don't have to pad 'short' records.
182
        self.assertEqual(238, len(content))
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
183
        self.assertEqual(
3641.3.3 by John Arbash Meinel
Change the header to indicate these indexes are
184
            "B+Tree Graph Index 2\nnode_ref_lists=2\nkey_elements=2\nlen=10\n"
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
185
            "row_lengths=1\n",
186
            content[:74])
187
        node_content = content[74:]
188
        node_bytes = zlib.decompress(node_content)
189
        expected_node = (
190
            "type=leaf\n"
191
            "0000000000000000000000000000000000000000\x000000000000000000000000000000000000000000\x00\t0000000000000000000000000000000000000000\x00ref0000000000000000000000000000000000000000\x00value:0\n"
192
            "0000000000000000000000000000000000000000\x001111111111111111111111111111111111111111\x000000000000000000000000000000000000000000\x00ref0000000000000000000000000000000000000000\t0000000000000000000000000000000000000000\x00ref0000000000000000000000000000000000000000\r0000000000000000000000000000000000000000\x00ref0000000000000000000000000000000000000000\x00value:1\n"
193
            "0000000000000000000000000000000000000000\x002222222222222222222222222222222222222222\x00\t0000000000000000000000000000000000000000\x00ref0000000000000000000000000000000000000000\x00value:2\n"
194
            "0000000000000000000000000000000000000000\x003333333333333333333333333333333333333333\x000000000000000000000000000000000000000000\x00ref2222222222222222222222222222222222222222\t0000000000000000000000000000000000000000\x00ref2222222222222222222222222222222222222222\r0000000000000000000000000000000000000000\x00ref2222222222222222222222222222222222222222\x00value:3\n"
195
            "0000000000000000000000000000000000000000\x004444444444444444444444444444444444444444\x00\t0000000000000000000000000000000000000000\x00ref0000000000000000000000000000000000000000\x00value:4\n"
196
            "1111111111111111111111111111111111111111\x000000000000000000000000000000000000000000\x00\t1111111111111111111111111111111111111111\x00ref0000000000000000000000000000000000000000\x00value:0\n"
197
            "1111111111111111111111111111111111111111\x001111111111111111111111111111111111111111\x001111111111111111111111111111111111111111\x00ref0000000000000000000000000000000000000000\t1111111111111111111111111111111111111111\x00ref0000000000000000000000000000000000000000\r1111111111111111111111111111111111111111\x00ref0000000000000000000000000000000000000000\x00value:1\n"
198
            "1111111111111111111111111111111111111111\x002222222222222222222222222222222222222222\x00\t1111111111111111111111111111111111111111\x00ref0000000000000000000000000000000000000000\x00value:2\n"
199
            "1111111111111111111111111111111111111111\x003333333333333333333333333333333333333333\x001111111111111111111111111111111111111111\x00ref2222222222222222222222222222222222222222\t1111111111111111111111111111111111111111\x00ref2222222222222222222222222222222222222222\r1111111111111111111111111111111111111111\x00ref2222222222222222222222222222222222222222\x00value:3\n"
200
            "1111111111111111111111111111111111111111\x004444444444444444444444444444444444444444\x00\t1111111111111111111111111111111111111111\x00ref0000000000000000000000000000000000000000\x00value:4\n"
201
            ""
202
            )
203
        self.assertEqual(expected_node, node_bytes)
204
205
    def test_2_leaves_1_0(self):
206
        builder = btree_index.BTreeBuilder(key_elements=1, reference_lists=0)
3641.5.17 by John Arbash Meinel
Fix up the test suite now that things don't pack as well.
207
        nodes = self.make_nodes(400, 1, 0)
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
208
        for node in nodes:
209
            builder.add_node(*node)
210
        # NamedTemporaryFile dies on builder.finish().read(). weird.
211
        temp_file = builder.finish()
212
        content = temp_file.read()
213
        del temp_file
6614.1.3 by Vincent Ladeuil
Fix assertEquals being deprecated by using assertEqual.
214
        self.assertEqualApproxCompressed(9283, len(content))
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
215
        self.assertEqual(
3641.5.17 by John Arbash Meinel
Fix up the test suite now that things don't pack as well.
216
            "B+Tree Graph Index 2\nnode_ref_lists=0\nkey_elements=1\nlen=400\n"
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
217
            "row_lengths=1,2\n",
218
            content[:77])
219
        root = content[77:4096]
220
        leaf1 = content[4096:8192]
221
        leaf2 = content[8192:]
222
        root_bytes = zlib.decompress(root)
223
        expected_root = (
224
            "type=internal\n"
225
            "offset=0\n"
3641.5.17 by John Arbash Meinel
Fix up the test suite now that things don't pack as well.
226
            ) + ("307" * 40) + "\n"
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
227
        self.assertEqual(expected_root, root_bytes)
228
        # We already know serialisation works for leaves, check key selection:
229
        leaf1_bytes = zlib.decompress(leaf1)
230
        sorted_node_keys = sorted(node[0] for node in nodes)
231
        node = btree_index._LeafNode(leaf1_bytes, 1, 0)
5365.5.1 by John Arbash Meinel
Implement a custom parser for chk btree leaves.
232
        self.assertEqual(231, len(node))
233
        self.assertEqual(sorted_node_keys[:231], node.all_keys())
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
234
        leaf2_bytes = zlib.decompress(leaf2)
235
        node = btree_index._LeafNode(leaf2_bytes, 1, 0)
5365.5.1 by John Arbash Meinel
Implement a custom parser for chk btree leaves.
236
        self.assertEqual(400 - 231, len(node))
237
        self.assertEqual(sorted_node_keys[231:], node.all_keys())
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
238
239
    def test_last_page_rounded_1_layer(self):
240
        builder = btree_index.BTreeBuilder(key_elements=1, reference_lists=0)
241
        nodes = self.make_nodes(10, 1, 0)
242
        for node in nodes:
243
            builder.add_node(*node)
244
        # NamedTemporaryFile dies on builder.finish().read(). weird.
245
        temp_file = builder.finish()
246
        content = temp_file.read()
247
        del temp_file
6614.1.3 by Vincent Ladeuil
Fix assertEquals being deprecated by using assertEqual.
248
        self.assertEqualApproxCompressed(155, len(content))
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
249
        self.assertEqual(
3641.3.3 by John Arbash Meinel
Change the header to indicate these indexes are
250
            "B+Tree Graph Index 2\nnode_ref_lists=0\nkey_elements=1\nlen=10\n"
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
251
            "row_lengths=1\n",
252
            content[:74])
253
        # Check thelast page is well formed
254
        leaf2 = content[74:]
255
        leaf2_bytes = zlib.decompress(leaf2)
256
        node = btree_index._LeafNode(leaf2_bytes, 1, 0)
5365.5.1 by John Arbash Meinel
Implement a custom parser for chk btree leaves.
257
        self.assertEqual(10, len(node))
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
258
        sorted_node_keys = sorted(node[0] for node in nodes)
5365.5.1 by John Arbash Meinel
Implement a custom parser for chk btree leaves.
259
        self.assertEqual(sorted_node_keys, node.all_keys())
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
260
261
    def test_last_page_not_rounded_2_layer(self):
262
        builder = btree_index.BTreeBuilder(key_elements=1, reference_lists=0)
3641.5.17 by John Arbash Meinel
Fix up the test suite now that things don't pack as well.
263
        nodes = self.make_nodes(400, 1, 0)
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
264
        for node in nodes:
265
            builder.add_node(*node)
266
        # NamedTemporaryFile dies on builder.finish().read(). weird.
267
        temp_file = builder.finish()
268
        content = temp_file.read()
269
        del temp_file
6614.1.3 by Vincent Ladeuil
Fix assertEquals being deprecated by using assertEqual.
270
        self.assertEqualApproxCompressed(9283, len(content))
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
271
        self.assertEqual(
3641.5.17 by John Arbash Meinel
Fix up the test suite now that things don't pack as well.
272
            "B+Tree Graph Index 2\nnode_ref_lists=0\nkey_elements=1\nlen=400\n"
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
273
            "row_lengths=1,2\n",
274
            content[:77])
3641.3.30 by John Arbash Meinel
Rename _parse_btree to _btree_serializer
275
        # Check the last page is well formed
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
276
        leaf2 = content[8192:]
277
        leaf2_bytes = zlib.decompress(leaf2)
278
        node = btree_index._LeafNode(leaf2_bytes, 1, 0)
5365.5.1 by John Arbash Meinel
Implement a custom parser for chk btree leaves.
279
        self.assertEqual(400 - 231, len(node))
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
280
        sorted_node_keys = sorted(node[0] for node in nodes)
5365.5.1 by John Arbash Meinel
Implement a custom parser for chk btree leaves.
281
        self.assertEqual(sorted_node_keys[231:], node.all_keys())
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
282
283
    def test_three_level_tree_details(self):
284
        # The left most pointer in the second internal node in a row should
285
        # pointer to the second node that the internal node is for, _not_
286
        # the first, otherwise the first node overlaps with the last node of
287
        # the prior internal node on that row.
3641.5.9 by John Arbash Meinel
Shrink the page size so that the three_level and iter_all
288
        self.shrink_page_size()
289
        builder = btree_index.BTreeBuilder(key_elements=2, reference_lists=2)
290
        # 40K nodes is enough to create a two internal nodes on the second
291
        # level, with a 2K page size
292
        nodes = self.make_nodes(20000, 2, 2)
3641.3.5 by John Arbash Meinel
For iter_all and three_level tests adjust spill-at.
293
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
294
        for node in nodes:
295
            builder.add_node(*node)
6083.1.1 by Jelmer Vernooij
Use get_transport_from_{url,path} in more places.
296
        t = transport.get_transport_from_url('trace+' + self.get_url(''))
5273.1.7 by Vincent Ladeuil
No more use of the get_transport imported *symbol*, all uses are through
297
        size = t.put_file('index', self.time(builder.finish))
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
298
        del builder
5273.1.7 by Vincent Ladeuil
No more use of the get_transport imported *symbol*, all uses are through
299
        index = btree_index.BTreeGraphIndex(t, 'index', size)
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
300
        # Seed the metadata, we're using internal calls now.
301
        index.key_count()
302
        self.assertEqual(3, len(index._row_lengths),
303
            "Not enough rows: %r" % index._row_lengths)
304
        self.assertEqual(4, len(index._row_offsets))
305
        self.assertEqual(sum(index._row_lengths), index._row_offsets[-1])
306
        internal_nodes = index._get_internal_nodes([0, 1, 2])
307
        root_node = internal_nodes[0]
308
        internal_node1 = internal_nodes[1]
309
        internal_node2 = internal_nodes[2]
310
        # The left most node node2 points at should be one after the right most
311
        # node pointed at by node1.
312
        self.assertEqual(internal_node2.offset, 1 + len(internal_node1.keys))
313
        # The left most key of the second node pointed at by internal_node2
314
        # should be its first key. We can check this by looking for its first key
315
        # in the second node it points at
316
        pos = index._row_offsets[2] + internal_node2.offset + 1
317
        leaf = index._get_leaf_nodes([pos])[pos]
5365.5.1 by John Arbash Meinel
Implement a custom parser for chk btree leaves.
318
        self.assertTrue(internal_node2.keys[0] in leaf)
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
319
320
    def test_2_leaves_2_2(self):
321
        builder = btree_index.BTreeBuilder(key_elements=2, reference_lists=2)
3641.5.17 by John Arbash Meinel
Fix up the test suite now that things don't pack as well.
322
        nodes = self.make_nodes(100, 2, 2)
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
323
        for node in nodes:
324
            builder.add_node(*node)
325
        # NamedTemporaryFile dies on builder.finish().read(). weird.
326
        temp_file = builder.finish()
327
        content = temp_file.read()
328
        del temp_file
6614.1.3 by Vincent Ladeuil
Fix assertEquals being deprecated by using assertEqual.
329
        self.assertEqualApproxCompressed(12643, len(content))
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
330
        self.assertEqual(
3641.5.17 by John Arbash Meinel
Fix up the test suite now that things don't pack as well.
331
            "B+Tree Graph Index 2\nnode_ref_lists=2\nkey_elements=2\nlen=200\n"
332
            "row_lengths=1,3\n",
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
333
            content[:77])
334
        root = content[77:4096]
335
        leaf1 = content[4096:8192]
3641.5.17 by John Arbash Meinel
Fix up the test suite now that things don't pack as well.
336
        leaf2 = content[8192:12288]
337
        leaf3 = content[12288:]
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
338
        root_bytes = zlib.decompress(root)
339
        expected_root = (
340
            "type=internal\n"
341
            "offset=0\n"
3641.5.17 by John Arbash Meinel
Fix up the test suite now that things don't pack as well.
342
            + ("0" * 40) + "\x00" + ("91" * 40) + "\n"
343
            + ("1" * 40) + "\x00" + ("81" * 40) + "\n"
344
            )
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
345
        self.assertEqual(expected_root, root_bytes)
346
        # We assume the other leaf nodes have been written correctly - layering
347
        # FTW.
348
349
    def test_spill_index_stress_1_1(self):
350
        builder = btree_index.BTreeBuilder(key_elements=1, spill_at=2)
351
        nodes = [node[0:2] for node in self.make_nodes(16, 1, 0)]
352
        builder.add_node(*nodes[0])
353
        # Test the parts of the index that take up memory are doing so
354
        # predictably.
355
        self.assertEqual(1, len(builder._nodes))
3644.2.5 by John Arbash Meinel
Restore the exact old tests, only assert that
356
        self.assertIs(None, builder._nodes_by_key)
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
357
        builder.add_node(*nodes[1])
358
        self.assertEqual(0, len(builder._nodes))
3644.2.5 by John Arbash Meinel
Restore the exact old tests, only assert that
359
        self.assertIs(None, builder._nodes_by_key)
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
360
        self.assertEqual(1, len(builder._backing_indices))
361
        self.assertEqual(2, builder._backing_indices[0].key_count())
362
        # now back to memory
363
        builder.add_node(*nodes[2])
364
        self.assertEqual(1, len(builder._nodes))
3644.2.5 by John Arbash Meinel
Restore the exact old tests, only assert that
365
        self.assertIs(None, builder._nodes_by_key)
4168.3.4 by John Arbash Meinel
Restore the ability to spill, but prepare a flag to disable it.
366
        # And spills to a second backing index combing all
367
        builder.add_node(*nodes[3])
368
        self.assertEqual(0, len(builder._nodes))
369
        self.assertIs(None, builder._nodes_by_key)
370
        self.assertEqual(2, len(builder._backing_indices))
371
        self.assertEqual(None, builder._backing_indices[0])
372
        self.assertEqual(4, builder._backing_indices[1].key_count())
373
        # The next spills to the 2-len slot
374
        builder.add_node(*nodes[4])
375
        builder.add_node(*nodes[5])
376
        self.assertEqual(0, len(builder._nodes))
377
        self.assertIs(None, builder._nodes_by_key)
378
        self.assertEqual(2, len(builder._backing_indices))
379
        self.assertEqual(2, builder._backing_indices[0].key_count())
380
        self.assertEqual(4, builder._backing_indices[1].key_count())
381
        # Next spill combines
382
        builder.add_node(*nodes[6])
383
        builder.add_node(*nodes[7])
384
        self.assertEqual(3, len(builder._backing_indices))
385
        self.assertEqual(None, builder._backing_indices[0])
386
        self.assertEqual(None, builder._backing_indices[1])
387
        self.assertEqual(8, builder._backing_indices[2].key_count())
388
        # And so forth - counting up in binary.
389
        builder.add_node(*nodes[8])
390
        builder.add_node(*nodes[9])
391
        self.assertEqual(3, len(builder._backing_indices))
392
        self.assertEqual(2, builder._backing_indices[0].key_count())
393
        self.assertEqual(None, builder._backing_indices[1])
394
        self.assertEqual(8, builder._backing_indices[2].key_count())
395
        builder.add_node(*nodes[10])
396
        builder.add_node(*nodes[11])
397
        self.assertEqual(3, len(builder._backing_indices))
398
        self.assertEqual(None, builder._backing_indices[0])
399
        self.assertEqual(4, builder._backing_indices[1].key_count())
400
        self.assertEqual(8, builder._backing_indices[2].key_count())
401
        builder.add_node(*nodes[12])
402
        # Test that memory and disk are both used for query methods; and that
403
        # None is skipped over happily.
404
        self.assertEqual([(builder,) + node for node in sorted(nodes[:13])],
405
            list(builder.iter_all_entries()))
406
        # Two nodes - one memory one disk
6619.3.12 by Jelmer Vernooij
Use 2to3 set_literal fixer.
407
        self.assertEqual({(builder,) + node for node in nodes[11:13]},
4168.3.4 by John Arbash Meinel
Restore the ability to spill, but prepare a flag to disable it.
408
            set(builder.iter_entries([nodes[12][0], nodes[11][0]])))
409
        self.assertEqual(13, builder.key_count())
6619.3.12 by Jelmer Vernooij
Use 2to3 set_literal fixer.
410
        self.assertEqual({(builder,) + node for node in nodes[11:13]},
4168.3.4 by John Arbash Meinel
Restore the ability to spill, but prepare a flag to disable it.
411
            set(builder.iter_entries_prefix([nodes[12][0], nodes[11][0]])))
412
        builder.add_node(*nodes[13])
413
        self.assertEqual(3, len(builder._backing_indices))
414
        self.assertEqual(2, builder._backing_indices[0].key_count())
415
        self.assertEqual(4, builder._backing_indices[1].key_count())
416
        self.assertEqual(8, builder._backing_indices[2].key_count())
417
        builder.add_node(*nodes[14])
418
        builder.add_node(*nodes[15])
419
        self.assertEqual(4, len(builder._backing_indices))
420
        self.assertEqual(None, builder._backing_indices[0])
421
        self.assertEqual(None, builder._backing_indices[1])
422
        self.assertEqual(None, builder._backing_indices[2])
423
        self.assertEqual(16, builder._backing_indices[3].key_count())
424
        # Now finish, and check we got a correctly ordered tree
5273.1.7 by Vincent Ladeuil
No more use of the get_transport imported *symbol*, all uses are through
425
        t = self.get_transport('')
426
        size = t.put_file('index', builder.finish())
427
        index = btree_index.BTreeGraphIndex(t, 'index', size)
4168.3.4 by John Arbash Meinel
Restore the ability to spill, but prepare a flag to disable it.
428
        nodes = list(index.iter_all_entries())
429
        self.assertEqual(sorted(nodes), nodes)
430
        self.assertEqual(16, len(nodes))
431
432
    def test_spill_index_stress_1_1_no_combine(self):
433
        builder = btree_index.BTreeBuilder(key_elements=1, spill_at=2)
4168.3.6 by John Arbash Meinel
Add 'combine_backing_indices' as a flag for GraphIndex.set_optimize().
434
        builder.set_optimize(for_size=False, combine_backing_indices=False)
4168.3.4 by John Arbash Meinel
Restore the ability to spill, but prepare a flag to disable it.
435
        nodes = [node[0:2] for node in self.make_nodes(16, 1, 0)]
436
        builder.add_node(*nodes[0])
437
        # Test the parts of the index that take up memory are doing so
438
        # predictably.
439
        self.assertEqual(1, len(builder._nodes))
440
        self.assertIs(None, builder._nodes_by_key)
441
        builder.add_node(*nodes[1])
442
        self.assertEqual(0, len(builder._nodes))
443
        self.assertIs(None, builder._nodes_by_key)
444
        self.assertEqual(1, len(builder._backing_indices))
445
        self.assertEqual(2, builder._backing_indices[0].key_count())
446
        # now back to memory
447
        builder.add_node(*nodes[2])
448
        self.assertEqual(1, len(builder._nodes))
449
        self.assertIs(None, builder._nodes_by_key)
450
        # And spills to a second backing index but doesn't combine
451
        builder.add_node(*nodes[3])
452
        self.assertEqual(0, len(builder._nodes))
453
        self.assertIs(None, builder._nodes_by_key)
454
        self.assertEqual(2, len(builder._backing_indices))
4168.3.5 by John Arbash Meinel
Check that setting _combine_spilled_indices has the expected effect.
455
        for backing_index in builder._backing_indices:
456
            self.assertEqual(2, backing_index.key_count())
457
        # The next spills to the 3rd slot
4168.3.4 by John Arbash Meinel
Restore the ability to spill, but prepare a flag to disable it.
458
        builder.add_node(*nodes[4])
459
        builder.add_node(*nodes[5])
460
        self.assertEqual(0, len(builder._nodes))
461
        self.assertIs(None, builder._nodes_by_key)
4168.3.5 by John Arbash Meinel
Check that setting _combine_spilled_indices has the expected effect.
462
        self.assertEqual(3, len(builder._backing_indices))
463
        for backing_index in builder._backing_indices:
464
            self.assertEqual(2, backing_index.key_count())
465
        # Now spill a few more, and check that we don't combine
4168.3.4 by John Arbash Meinel
Restore the ability to spill, but prepare a flag to disable it.
466
        builder.add_node(*nodes[6])
467
        builder.add_node(*nodes[7])
468
        builder.add_node(*nodes[8])
469
        builder.add_node(*nodes[9])
470
        builder.add_node(*nodes[10])
471
        builder.add_node(*nodes[11])
472
        builder.add_node(*nodes[12])
4168.3.5 by John Arbash Meinel
Check that setting _combine_spilled_indices has the expected effect.
473
        self.assertEqual(6, len(builder._backing_indices))
474
        for backing_index in builder._backing_indices:
475
            self.assertEqual(2, backing_index.key_count())
4168.3.4 by John Arbash Meinel
Restore the ability to spill, but prepare a flag to disable it.
476
        # Test that memory and disk are both used for query methods; and that
477
        # None is skipped over happily.
478
        self.assertEqual([(builder,) + node for node in sorted(nodes[:13])],
479
            list(builder.iter_all_entries()))
480
        # Two nodes - one memory one disk
6619.3.12 by Jelmer Vernooij
Use 2to3 set_literal fixer.
481
        self.assertEqual({(builder,) + node for node in nodes[11:13]},
4168.3.4 by John Arbash Meinel
Restore the ability to spill, but prepare a flag to disable it.
482
            set(builder.iter_entries([nodes[12][0], nodes[11][0]])))
483
        self.assertEqual(13, builder.key_count())
6619.3.12 by Jelmer Vernooij
Use 2to3 set_literal fixer.
484
        self.assertEqual({(builder,) + node for node in nodes[11:13]},
4168.3.4 by John Arbash Meinel
Restore the ability to spill, but prepare a flag to disable it.
485
            set(builder.iter_entries_prefix([nodes[12][0], nodes[11][0]])))
486
        builder.add_node(*nodes[13])
487
        builder.add_node(*nodes[14])
488
        builder.add_node(*nodes[15])
4168.3.5 by John Arbash Meinel
Check that setting _combine_spilled_indices has the expected effect.
489
        self.assertEqual(8, len(builder._backing_indices))
490
        for backing_index in builder._backing_indices:
491
            self.assertEqual(2, backing_index.key_count())
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
492
        # Now finish, and check we got a correctly ordered tree
493
        transport = self.get_transport('')
494
        size = transport.put_file('index', builder.finish())
495
        index = btree_index.BTreeGraphIndex(transport, 'index', size)
496
        nodes = list(index.iter_all_entries())
497
        self.assertEqual(sorted(nodes), nodes)
498
        self.assertEqual(16, len(nodes))
499
3777.5.3 by John Arbash Meinel
Add Builder.set_optimize(for_size=True) for GraphIndexBuilder and BTreeBuilder.
500
    def test_set_optimize(self):
501
        builder = btree_index.BTreeBuilder(key_elements=2, reference_lists=2)
502
        builder.set_optimize(for_size=True)
503
        self.assertTrue(builder._optimize_for_size)
504
        builder.set_optimize(for_size=False)
505
        self.assertFalse(builder._optimize_for_size)
4168.3.6 by John Arbash Meinel
Add 'combine_backing_indices' as a flag for GraphIndex.set_optimize().
506
        # test that we can set combine_backing_indices without effecting
507
        # _optimize_for_size
508
        obj = object()
509
        builder._optimize_for_size = obj
510
        builder.set_optimize(combine_backing_indices=False)
511
        self.assertFalse(builder._combine_backing_indices)
512
        self.assertIs(obj, builder._optimize_for_size)
513
        builder.set_optimize(combine_backing_indices=True)
514
        self.assertTrue(builder._combine_backing_indices)
515
        self.assertIs(obj, builder._optimize_for_size)
3777.5.3 by John Arbash Meinel
Add Builder.set_optimize(for_size=True) for GraphIndexBuilder and BTreeBuilder.
516
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
517
    def test_spill_index_stress_2_2(self):
518
        # test that references and longer keys don't confuse things.
519
        builder = btree_index.BTreeBuilder(key_elements=2, reference_lists=2,
520
            spill_at=2)
521
        nodes = self.make_nodes(16, 2, 2)
522
        builder.add_node(*nodes[0])
523
        # Test the parts of the index that take up memory are doing so
524
        # predictably.
3644.2.1 by John Arbash Meinel
Change the IndexBuilders to not generate the nodes_by_key unless needed.
525
        self.assertEqual(1, len(builder._nodes))
3644.2.5 by John Arbash Meinel
Restore the exact old tests, only assert that
526
        self.assertIs(None, builder._nodes_by_key)
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
527
        builder.add_node(*nodes[1])
528
        self.assertEqual(0, len(builder._nodes))
3644.2.5 by John Arbash Meinel
Restore the exact old tests, only assert that
529
        self.assertIs(None, builder._nodes_by_key)
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
530
        self.assertEqual(1, len(builder._backing_indices))
531
        self.assertEqual(2, builder._backing_indices[0].key_count())
532
        # now back to memory
3644.2.6 by John Arbash Meinel
Restore the exact old tests, only assert that
533
        old = dict(builder._get_nodes_by_key()) #Build up the nodes by key dict
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
534
        builder.add_node(*nodes[2])
3644.2.1 by John Arbash Meinel
Change the IndexBuilders to not generate the nodes_by_key unless needed.
535
        self.assertEqual(1, len(builder._nodes))
3644.2.6 by John Arbash Meinel
Restore the exact old tests, only assert that
536
        self.assertIsNot(None, builder._nodes_by_key)
537
        self.assertNotEqual({}, builder._nodes_by_key)
538
        # We should have a new entry
539
        self.assertNotEqual(old, builder._nodes_by_key)
4168.3.4 by John Arbash Meinel
Restore the ability to spill, but prepare a flag to disable it.
540
        # And spills to a second backing index combing all
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
541
        builder.add_node(*nodes[3])
542
        self.assertEqual(0, len(builder._nodes))
3644.2.5 by John Arbash Meinel
Restore the exact old tests, only assert that
543
        self.assertIs(None, builder._nodes_by_key)
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
544
        self.assertEqual(2, len(builder._backing_indices))
4168.3.4 by John Arbash Meinel
Restore the ability to spill, but prepare a flag to disable it.
545
        self.assertEqual(None, builder._backing_indices[0])
546
        self.assertEqual(4, builder._backing_indices[1].key_count())
547
        # The next spills to the 2-len slot
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
548
        builder.add_node(*nodes[4])
549
        builder.add_node(*nodes[5])
550
        self.assertEqual(0, len(builder._nodes))
3644.2.5 by John Arbash Meinel
Restore the exact old tests, only assert that
551
        self.assertIs(None, builder._nodes_by_key)
4168.3.4 by John Arbash Meinel
Restore the ability to spill, but prepare a flag to disable it.
552
        self.assertEqual(2, len(builder._backing_indices))
553
        self.assertEqual(2, builder._backing_indices[0].key_count())
554
        self.assertEqual(4, builder._backing_indices[1].key_count())
555
        # Next spill combines
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
556
        builder.add_node(*nodes[6])
557
        builder.add_node(*nodes[7])
4168.3.4 by John Arbash Meinel
Restore the ability to spill, but prepare a flag to disable it.
558
        self.assertEqual(3, len(builder._backing_indices))
559
        self.assertEqual(None, builder._backing_indices[0])
560
        self.assertEqual(None, builder._backing_indices[1])
561
        self.assertEqual(8, builder._backing_indices[2].key_count())
562
        # And so forth - counting up in binary.
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
563
        builder.add_node(*nodes[8])
564
        builder.add_node(*nodes[9])
4168.3.4 by John Arbash Meinel
Restore the ability to spill, but prepare a flag to disable it.
565
        self.assertEqual(3, len(builder._backing_indices))
566
        self.assertEqual(2, builder._backing_indices[0].key_count())
567
        self.assertEqual(None, builder._backing_indices[1])
568
        self.assertEqual(8, builder._backing_indices[2].key_count())
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
569
        builder.add_node(*nodes[10])
570
        builder.add_node(*nodes[11])
4168.3.4 by John Arbash Meinel
Restore the ability to spill, but prepare a flag to disable it.
571
        self.assertEqual(3, len(builder._backing_indices))
572
        self.assertEqual(None, builder._backing_indices[0])
573
        self.assertEqual(4, builder._backing_indices[1].key_count())
574
        self.assertEqual(8, builder._backing_indices[2].key_count())
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
575
        builder.add_node(*nodes[12])
576
        # Test that memory and disk are both used for query methods; and that
577
        # None is skipped over happily.
578
        self.assertEqual([(builder,) + node for node in sorted(nodes[:13])],
579
            list(builder.iter_all_entries()))
580
        # Two nodes - one memory one disk
6619.3.12 by Jelmer Vernooij
Use 2to3 set_literal fixer.
581
        self.assertEqual({(builder,) + node for node in nodes[11:13]},
3644.2.5 by John Arbash Meinel
Restore the exact old tests, only assert that
582
            set(builder.iter_entries([nodes[12][0], nodes[11][0]])))
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
583
        self.assertEqual(13, builder.key_count())
6619.3.12 by Jelmer Vernooij
Use 2to3 set_literal fixer.
584
        self.assertEqual({(builder,) + node for node in nodes[11:13]},
3644.2.5 by John Arbash Meinel
Restore the exact old tests, only assert that
585
            set(builder.iter_entries_prefix([nodes[12][0], nodes[11][0]])))
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
586
        builder.add_node(*nodes[13])
4168.3.4 by John Arbash Meinel
Restore the ability to spill, but prepare a flag to disable it.
587
        self.assertEqual(3, len(builder._backing_indices))
588
        self.assertEqual(2, builder._backing_indices[0].key_count())
589
        self.assertEqual(4, builder._backing_indices[1].key_count())
590
        self.assertEqual(8, builder._backing_indices[2].key_count())
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
591
        builder.add_node(*nodes[14])
592
        builder.add_node(*nodes[15])
4168.3.4 by John Arbash Meinel
Restore the ability to spill, but prepare a flag to disable it.
593
        self.assertEqual(4, len(builder._backing_indices))
594
        self.assertEqual(None, builder._backing_indices[0])
595
        self.assertEqual(None, builder._backing_indices[1])
596
        self.assertEqual(None, builder._backing_indices[2])
597
        self.assertEqual(16, builder._backing_indices[3].key_count())
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
598
        # Now finish, and check we got a correctly ordered tree
599
        transport = self.get_transport('')
600
        size = transport.put_file('index', builder.finish())
601
        index = btree_index.BTreeGraphIndex(transport, 'index', size)
602
        nodes = list(index.iter_all_entries())
603
        self.assertEqual(sorted(nodes), nodes)
604
        self.assertEqual(16, len(nodes))
605
606
    def test_spill_index_duplicate_key_caught_on_finish(self):
607
        builder = btree_index.BTreeBuilder(key_elements=1, spill_at=2)
608
        nodes = [node[0:2] for node in self.make_nodes(16, 1, 0)]
609
        builder.add_node(*nodes[0])
610
        builder.add_node(*nodes[1])
611
        builder.add_node(*nodes[0])
612
        self.assertRaises(errors.BadIndexDuplicateKey, builder.finish)
613
614
615
class TestBTreeIndex(BTreeTestCase):
616
617
    def make_index(self, ref_lists=0, key_elements=1, nodes=[]):
618
        builder = btree_index.BTreeBuilder(reference_lists=ref_lists,
619
            key_elements=key_elements)
620
        for key, value, references in nodes:
621
            builder.add_node(key, value, references)
622
        stream = builder.finish()
6083.1.1 by Jelmer Vernooij
Use get_transport_from_{url,path} in more places.
623
        trans = transport.get_transport_from_url('trace+' + self.get_url())
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
624
        size = trans.put_file('index', stream)
625
        return btree_index.BTreeGraphIndex(trans, 'index', size)
626
5074.4.1 by John Arbash Meinel
Add an offset flag to BTreeGraphIndex.
627
    def make_index_with_offset(self, ref_lists=1, key_elements=1, nodes=[],
628
                               offset=0):
629
        builder = btree_index.BTreeBuilder(key_elements=key_elements,
630
                                           reference_lists=ref_lists)
631
        builder.add_nodes(nodes)
632
        transport = self.get_transport('')
5074.4.7 by John Arbash Meinel
Workaround a strange bug about the file closing before it is read.
633
        # NamedTemporaryFile dies on builder.finish().read(). weird.
634
        temp_file = builder.finish()
635
        content = temp_file.read()
636
        del temp_file
5074.4.1 by John Arbash Meinel
Add an offset flag to BTreeGraphIndex.
637
        size = len(content)
638
        transport.put_bytes('index', (' '*offset)+content)
639
        return btree_index.BTreeGraphIndex(transport, 'index', size=size,
640
                                           offset=offset)
641
4744.2.6 by John Arbash Meinel
Start exposing an GraphIndex.clear_cache() member.
642
    def test_clear_cache(self):
643
        nodes = self.make_nodes(160, 2, 2)
644
        index = self.make_index(ref_lists=2, key_elements=2, nodes=nodes)
645
        self.assertEqual(1, len(list(index.iter_entries([nodes[30][0]]))))
646
        self.assertEqual([1, 4], index._row_lengths)
647
        self.assertIsNot(None, index._root_node)
6656.1.1 by Martin
Apply 2to3 dict fixer and clean up resulting mess using view helpers
648
        internal_node_pre_clear = set(index._internal_node_cache)
4744.2.6 by John Arbash Meinel
Start exposing an GraphIndex.clear_cache() member.
649
        self.assertTrue(len(index._leaf_node_cache) > 0)
650
        index.clear_cache()
651
        # We don't touch _root_node or _internal_node_cache, both should be
652
        # small, and can save a round trip or two
653
        self.assertIsNot(None, index._root_node)
4744.2.9 by John Arbash Meinel
Move the note and assertions.
654
        # NOTE: We don't want to affect the _internal_node_cache, as we expect
655
        #       it will be small, and if we ever do touch this index again, it
656
        #       will save round-trips.  This assertion isn't very strong,
657
        #       becuase without a 3-level index, we don't have any internal
658
        #       nodes cached.
659
        self.assertEqual(internal_node_pre_clear,
6656.1.1 by Martin
Apply 2to3 dict fixer and clean up resulting mess using view helpers
660
                         set(index._internal_node_cache))
4744.2.6 by John Arbash Meinel
Start exposing an GraphIndex.clear_cache() member.
661
        self.assertEqual(0, len(index._leaf_node_cache))
662
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
663
    def test_trivial_constructor(self):
6083.1.1 by Jelmer Vernooij
Use get_transport_from_{url,path} in more places.
664
        t = transport.get_transport_from_url('trace+' + self.get_url(''))
5273.1.7 by Vincent Ladeuil
No more use of the get_transport imported *symbol*, all uses are through
665
        index = btree_index.BTreeGraphIndex(t, 'index', None)
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
666
        # Checks the page size at load, but that isn't logged yet.
5273.1.7 by Vincent Ladeuil
No more use of the get_transport imported *symbol*, all uses are through
667
        self.assertEqual([], t._activity)
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
668
669
    def test_with_size_constructor(self):
6083.1.1 by Jelmer Vernooij
Use get_transport_from_{url,path} in more places.
670
        t = transport.get_transport_from_url('trace+' + self.get_url(''))
5273.1.7 by Vincent Ladeuil
No more use of the get_transport imported *symbol*, all uses are through
671
        index = btree_index.BTreeGraphIndex(t, 'index', 1)
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
672
        # Checks the page size at load, but that isn't logged yet.
5273.1.7 by Vincent Ladeuil
No more use of the get_transport imported *symbol*, all uses are through
673
        self.assertEqual([], t._activity)
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
674
675
    def test_empty_key_count_no_size(self):
676
        builder = btree_index.BTreeBuilder(key_elements=1, reference_lists=0)
6083.1.1 by Jelmer Vernooij
Use get_transport_from_{url,path} in more places.
677
        t = transport.get_transport_from_url('trace+' + self.get_url(''))
5273.1.7 by Vincent Ladeuil
No more use of the get_transport imported *symbol*, all uses are through
678
        t.put_file('index', builder.finish())
679
        index = btree_index.BTreeGraphIndex(t, 'index', None)
680
        del t._activity[:]
681
        self.assertEqual([], t._activity)
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
682
        self.assertEqual(0, index.key_count())
683
        # The entire index should have been requested (as we generally have the
684
        # size available, and doing many small readvs is inappropriate).
685
        # We can't tell how much was actually read here, but - check the code.
5273.1.7 by Vincent Ladeuil
No more use of the get_transport imported *symbol*, all uses are through
686
        self.assertEqual([('get', 'index')], t._activity)
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
687
688
    def test_empty_key_count(self):
689
        builder = btree_index.BTreeBuilder(key_elements=1, reference_lists=0)
6083.1.1 by Jelmer Vernooij
Use get_transport_from_{url,path} in more places.
690
        t = transport.get_transport_from_url('trace+' + self.get_url(''))
5273.1.7 by Vincent Ladeuil
No more use of the get_transport imported *symbol*, all uses are through
691
        size = t.put_file('index', builder.finish())
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
692
        self.assertEqual(72, size)
5273.1.7 by Vincent Ladeuil
No more use of the get_transport imported *symbol*, all uses are through
693
        index = btree_index.BTreeGraphIndex(t, 'index', size)
694
        del t._activity[:]
695
        self.assertEqual([], t._activity)
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
696
        self.assertEqual(0, index.key_count())
697
        # The entire index should have been read, as 4K > size
698
        self.assertEqual([('readv', 'index', [(0, 72)], False, None)],
5273.1.7 by Vincent Ladeuil
No more use of the get_transport imported *symbol*, all uses are through
699
                         t._activity)
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
700
701
    def test_non_empty_key_count_2_2(self):
702
        builder = btree_index.BTreeBuilder(key_elements=2, reference_lists=2)
703
        nodes = self.make_nodes(35, 2, 2)
704
        for node in nodes:
705
            builder.add_node(*node)
6083.1.1 by Jelmer Vernooij
Use get_transport_from_{url,path} in more places.
706
        t = transport.get_transport_from_url('trace+' + self.get_url(''))
5273.1.7 by Vincent Ladeuil
No more use of the get_transport imported *symbol*, all uses are through
707
        size = t.put_file('index', builder.finish())
708
        index = btree_index.BTreeGraphIndex(t, 'index', size)
709
        del t._activity[:]
710
        self.assertEqual([], t._activity)
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
711
        self.assertEqual(70, index.key_count())
712
        # The entire index should have been read, as it is one page long.
713
        self.assertEqual([('readv', 'index', [(0, size)], False, None)],
5273.1.7 by Vincent Ladeuil
No more use of the get_transport imported *symbol*, all uses are through
714
            t._activity)
6614.1.3 by Vincent Ladeuil
Fix assertEquals being deprecated by using assertEqual.
715
        self.assertEqualApproxCompressed(1173, size)
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
716
5074.4.1 by John Arbash Meinel
Add an offset flag to BTreeGraphIndex.
717
    def test_with_offset_no_size(self):
718
        index = self.make_index_with_offset(key_elements=1, ref_lists=1,
719
                                            offset=1234,
720
                                            nodes=self.make_nodes(200, 1, 1))
721
        index._size = None # throw away the size info
722
        self.assertEqual(200, index.key_count())
723
724
    def test_with_small_offset(self):
725
        index = self.make_index_with_offset(key_elements=1, ref_lists=1,
726
                                            offset=1234,
727
                                            nodes=self.make_nodes(200, 1, 1))
728
        self.assertEqual(200, index.key_count())
729
730
    def test_with_large_offset(self):
731
        index = self.make_index_with_offset(key_elements=1, ref_lists=1,
732
                                            offset=123456,
733
                                            nodes=self.make_nodes(200, 1, 1))
734
        self.assertEqual(200, index.key_count())
735
3824.1.1 by John Arbash Meinel
Fix _read_nodes() to only issue a single read if there is no known size.
736
    def test__read_nodes_no_size_one_page_reads_once(self):
737
        self.make_index(nodes=[(('key',), 'value', ())])
6083.1.1 by Jelmer Vernooij
Use get_transport_from_{url,path} in more places.
738
        trans = transport.get_transport_from_url('trace+' + self.get_url())
3824.1.1 by John Arbash Meinel
Fix _read_nodes() to only issue a single read if there is no known size.
739
        index = btree_index.BTreeGraphIndex(trans, 'index', None)
740
        del trans._activity[:]
741
        nodes = dict(index._read_nodes([0]))
6656.1.1 by Martin
Apply 2to3 dict fixer and clean up resulting mess using view helpers
742
        self.assertEqual({0}, set(nodes))
3824.1.1 by John Arbash Meinel
Fix _read_nodes() to only issue a single read if there is no known size.
743
        node = nodes[0]
5365.5.1 by John Arbash Meinel
Implement a custom parser for chk btree leaves.
744
        self.assertEqual([('key',)], node.all_keys())
3824.1.1 by John Arbash Meinel
Fix _read_nodes() to only issue a single read if there is no known size.
745
        self.assertEqual([('get', 'index')], trans._activity)
746
747
    def test__read_nodes_no_size_multiple_pages(self):
748
        index = self.make_index(2, 2, nodes=self.make_nodes(160, 2, 2))
749
        index.key_count()
750
        num_pages = index._row_offsets[-1]
751
        # Reopen with a traced transport and no size
6083.1.1 by Jelmer Vernooij
Use get_transport_from_{url,path} in more places.
752
        trans = transport.get_transport_from_url('trace+' + self.get_url())
3824.1.1 by John Arbash Meinel
Fix _read_nodes() to only issue a single read if there is no known size.
753
        index = btree_index.BTreeGraphIndex(trans, 'index', None)
754
        del trans._activity[:]
755
        nodes = dict(index._read_nodes([0]))
6651.2.2 by Martin
Apply 2to3 xrange fix and fix up with sixish range
756
        self.assertEqual(list(range(num_pages)), sorted(nodes))
3824.1.1 by John Arbash Meinel
Fix _read_nodes() to only issue a single read if there is no known size.
757
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
758
    def test_2_levels_key_count_2_2(self):
759
        builder = btree_index.BTreeBuilder(key_elements=2, reference_lists=2)
760
        nodes = self.make_nodes(160, 2, 2)
761
        for node in nodes:
762
            builder.add_node(*node)
6083.1.1 by Jelmer Vernooij
Use get_transport_from_{url,path} in more places.
763
        t = transport.get_transport_from_url('trace+' + self.get_url(''))
5273.1.7 by Vincent Ladeuil
No more use of the get_transport imported *symbol*, all uses are through
764
        size = t.put_file('index', builder.finish())
6614.1.3 by Vincent Ladeuil
Fix assertEquals being deprecated by using assertEqual.
765
        self.assertEqualApproxCompressed(17692, size)
5273.1.7 by Vincent Ladeuil
No more use of the get_transport imported *symbol*, all uses are through
766
        index = btree_index.BTreeGraphIndex(t, 'index', size)
767
        del t._activity[:]
768
        self.assertEqual([], t._activity)
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
769
        self.assertEqual(320, index.key_count())
770
        # The entire index should not have been read.
771
        self.assertEqual([('readv', 'index', [(0, 4096)], False, None)],
5273.1.7 by Vincent Ladeuil
No more use of the get_transport imported *symbol*, all uses are through
772
                         t._activity)
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
773
774
    def test_validate_one_page(self):
775
        builder = btree_index.BTreeBuilder(key_elements=2, reference_lists=2)
3641.5.17 by John Arbash Meinel
Fix up the test suite now that things don't pack as well.
776
        nodes = self.make_nodes(45, 2, 2)
777
        for node in nodes:
778
            builder.add_node(*node)
6083.1.1 by Jelmer Vernooij
Use get_transport_from_{url,path} in more places.
779
        t = transport.get_transport_from_url('trace+' + self.get_url(''))
5273.1.7 by Vincent Ladeuil
No more use of the get_transport imported *symbol*, all uses are through
780
        size = t.put_file('index', builder.finish())
781
        index = btree_index.BTreeGraphIndex(t, 'index', size)
782
        del t._activity[:]
783
        self.assertEqual([], t._activity)
3641.5.17 by John Arbash Meinel
Fix up the test suite now that things don't pack as well.
784
        index.validate()
785
        # The entire index should have been read linearly.
786
        self.assertEqual([('readv', 'index', [(0, size)], False, None)],
5273.1.7 by Vincent Ladeuil
No more use of the get_transport imported *symbol*, all uses are through
787
                         t._activity)
6614.1.3 by Vincent Ladeuil
Fix assertEquals being deprecated by using assertEqual.
788
        self.assertEqualApproxCompressed(1488, size)
3641.5.17 by John Arbash Meinel
Fix up the test suite now that things don't pack as well.
789
790
    def test_validate_two_pages(self):
791
        builder = btree_index.BTreeBuilder(key_elements=2, reference_lists=2)
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
792
        nodes = self.make_nodes(80, 2, 2)
793
        for node in nodes:
794
            builder.add_node(*node)
6083.1.1 by Jelmer Vernooij
Use get_transport_from_{url,path} in more places.
795
        t = transport.get_transport_from_url('trace+' + self.get_url(''))
5273.1.7 by Vincent Ladeuil
No more use of the get_transport imported *symbol*, all uses are through
796
        size = t.put_file('index', builder.finish())
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
797
        # Root page, 2 leaf pages
6614.1.3 by Vincent Ladeuil
Fix assertEquals being deprecated by using assertEqual.
798
        self.assertEqualApproxCompressed(9339, size)
5273.1.7 by Vincent Ladeuil
No more use of the get_transport imported *symbol*, all uses are through
799
        index = btree_index.BTreeGraphIndex(t, 'index', size)
800
        del t._activity[:]
801
        self.assertEqual([], t._activity)
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
802
        index.validate()
6015.54.1 by Martin Packman
Add assertEqualsApproxCompressed to bt.test_btree_index as newer zlib gives smaller output
803
        rem = size - 8192 # Number of remaining bytes after second block
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
804
        # The entire index should have been read linearly.
5273.1.7 by Vincent Ladeuil
No more use of the get_transport imported *symbol*, all uses are through
805
        self.assertEqual(
806
            [('readv', 'index', [(0, 4096)], False, None),
6015.54.1 by Martin Packman
Add assertEqualsApproxCompressed to bt.test_btree_index as newer zlib gives smaller output
807
             ('readv', 'index', [(4096, 4096), (8192, rem)], False, None)],
5273.1.7 by Vincent Ladeuil
No more use of the get_transport imported *symbol*, all uses are through
808
            t._activity)
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
809
        # XXX: TODO: write some badly-ordered nodes, and some pointers-to-wrong
810
        # node and make validate find them.
811
812
    def test_eq_ne(self):
813
        # two indices are equal when constructed with the same parameters:
6083.1.1 by Jelmer Vernooij
Use get_transport_from_{url,path} in more places.
814
        t1 = transport.get_transport_from_url('trace+' + self.get_url(''))
5609.9.4 by Vincent Ladeuil
Use self.get_transport instead of transport.get_transport where possible.
815
        t2 = self.get_transport()
5273.1.7 by Vincent Ladeuil
No more use of the get_transport imported *symbol*, all uses are through
816
        self.assertTrue(
817
            btree_index.BTreeGraphIndex(t1, 'index', None) ==
818
            btree_index.BTreeGraphIndex(t1, 'index', None))
819
        self.assertTrue(
820
            btree_index.BTreeGraphIndex(t1, 'index', 20) ==
821
            btree_index.BTreeGraphIndex(t1, 'index', 20))
822
        self.assertFalse(
823
            btree_index.BTreeGraphIndex(t1, 'index', 20) ==
824
            btree_index.BTreeGraphIndex(t2, 'index', 20))
825
        self.assertFalse(
826
            btree_index.BTreeGraphIndex(t1, 'inde1', 20) ==
827
            btree_index.BTreeGraphIndex(t1, 'inde2', 20))
828
        self.assertFalse(
829
            btree_index.BTreeGraphIndex(t1, 'index', 10) ==
830
            btree_index.BTreeGraphIndex(t1, 'index', 20))
831
        self.assertFalse(
832
            btree_index.BTreeGraphIndex(t1, 'index', None) !=
833
            btree_index.BTreeGraphIndex(t1, 'index', None))
834
        self.assertFalse(
835
            btree_index.BTreeGraphIndex(t1, 'index', 20) !=
836
            btree_index.BTreeGraphIndex(t1, 'index', 20))
837
        self.assertTrue(
838
            btree_index.BTreeGraphIndex(t1, 'index', 20) !=
839
            btree_index.BTreeGraphIndex(t2, 'index', 20))
840
        self.assertTrue(
841
            btree_index.BTreeGraphIndex(t1, 'inde1', 20) !=
842
            btree_index.BTreeGraphIndex(t1, 'inde2', 20))
843
        self.assertTrue(
844
            btree_index.BTreeGraphIndex(t1, 'index', 10) !=
845
            btree_index.BTreeGraphIndex(t1, 'index', 20))
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
846
6178.2.9 by Shannon Weyrick
A version of the patch, based on suggestions from John Meinel, which detects an empty page differently to avoid false positives.
847
    def test_key_too_big(self):
848
        # the size that matters here is the _compressed_ size of the key, so we can't
849
        # do a simple character repeat.
6651.2.2 by Martin
Apply 2to3 xrange fix and fix up with sixish range
850
        bigKey = ''.join(map(repr, range(btree_index._PAGE_SIZE)))
6178.2.9 by Shannon Weyrick
A version of the patch, based on suggestions from John Meinel, which detects an empty page differently to avoid false positives.
851
        self.assertRaises(errors.BadIndexKey,
852
                          self.make_index,
853
                          nodes=[((bigKey,), 'value', ())])
854
        
3824.1.2 by John Arbash Meinel
iter_all_entries() shouldn't need to re-read the page.
855
    def test_iter_all_only_root_no_size(self):
856
        self.make_index(nodes=[(('key',), 'value', ())])
6083.1.1 by Jelmer Vernooij
Use get_transport_from_{url,path} in more places.
857
        t = transport.get_transport_from_url('trace+' + self.get_url(''))
5273.1.7 by Vincent Ladeuil
No more use of the get_transport imported *symbol*, all uses are through
858
        index = btree_index.BTreeGraphIndex(t, 'index', None)
859
        del t._activity[:]
3824.1.2 by John Arbash Meinel
iter_all_entries() shouldn't need to re-read the page.
860
        self.assertEqual([(('key',), 'value')],
861
                         [x[1:] for x in index.iter_all_entries()])
5273.1.7 by Vincent Ladeuil
No more use of the get_transport imported *symbol*, all uses are through
862
        self.assertEqual([('get', 'index')], t._activity)
3824.1.2 by John Arbash Meinel
iter_all_entries() shouldn't need to re-read the page.
863
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
864
    def test_iter_all_entries_reads(self):
865
        # iterating all entries reads the header, then does a linear
866
        # read.
3641.5.9 by John Arbash Meinel
Shrink the page size so that the three_level and iter_all
867
        self.shrink_page_size()
3641.5.11 by John Arbash Meinel
Some small test cleanup
868
        builder = btree_index.BTreeBuilder(key_elements=2, reference_lists=2)
3641.5.17 by John Arbash Meinel
Fix up the test suite now that things don't pack as well.
869
        # 20k nodes is enough to create a two internal nodes on the second
3641.5.9 by John Arbash Meinel
Shrink the page size so that the three_level and iter_all
870
        # level, with a 2K page size
3641.5.17 by John Arbash Meinel
Fix up the test suite now that things don't pack as well.
871
        nodes = self.make_nodes(10000, 2, 2)
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
872
        for node in nodes:
873
            builder.add_node(*node)
6083.1.1 by Jelmer Vernooij
Use get_transport_from_{url,path} in more places.
874
        t = transport.get_transport_from_url('trace+' + self.get_url(''))
5273.1.7 by Vincent Ladeuil
No more use of the get_transport imported *symbol*, all uses are through
875
        size = t.put_file('index', builder.finish())
3641.5.9 by John Arbash Meinel
Shrink the page size so that the three_level and iter_all
876
        page_size = btree_index._PAGE_SIZE
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
877
        del builder
5273.1.7 by Vincent Ladeuil
No more use of the get_transport imported *symbol*, all uses are through
878
        index = btree_index.BTreeGraphIndex(t, 'index', size)
879
        del t._activity[:]
880
        self.assertEqual([], t._activity)
3641.5.8 by John Arbash Meinel
Fix up the test suite, now that we pack more efficiently.
881
        found_nodes = self.time(list, index.iter_all_entries())
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
882
        bare_nodes = []
883
        for node in found_nodes:
884
            self.assertTrue(node[0] is index)
885
            bare_nodes.append(node[1:])
886
        self.assertEqual(3, len(index._row_lengths),
887
            "Not enough rows: %r" % index._row_lengths)
888
        # Should be as long as the nodes we supplied
3641.5.17 by John Arbash Meinel
Fix up the test suite now that things don't pack as well.
889
        self.assertEqual(20000, len(found_nodes))
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
890
        # Should have the same content
891
        self.assertEqual(set(nodes), set(bare_nodes))
892
        # Should have done linear scan IO up the index, ignoring
893
        # the internal nodes:
894
        # The entire index should have been read
895
        total_pages = sum(index._row_lengths)
896
        self.assertEqual(total_pages, index._row_offsets[-1])
6614.1.3 by Vincent Ladeuil
Fix assertEquals being deprecated by using assertEqual.
897
        self.assertEqualApproxCompressed(1303220, size)
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
898
        # The start of the leaves
3641.5.9 by John Arbash Meinel
Shrink the page size so that the three_level and iter_all
899
        first_byte = index._row_offsets[-2] * page_size
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
900
        readv_request = []
3641.5.9 by John Arbash Meinel
Shrink the page size so that the three_level and iter_all
901
        for offset in range(first_byte, size, page_size):
902
            readv_request.append((offset, page_size))
3641.3.6 by John Arbash Meinel
Dropping the number of nodes to 100k from 200k
903
        # The last page is truncated
6015.54.3 by Martin Packman
Fix test_iter_all_entries_reads for real by using observed size
904
        readv_request[-1] = (readv_request[-1][0], size % page_size)
3641.5.9 by John Arbash Meinel
Shrink the page size so that the three_level and iter_all
905
        expected = [('readv', 'index', [(0, page_size)], False, None),
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
906
             ('readv',  'index', readv_request, False, None)]
5273.1.7 by Vincent Ladeuil
No more use of the get_transport imported *symbol*, all uses are through
907
        if expected != t._activity:
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
908
            self.assertEqualDiff(pprint.pformat(expected),
6015.54.3 by Martin Packman
Fix test_iter_all_entries_reads for real by using observed size
909
                                 pprint.pformat(t._activity))
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
910
911
    def _test_iter_entries_references_resolved(self):
912
        index = self.make_index(1, nodes=[
913
            (('name', ), 'data', ([('ref', ), ('ref', )], )),
914
            (('ref', ), 'refdata', ([], ))])
6619.3.12 by Jelmer Vernooij
Use 2to3 set_literal fixer.
915
        self.assertEqual({(index, ('name', ), 'data', ((('ref',),('ref',)),)),
916
            (index, ('ref', ), 'refdata', ((), ))},
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
917
            set(index.iter_entries([('name',), ('ref',)])))
918
919
    def test_iter_entries_references_2_refs_resolved(self):
920
        # iterating some entries reads just the pages needed. For now, to
921
        # get it working and start measuring, only 4K pages are read.
922
        builder = btree_index.BTreeBuilder(key_elements=2, reference_lists=2)
923
        # 80 nodes is enough to create a two-level index.
924
        nodes = self.make_nodes(160, 2, 2)
925
        for node in nodes:
926
            builder.add_node(*node)
6083.1.1 by Jelmer Vernooij
Use get_transport_from_{url,path} in more places.
927
        t = transport.get_transport_from_url('trace+' + self.get_url(''))
5273.1.7 by Vincent Ladeuil
No more use of the get_transport imported *symbol*, all uses are through
928
        size = t.put_file('index', builder.finish())
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
929
        del builder
5273.1.7 by Vincent Ladeuil
No more use of the get_transport imported *symbol*, all uses are through
930
        index = btree_index.BTreeGraphIndex(t, 'index', size)
931
        del t._activity[:]
932
        self.assertEqual([], t._activity)
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
933
        # search for one key
934
        found_nodes = list(index.iter_entries([nodes[30][0]]))
935
        bare_nodes = []
936
        for node in found_nodes:
937
            self.assertTrue(node[0] is index)
938
            bare_nodes.append(node[1:])
939
        # Should be as long as the nodes we supplied
940
        self.assertEqual(1, len(found_nodes))
941
        # Should have the same content
942
        self.assertEqual(nodes[30], bare_nodes[0])
943
        # Should have read the root node, then one leaf page:
944
        self.assertEqual([('readv', 'index', [(0, 4096)], False, None),
3641.5.17 by John Arbash Meinel
Fix up the test suite now that things don't pack as well.
945
             ('readv',  'index', [(8192, 4096), ], False, None)],
5273.1.7 by Vincent Ladeuil
No more use of the get_transport imported *symbol*, all uses are through
946
            t._activity)
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
947
948
    def test_iter_key_prefix_1_element_key_None(self):
949
        index = self.make_index()
950
        self.assertRaises(errors.BadIndexKey, list,
951
            index.iter_entries_prefix([(None, )]))
952
953
    def test_iter_key_prefix_wrong_length(self):
954
        index = self.make_index()
955
        self.assertRaises(errors.BadIndexKey, list,
956
            index.iter_entries_prefix([('foo', None)]))
957
        index = self.make_index(key_elements=2)
958
        self.assertRaises(errors.BadIndexKey, list,
959
            index.iter_entries_prefix([('foo', )]))
960
        self.assertRaises(errors.BadIndexKey, list,
961
            index.iter_entries_prefix([('foo', None, None)]))
962
963
    def test_iter_key_prefix_1_key_element_no_refs(self):
964
        index = self.make_index( nodes=[
965
            (('name', ), 'data', ()),
966
            (('ref', ), 'refdata', ())])
6619.3.12 by Jelmer Vernooij
Use 2to3 set_literal fixer.
967
        self.assertEqual({(index, ('name', ), 'data'),
968
            (index, ('ref', ), 'refdata')},
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
969
            set(index.iter_entries_prefix([('name', ), ('ref', )])))
970
971
    def test_iter_key_prefix_1_key_element_refs(self):
972
        index = self.make_index(1, nodes=[
973
            (('name', ), 'data', ([('ref', )], )),
974
            (('ref', ), 'refdata', ([], ))])
6619.3.12 by Jelmer Vernooij
Use 2to3 set_literal fixer.
975
        self.assertEqual({(index, ('name', ), 'data', ((('ref',),),)),
976
            (index, ('ref', ), 'refdata', ((), ))},
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
977
            set(index.iter_entries_prefix([('name', ), ('ref', )])))
978
979
    def test_iter_key_prefix_2_key_element_no_refs(self):
980
        index = self.make_index(key_elements=2, nodes=[
981
            (('name', 'fin1'), 'data', ()),
982
            (('name', 'fin2'), 'beta', ()),
983
            (('ref', 'erence'), 'refdata', ())])
6619.3.12 by Jelmer Vernooij
Use 2to3 set_literal fixer.
984
        self.assertEqual({(index, ('name', 'fin1'), 'data'),
985
            (index, ('ref', 'erence'), 'refdata')},
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
986
            set(index.iter_entries_prefix([('name', 'fin1'), ('ref', 'erence')])))
6619.3.12 by Jelmer Vernooij
Use 2to3 set_literal fixer.
987
        self.assertEqual({(index, ('name', 'fin1'), 'data'),
988
            (index, ('name', 'fin2'), 'beta')},
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
989
            set(index.iter_entries_prefix([('name', None)])))
990
991
    def test_iter_key_prefix_2_key_element_refs(self):
992
        index = self.make_index(1, key_elements=2, nodes=[
993
            (('name', 'fin1'), 'data', ([('ref', 'erence')], )),
994
            (('name', 'fin2'), 'beta', ([], )),
995
            (('ref', 'erence'), 'refdata', ([], ))])
6619.3.12 by Jelmer Vernooij
Use 2to3 set_literal fixer.
996
        self.assertEqual({(index, ('name', 'fin1'), 'data', ((('ref', 'erence'),),)),
997
            (index, ('ref', 'erence'), 'refdata', ((), ))},
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
998
            set(index.iter_entries_prefix([('name', 'fin1'), ('ref', 'erence')])))
6619.3.12 by Jelmer Vernooij
Use 2to3 set_literal fixer.
999
        self.assertEqual({(index, ('name', 'fin1'), 'data', ((('ref', 'erence'),),)),
1000
            (index, ('name', 'fin2'), 'beta', ((), ))},
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
1001
            set(index.iter_entries_prefix([('name', None)])))
1002
4011.5.5 by Andrew Bennetts
Fix typo in comment.
1003
    # XXX: external_references tests are duplicated in test_index.  We
4011.5.3 by Andrew Bennetts
Implement and test external_references on GraphIndex and BTreeGraphIndex.
1004
    # probably should have per_graph_index tests...
1005
    def test_external_references_no_refs(self):
1006
        index = self.make_index(ref_lists=0, nodes=[])
1007
        self.assertRaises(ValueError, index.external_references, 0)
1008
1009
    def test_external_references_no_results(self):
1010
        index = self.make_index(ref_lists=1, nodes=[
1011
            (('key',), 'value', ([],))])
1012
        self.assertEqual(set(), index.external_references(0))
1013
1014
    def test_external_references_missing_ref(self):
1015
        missing_key = ('missing',)
1016
        index = self.make_index(ref_lists=1, nodes=[
1017
            (('key',), 'value', ([missing_key],))])
6619.3.12 by Jelmer Vernooij
Use 2to3 set_literal fixer.
1018
        self.assertEqual({missing_key}, index.external_references(0))
4011.5.3 by Andrew Bennetts
Implement and test external_references on GraphIndex and BTreeGraphIndex.
1019
1020
    def test_external_references_multiple_ref_lists(self):
1021
        missing_key = ('missing',)
1022
        index = self.make_index(ref_lists=2, nodes=[
1023
            (('key',), 'value', ([], [missing_key]))])
1024
        self.assertEqual(set([]), index.external_references(0))
6619.3.12 by Jelmer Vernooij
Use 2to3 set_literal fixer.
1025
        self.assertEqual({missing_key}, index.external_references(1))
4011.5.3 by Andrew Bennetts
Implement and test external_references on GraphIndex and BTreeGraphIndex.
1026
1027
    def test_external_references_two_records(self):
1028
        index = self.make_index(ref_lists=1, nodes=[
1029
            (('key-1',), 'value', ([('key-2',)],)),
1030
            (('key-2',), 'value', ([],)),
1031
            ])
1032
        self.assertEqual(set([]), index.external_references(0))
1033
4593.4.12 by John Arbash Meinel
Name the specific index api _find_ancestors, and the public CombinedGraphIndex api find_ancestry()
1034
    def test__find_ancestors_one_page(self):
4593.4.5 by John Arbash Meinel
Start adding some tests.
1035
        key1 = ('key-1',)
1036
        key2 = ('key-2',)
1037
        index = self.make_index(ref_lists=1, key_elements=1, nodes=[
1038
            (key1, 'value', ([key2],)),
1039
            (key2, 'value', ([],)),
1040
            ])
1041
        parent_map = {}
1042
        missing_keys = set()
4593.4.12 by John Arbash Meinel
Name the specific index api _find_ancestors, and the public CombinedGraphIndex api find_ancestry()
1043
        search_keys = index._find_ancestors([key1], 0, parent_map, missing_keys)
4593.4.5 by John Arbash Meinel
Start adding some tests.
1044
        self.assertEqual({key1: (key2,), key2: ()}, parent_map)
1045
        self.assertEqual(set(), missing_keys)
4593.4.7 by John Arbash Meinel
Basic implementation of a conforming interface for GraphIndex.
1046
        self.assertEqual(set(), search_keys)
4593.4.5 by John Arbash Meinel
Start adding some tests.
1047
4593.4.12 by John Arbash Meinel
Name the specific index api _find_ancestors, and the public CombinedGraphIndex api find_ancestry()
1048
    def test__find_ancestors_one_page_w_missing(self):
4593.4.6 by John Arbash Meinel
Add a test that we can walk some of the keys on a given page
1049
        key1 = ('key-1',)
1050
        key2 = ('key-2',)
1051
        key3 = ('key-3',)
1052
        index = self.make_index(ref_lists=1, key_elements=1, nodes=[
1053
            (key1, 'value', ([key2],)),
1054
            (key2, 'value', ([],)),
1055
            ])
1056
        parent_map = {}
1057
        missing_keys = set()
4593.4.12 by John Arbash Meinel
Name the specific index api _find_ancestors, and the public CombinedGraphIndex api find_ancestry()
1058
        search_keys = index._find_ancestors([key2, key3], 0, parent_map,
1059
                                            missing_keys)
4593.4.6 by John Arbash Meinel
Add a test that we can walk some of the keys on a given page
1060
        self.assertEqual({key2: ()}, parent_map)
1061
        # we know that key3 is missing because we read the page that it would
1062
        # otherwise be on
6619.3.12 by Jelmer Vernooij
Use 2to3 set_literal fixer.
1063
        self.assertEqual({key3}, missing_keys)
4593.4.6 by John Arbash Meinel
Add a test that we can walk some of the keys on a given page
1064
        self.assertEqual(set(), search_keys)
1065
4593.4.12 by John Arbash Meinel
Name the specific index api _find_ancestors, and the public CombinedGraphIndex api find_ancestry()
1066
    def test__find_ancestors_one_parent_missing(self):
4593.4.6 by John Arbash Meinel
Add a test that we can walk some of the keys on a given page
1067
        key1 = ('key-1',)
1068
        key2 = ('key-2',)
1069
        key3 = ('key-3',)
1070
        index = self.make_index(ref_lists=1, key_elements=1, nodes=[
1071
            (key1, 'value', ([key2],)),
1072
            (key2, 'value', ([key3],)),
1073
            ])
1074
        parent_map = {}
1075
        missing_keys = set()
4593.4.12 by John Arbash Meinel
Name the specific index api _find_ancestors, and the public CombinedGraphIndex api find_ancestry()
1076
        search_keys = index._find_ancestors([key1], 0, parent_map,
1077
                                            missing_keys)
4593.4.6 by John Arbash Meinel
Add a test that we can walk some of the keys on a given page
1078
        self.assertEqual({key1: (key2,), key2: (key3,)}, parent_map)
1079
        self.assertEqual(set(), missing_keys)
1080
        # all we know is that key3 wasn't present on the page we were reading
1081
        # but if you look, the last key is key2 which comes before key3, so we
1082
        # don't know whether key3 would land on this page or not.
6619.3.12 by Jelmer Vernooij
Use 2to3 set_literal fixer.
1083
        self.assertEqual({key3}, search_keys)
4593.4.12 by John Arbash Meinel
Name the specific index api _find_ancestors, and the public CombinedGraphIndex api find_ancestry()
1084
        search_keys = index._find_ancestors(search_keys, 0, parent_map,
1085
                                            missing_keys)
4593.4.6 by John Arbash Meinel
Add a test that we can walk some of the keys on a given page
1086
        # passing it back in, we are sure it is 'missing'
1087
        self.assertEqual({key1: (key2,), key2: (key3,)}, parent_map)
6619.3.12 by Jelmer Vernooij
Use 2to3 set_literal fixer.
1088
        self.assertEqual({key3}, missing_keys)
4593.4.6 by John Arbash Meinel
Add a test that we can walk some of the keys on a given page
1089
        self.assertEqual(set([]), search_keys)
1090
4593.4.12 by John Arbash Meinel
Name the specific index api _find_ancestors, and the public CombinedGraphIndex api find_ancestry()
1091
    def test__find_ancestors_dont_search_known(self):
4593.4.7 by John Arbash Meinel
Basic implementation of a conforming interface for GraphIndex.
1092
        key1 = ('key-1',)
1093
        key2 = ('key-2',)
1094
        key3 = ('key-3',)
1095
        index = self.make_index(ref_lists=1, key_elements=1, nodes=[
1096
            (key1, 'value', ([key2],)),
1097
            (key2, 'value', ([key3],)),
1098
            (key3, 'value', ([],)),
1099
            ])
1100
        # We already know about key2, so we won't try to search for key3
1101
        parent_map = {key2: (key3,)}
1102
        missing_keys = set()
4593.4.12 by John Arbash Meinel
Name the specific index api _find_ancestors, and the public CombinedGraphIndex api find_ancestry()
1103
        search_keys = index._find_ancestors([key1], 0, parent_map,
1104
                                            missing_keys)
4593.4.7 by John Arbash Meinel
Basic implementation of a conforming interface for GraphIndex.
1105
        self.assertEqual({key1: (key2,), key2: (key3,)}, parent_map)
1106
        self.assertEqual(set(), missing_keys)
1107
        self.assertEqual(set(), search_keys)
1108
4593.4.12 by John Arbash Meinel
Name the specific index api _find_ancestors, and the public CombinedGraphIndex api find_ancestry()
1109
    def test__find_ancestors_multiple_pages(self):
4593.4.6 by John Arbash Meinel
Add a test that we can walk some of the keys on a given page
1110
        # We need to use enough keys that we actually cause a split
1111
        start_time = 1249671539
1112
        email = "joebob@example.com"
1113
        nodes = []
1114
        ref_lists = ((),)
1115
        rev_keys = []
6651.2.2 by Martin
Apply 2to3 xrange fix and fix up with sixish range
1116
        for i in range(400):
4593.4.6 by John Arbash Meinel
Add a test that we can walk some of the keys on a given page
1117
            rev_id = '%s-%s-%s' % (email,
1118
                                   osutils.compact_date(start_time + i),
1119
                                   osutils.rand_chars(16))
1120
            rev_key = (rev_id,)
1121
            nodes.append((rev_key, 'value', ref_lists))
1122
            # We have a ref 'list' of length 1, with a list of parents, with 1
1123
            # parent which is a key
1124
            ref_lists = ((rev_key,),)
1125
            rev_keys.append(rev_key)
1126
        index = self.make_index(ref_lists=1, key_elements=1, nodes=nodes)
1127
        self.assertEqual(400, index.key_count())
1128
        self.assertEqual(3, len(index._row_offsets))
1129
        nodes = dict(index._read_nodes([1, 2]))
1130
        l1 = nodes[1]
1131
        l2 = nodes[2]
1132
        min_l2_key = l2.min_key
1133
        max_l1_key = l1.max_key
1134
        self.assertTrue(max_l1_key < min_l2_key)
5365.5.1 by John Arbash Meinel
Implement a custom parser for chk btree leaves.
1135
        parents_min_l2_key = l2[min_l2_key][1][0]
4593.4.6 by John Arbash Meinel
Add a test that we can walk some of the keys on a given page
1136
        self.assertEqual((l1.max_key,), parents_min_l2_key)
1137
        # Now, whatever key we select that would fall on the second page,
1138
        # should give us all the parents until the page break
1139
        key_idx = rev_keys.index(min_l2_key)
1140
        next_key = rev_keys[key_idx+1]
1141
        # So now when we get the parent map, we should get the key we are
1142
        # looking for, min_l2_key, and then a reference to go look for the
1143
        # parent of that key
1144
        parent_map = {}
1145
        missing_keys = set()
4593.4.12 by John Arbash Meinel
Name the specific index api _find_ancestors, and the public CombinedGraphIndex api find_ancestry()
1146
        search_keys = index._find_ancestors([next_key], 0, parent_map,
1147
                                            missing_keys)
4593.4.6 by John Arbash Meinel
Add a test that we can walk some of the keys on a given page
1148
        self.assertEqual([min_l2_key, next_key], sorted(parent_map))
1149
        self.assertEqual(set(), missing_keys)
6619.3.12 by Jelmer Vernooij
Use 2to3 set_literal fixer.
1150
        self.assertEqual({max_l1_key}, search_keys)
4593.4.6 by John Arbash Meinel
Add a test that we can walk some of the keys on a given page
1151
        parent_map = {}
4593.4.12 by John Arbash Meinel
Name the specific index api _find_ancestors, and the public CombinedGraphIndex api find_ancestry()
1152
        search_keys = index._find_ancestors([max_l1_key], 0, parent_map,
1153
                                            missing_keys)
5365.5.1 by John Arbash Meinel
Implement a custom parser for chk btree leaves.
1154
        self.assertEqual(l1.all_keys(), sorted(parent_map))
4593.4.6 by John Arbash Meinel
Add a test that we can walk some of the keys on a given page
1155
        self.assertEqual(set(), missing_keys)
1156
        self.assertEqual(set(), search_keys)
1157
4593.4.12 by John Arbash Meinel
Name the specific index api _find_ancestors, and the public CombinedGraphIndex api find_ancestry()
1158
    def test__find_ancestors_empty_index(self):
4593.4.11 by John Arbash Meinel
Snapshot the work in progress.
1159
        index = self.make_index(ref_lists=1, key_elements=1, nodes=[])
1160
        parent_map = {}
1161
        missing_keys = set()
4593.4.12 by John Arbash Meinel
Name the specific index api _find_ancestors, and the public CombinedGraphIndex api find_ancestry()
1162
        search_keys = index._find_ancestors([('one',), ('two',)], 0, parent_map,
1163
                                            missing_keys)
4593.4.11 by John Arbash Meinel
Snapshot the work in progress.
1164
        self.assertEqual(set(), search_keys)
1165
        self.assertEqual({}, parent_map)
6619.3.12 by Jelmer Vernooij
Use 2to3 set_literal fixer.
1166
        self.assertEqual({('one',), ('two',)}, missing_keys)
4593.4.11 by John Arbash Meinel
Snapshot the work in progress.
1167
4634.71.1 by John Arbash Meinel
Work around bug #402623 by allowing BTreeGraphIndex(...,unlimited_cache=True).
1168
    def test_supports_unlimited_cache(self):
1169
        builder = btree_index.BTreeBuilder(reference_lists=0, key_elements=1)
4634.71.3 by John Arbash Meinel
Add some comments to test_supports_unlimited_cache, as mentioned by Vincent.
1170
        # We need enough nodes to cause a page split (so we have both an
1171
        # internal node and a couple leaf nodes. 500 seems to be enough.)
4634.71.2 by John Arbash Meinel
If we are going to sometimes use a dict, we have to conform to just the dict interface.
1172
        nodes = self.make_nodes(500, 1, 0)
1173
        for node in nodes:
1174
            builder.add_node(*node)
4634.71.1 by John Arbash Meinel
Work around bug #402623 by allowing BTreeGraphIndex(...,unlimited_cache=True).
1175
        stream = builder.finish()
5609.9.4 by Vincent Ladeuil
Use self.get_transport instead of transport.get_transport where possible.
1176
        trans = self.get_transport()
4634.71.1 by John Arbash Meinel
Work around bug #402623 by allowing BTreeGraphIndex(...,unlimited_cache=True).
1177
        size = trans.put_file('index', stream)
1178
        index = btree_index.BTreeGraphIndex(trans, 'index', size)
4634.71.3 by John Arbash Meinel
Add some comments to test_supports_unlimited_cache, as mentioned by Vincent.
1179
        self.assertEqual(500, index.key_count())
1180
        # We have an internal node
1181
        self.assertEqual(2, len(index._row_lengths))
1182
        # We have at least 2 leaf nodes
1183
        self.assertTrue(index._row_lengths[-1] >= 2)
4634.71.1 by John Arbash Meinel
Work around bug #402623 by allowing BTreeGraphIndex(...,unlimited_cache=True).
1184
        self.assertIsInstance(index._leaf_node_cache, lru_cache.LRUCache)
1185
        self.assertEqual(btree_index._NODE_CACHE_SIZE,
1186
                         index._leaf_node_cache._max_cache)
1187
        self.assertIsInstance(index._internal_node_cache, fifo_cache.FIFOCache)
1188
        self.assertEqual(100, index._internal_node_cache._max_cache)
1189
        # No change if unlimited_cache=False is passed
1190
        index = btree_index.BTreeGraphIndex(trans, 'index', size,
1191
                                            unlimited_cache=False)
1192
        self.assertIsInstance(index._leaf_node_cache, lru_cache.LRUCache)
1193
        self.assertEqual(btree_index._NODE_CACHE_SIZE,
1194
                         index._leaf_node_cache._max_cache)
1195
        self.assertIsInstance(index._internal_node_cache, fifo_cache.FIFOCache)
1196
        self.assertEqual(100, index._internal_node_cache._max_cache)
1197
        index = btree_index.BTreeGraphIndex(trans, 'index', size,
1198
                                            unlimited_cache=True)
1199
        self.assertIsInstance(index._leaf_node_cache, dict)
1200
        self.assertIs(type(index._internal_node_cache), dict)
4634.71.3 by John Arbash Meinel
Add some comments to test_supports_unlimited_cache, as mentioned by Vincent.
1201
        # Exercise the lookup code
1202
        entries = set(index.iter_entries([n[0] for n in nodes]))
1203
        self.assertEqual(500, len(entries))
4634.71.1 by John Arbash Meinel
Work around bug #402623 by allowing BTreeGraphIndex(...,unlimited_cache=True).
1204
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
1205
1206
class TestBTreeNodes(BTreeTestCase):
1207
5559.2.2 by Martin Pool
Change to using standard load_tests_apply_scenarios.
1208
    scenarios = btreeparser_scenarios()
1209
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
1210
    def setUp(self):
6552.1.4 by Vincent Ladeuil
Remaining tests matching setup(self) that can be rewritten with super().
1211
        super(TestBTreeNodes, self).setUp()
4985.1.5 by Vincent Ladeuil
Deploying the new overrideAttr facility further reduces the complexity
1212
        self.overrideAttr(btree_index, '_btree_serializer', self.parse_btree)
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
1213
1214
    def test_LeafNode_1_0(self):
1215
        node_bytes = ("type=leaf\n"
1216
            "0000000000000000000000000000000000000000\x00\x00value:0\n"
1217
            "1111111111111111111111111111111111111111\x00\x00value:1\n"
1218
            "2222222222222222222222222222222222222222\x00\x00value:2\n"
1219
            "3333333333333333333333333333333333333333\x00\x00value:3\n"
1220
            "4444444444444444444444444444444444444444\x00\x00value:4\n")
1221
        node = btree_index._LeafNode(node_bytes, 1, 0)
1222
        # We do direct access, or don't care about order, to leaf nodes most of
1223
        # the time, so a dict is useful:
1224
        self.assertEqual({
1225
            ("0000000000000000000000000000000000000000",): ("value:0", ()),
1226
            ("1111111111111111111111111111111111111111",): ("value:1", ()),
1227
            ("2222222222222222222222222222222222222222",): ("value:2", ()),
1228
            ("3333333333333333333333333333333333333333",): ("value:3", ()),
1229
            ("4444444444444444444444444444444444444444",): ("value:4", ()),
5365.5.1 by John Arbash Meinel
Implement a custom parser for chk btree leaves.
1230
            }, dict(node.all_items()))
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
1231
1232
    def test_LeafNode_2_2(self):
1233
        node_bytes = ("type=leaf\n"
1234
            "00\x0000\x00\t00\x00ref00\x00value:0\n"
1235
            "00\x0011\x0000\x00ref00\t00\x00ref00\r01\x00ref01\x00value:1\n"
1236
            "11\x0033\x0011\x00ref22\t11\x00ref22\r11\x00ref22\x00value:3\n"
1237
            "11\x0044\x00\t11\x00ref00\x00value:4\n"
1238
            ""
1239
            )
1240
        node = btree_index._LeafNode(node_bytes, 2, 2)
1241
        # We do direct access, or don't care about order, to leaf nodes most of
1242
        # the time, so a dict is useful:
1243
        self.assertEqual({
1244
            ('00', '00'): ('value:0', ((), (('00', 'ref00'),))),
1245
            ('00', '11'): ('value:1',
1246
                ((('00', 'ref00'),), (('00', 'ref00'), ('01', 'ref01')))),
1247
            ('11', '33'): ('value:3',
1248
                ((('11', 'ref22'),), (('11', 'ref22'), ('11', 'ref22')))),
1249
            ('11', '44'): ('value:4', ((), (('11', 'ref00'),)))
5365.5.1 by John Arbash Meinel
Implement a custom parser for chk btree leaves.
1250
            }, dict(node.all_items()))
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
1251
1252
    def test_InternalNode_1(self):
1253
        node_bytes = ("type=internal\n"
1254
            "offset=1\n"
1255
            "0000000000000000000000000000000000000000\n"
1256
            "1111111111111111111111111111111111111111\n"
1257
            "2222222222222222222222222222222222222222\n"
1258
            "3333333333333333333333333333333333333333\n"
1259
            "4444444444444444444444444444444444444444\n"
1260
            )
1261
        node = btree_index._InternalNode(node_bytes)
1262
        # We want to bisect to find the right children from this node, so a
1263
        # vector is most useful.
1264
        self.assertEqual([
1265
            ("0000000000000000000000000000000000000000",),
1266
            ("1111111111111111111111111111111111111111",),
1267
            ("2222222222222222222222222222222222222222",),
1268
            ("3333333333333333333333333333333333333333",),
1269
            ("4444444444444444444444444444444444444444",),
1270
            ], node.keys)
1271
        self.assertEqual(1, node.offset)
1272
1273
    def test_LeafNode_2_2(self):
1274
        node_bytes = ("type=leaf\n"
1275
            "00\x0000\x00\t00\x00ref00\x00value:0\n"
1276
            "00\x0011\x0000\x00ref00\t00\x00ref00\r01\x00ref01\x00value:1\n"
1277
            "11\x0033\x0011\x00ref22\t11\x00ref22\r11\x00ref22\x00value:3\n"
1278
            "11\x0044\x00\t11\x00ref00\x00value:4\n"
1279
            ""
1280
            )
1281
        node = btree_index._LeafNode(node_bytes, 2, 2)
1282
        # We do direct access, or don't care about order, to leaf nodes most of
1283
        # the time, so a dict is useful:
1284
        self.assertEqual({
1285
            ('00', '00'): ('value:0', ((), (('00', 'ref00'),))),
1286
            ('00', '11'): ('value:1',
1287
                ((('00', 'ref00'),), (('00', 'ref00'), ('01', 'ref01')))),
1288
            ('11', '33'): ('value:3',
1289
                ((('11', 'ref22'),), (('11', 'ref22'), ('11', 'ref22')))),
1290
            ('11', '44'): ('value:4', ((), (('11', 'ref00'),)))
5365.5.1 by John Arbash Meinel
Implement a custom parser for chk btree leaves.
1291
            }, dict(node.all_items()))
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
1292
3641.3.19 by John Arbash Meinel
The flatten code now handles the no-ref-list case.
1293
    def assertFlattened(self, expected, key, value, refs):
3641.3.18 by John Arbash Meinel
Start working on a compiled function for transforming
1294
        flat_key, flat_line = self.parse_btree._flatten_node(
3641.3.19 by John Arbash Meinel
The flatten code now handles the no-ref-list case.
1295
            (None, key, value, refs), bool(refs))
3641.3.18 by John Arbash Meinel
Start working on a compiled function for transforming
1296
        self.assertEqual('\x00'.join(key), flat_key)
1297
        self.assertEqual(expected, flat_line)
1298
3641.3.19 by John Arbash Meinel
The flatten code now handles the no-ref-list case.
1299
    def test__flatten_node(self):
1300
        self.assertFlattened('key\0\0value\n', ('key',), 'value', [])
1301
        self.assertFlattened('key\0tuple\0\0value str\n',
1302
                             ('key', 'tuple'), 'value str', [])
1303
        self.assertFlattened('key\0tuple\0triple\0\0value str\n',
1304
                             ('key', 'tuple', 'triple'), 'value str', [])
1305
        self.assertFlattened('k\0t\0s\0ref\0value str\n',
1306
                             ('k', 't', 's'), 'value str', [[('ref',)]])
1307
        self.assertFlattened('key\0tuple\0ref\0key\0value str\n',
1308
                             ('key', 'tuple'), 'value str', [[('ref', 'key')]])
1309
        self.assertFlattened("00\x0000\x00\t00\x00ref00\x00value:0\n",
1310
            ('00', '00'), 'value:0', ((), (('00', 'ref00'),)))
1311
        self.assertFlattened(
1312
            "00\x0011\x0000\x00ref00\t00\x00ref00\r01\x00ref01\x00value:1\n",
1313
            ('00', '11'), 'value:1',
1314
                ((('00', 'ref00'),), (('00', 'ref00'), ('01', 'ref01'))))
1315
        self.assertFlattened(
1316
            "11\x0033\x0011\x00ref22\t11\x00ref22\r11\x00ref22\x00value:3\n",
1317
            ('11', '33'), 'value:3',
1318
                ((('11', 'ref22'),), (('11', 'ref22'), ('11', 'ref22'))))
1319
        self.assertFlattened(
1320
            "11\x0044\x00\t11\x00ref00\x00value:4\n",
1321
            ('11', '44'), 'value:4', ((), (('11', 'ref00'),)))
3641.3.18 by John Arbash Meinel
Start working on a compiled function for transforming
1322
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
1323
1324
class TestCompiledBtree(tests.TestCase):
1325
1326
    def test_exists(self):
1327
        # This is just to let the user know if they don't have the feature
1328
        # available
4913.2.20 by John Arbash Meinel
Change all of the compiled_foo to compiled_foo_feature
1329
        self.requireFeature(compiled_btreeparser_feature)
3641.3.1 by John Arbash Meinel
Bring in the btree_index and chunk_writer code and their tests.
1330
1331
1332
class TestMultiBisectRight(tests.TestCase):
1333
1334
    def assertMultiBisectRight(self, offsets, search_keys, fixed_keys):
1335
        self.assertEqual(offsets,
1336
                         btree_index.BTreeGraphIndex._multi_bisect_right(
1337
                            search_keys, fixed_keys))
1338
1339
    def test_after(self):
1340
        self.assertMultiBisectRight([(1, ['b'])], ['b'], ['a'])
1341
        self.assertMultiBisectRight([(3, ['e', 'f', 'g'])],
1342
                                    ['e', 'f', 'g'], ['a', 'b', 'c'])
1343
1344
    def test_before(self):
1345
        self.assertMultiBisectRight([(0, ['a'])], ['a'], ['b'])
1346
        self.assertMultiBisectRight([(0, ['a', 'b', 'c', 'd'])],
1347
                                    ['a', 'b', 'c', 'd'], ['e', 'f', 'g'])
1348
1349
    def test_exact(self):
1350
        self.assertMultiBisectRight([(1, ['a'])], ['a'], ['a'])
1351
        self.assertMultiBisectRight([(1, ['a']), (2, ['b'])], ['a', 'b'], ['a', 'b'])
1352
        self.assertMultiBisectRight([(1, ['a']), (3, ['c'])],
1353
                                    ['a', 'c'], ['a', 'b', 'c'])
1354
1355
    def test_inbetween(self):
1356
        self.assertMultiBisectRight([(1, ['b'])], ['b'], ['a', 'c'])
1357
        self.assertMultiBisectRight([(1, ['b', 'c', 'd']), (2, ['f', 'g'])],
1358
                                    ['b', 'c', 'd', 'f', 'g'], ['a', 'e', 'h'])
1359
1360
    def test_mixed(self):
1361
        self.assertMultiBisectRight([(0, ['a', 'b']), (2, ['d', 'e']),
1362
                                     (4, ['g', 'h'])],
1363
                                    ['a', 'b', 'd', 'e', 'g', 'h'],
1364
                                    ['c', 'd', 'f', 'g'])
3763.8.7 by John Arbash Meinel
A bit of doc updates, start putting in tests for current behavior.
1365
1366
3763.8.15 by John Arbash Meinel
Review comments from Martin. Code clarity/variable name/docstring updates.
1367
class TestExpandOffsets(tests.TestCase):
3763.8.7 by John Arbash Meinel
A bit of doc updates, start putting in tests for current behavior.
1368
1369
    def make_index(self, size, recommended_pages=None):
1370
        """Make an index with a generic size.
1371
1372
        This doesn't actually create anything on disk, it just primes a
1373
        BTreeGraphIndex with the recommended information.
1374
        """
5273.1.7 by Vincent Ladeuil
No more use of the get_transport imported *symbol*, all uses are through
1375
        index = btree_index.BTreeGraphIndex(
6083.1.1 by Jelmer Vernooij
Use get_transport_from_{url,path} in more places.
1376
            transport.get_transport_from_url('memory:///'),
1377
            'test-index', size=size)
3763.8.7 by John Arbash Meinel
A bit of doc updates, start putting in tests for current behavior.
1378
        if recommended_pages is not None:
1379
            index._recommended_pages = recommended_pages
1380
        return index
1381
3763.8.15 by John Arbash Meinel
Review comments from Martin. Code clarity/variable name/docstring updates.
1382
    def set_cached_offsets(self, index, cached_offsets):
1383
        """Monkeypatch to give a canned answer for _get_offsets_for...()."""
1384
        def _get_offsets_to_cached_pages():
1385
            cached = set(cached_offsets)
3763.8.7 by John Arbash Meinel
A bit of doc updates, start putting in tests for current behavior.
1386
            return cached
3763.8.15 by John Arbash Meinel
Review comments from Martin. Code clarity/variable name/docstring updates.
1387
        index._get_offsets_to_cached_pages = _get_offsets_to_cached_pages
3763.8.7 by John Arbash Meinel
A bit of doc updates, start putting in tests for current behavior.
1388
1389
    def prepare_index(self, index, node_ref_lists, key_length, key_count,
3763.8.15 by John Arbash Meinel
Review comments from Martin. Code clarity/variable name/docstring updates.
1390
                      row_lengths, cached_offsets):
3763.8.7 by John Arbash Meinel
A bit of doc updates, start putting in tests for current behavior.
1391
        """Setup the BTreeGraphIndex with some pre-canned information."""
1392
        index.node_ref_lists = node_ref_lists
1393
        index._key_length = key_length
1394
        index._key_count = key_count
1395
        index._row_lengths = row_lengths
1396
        index._compute_row_offsets()
1397
        index._root_node = btree_index._InternalNode('internal\noffset=0\n')
3763.8.15 by John Arbash Meinel
Review comments from Martin. Code clarity/variable name/docstring updates.
1398
        self.set_cached_offsets(index, cached_offsets)
3763.8.7 by John Arbash Meinel
A bit of doc updates, start putting in tests for current behavior.
1399
1400
    def make_100_node_index(self):
1401
        index = self.make_index(4096*100, 6)
3763.8.14 by John Arbash Meinel
Add in a shortcut when we haven't cached much yet.
1402
        # Consider we've already made a single request at the middle
3763.8.7 by John Arbash Meinel
A bit of doc updates, start putting in tests for current behavior.
1403
        self.prepare_index(index, node_ref_lists=0, key_length=1,
1404
                           key_count=1000, row_lengths=[1, 99],
3763.8.15 by John Arbash Meinel
Review comments from Martin. Code clarity/variable name/docstring updates.
1405
                           cached_offsets=[0, 50])
3763.8.7 by John Arbash Meinel
A bit of doc updates, start putting in tests for current behavior.
1406
        return index
1407
3763.8.9 by John Arbash Meinel
Finish up the algorithm to stay within a given layer.
1408
    def make_1000_node_index(self):
1409
        index = self.make_index(4096*1000, 6)
3763.8.14 by John Arbash Meinel
Add in a shortcut when we haven't cached much yet.
1410
        # Pretend we've already made a single request in the middle
3763.8.9 by John Arbash Meinel
Finish up the algorithm to stay within a given layer.
1411
        self.prepare_index(index, node_ref_lists=0, key_length=1,
1412
                           key_count=90000, row_lengths=[1, 9, 990],
3763.8.15 by John Arbash Meinel
Review comments from Martin. Code clarity/variable name/docstring updates.
1413
                           cached_offsets=[0, 5, 500])
3763.8.9 by John Arbash Meinel
Finish up the algorithm to stay within a given layer.
1414
        return index
1415
3763.8.7 by John Arbash Meinel
A bit of doc updates, start putting in tests for current behavior.
1416
    def assertNumPages(self, expected_pages, index, size):
1417
        index._size = size
3763.8.15 by John Arbash Meinel
Review comments from Martin. Code clarity/variable name/docstring updates.
1418
        self.assertEqual(expected_pages, index._compute_total_pages_in_index())
3763.8.7 by John Arbash Meinel
A bit of doc updates, start putting in tests for current behavior.
1419
3763.8.15 by John Arbash Meinel
Review comments from Martin. Code clarity/variable name/docstring updates.
1420
    def assertExpandOffsets(self, expected, index, offsets):
1421
        self.assertEqual(expected, index._expand_offsets(offsets),
1422
                         'We did not get the expected value after expanding'
1423
                         ' %s' % (offsets,))
3763.8.7 by John Arbash Meinel
A bit of doc updates, start putting in tests for current behavior.
1424
1425
    def test_default_recommended_pages(self):
1426
        index = self.make_index(None)
1427
        # local transport recommends 4096 byte reads, which is 1 page
1428
        self.assertEqual(1, index._recommended_pages)
1429
3763.8.15 by John Arbash Meinel
Review comments from Martin. Code clarity/variable name/docstring updates.
1430
    def test__compute_total_pages_in_index(self):
3763.8.7 by John Arbash Meinel
A bit of doc updates, start putting in tests for current behavior.
1431
        index = self.make_index(None)
1432
        self.assertNumPages(1, index, 1024)
1433
        self.assertNumPages(1, index, 4095)
1434
        self.assertNumPages(1, index, 4096)
1435
        self.assertNumPages(2, index, 4097)
1436
        self.assertNumPages(2, index, 8192)
1437
        self.assertNumPages(76, index, 4096*75 + 10)
1438
3763.8.9 by John Arbash Meinel
Finish up the algorithm to stay within a given layer.
1439
    def test__find_layer_start_and_stop(self):
1440
        index = self.make_1000_node_index()
1441
        self.assertEqual((0, 1), index._find_layer_first_and_end(0))
1442
        self.assertEqual((1, 10), index._find_layer_first_and_end(1))
1443
        self.assertEqual((1, 10), index._find_layer_first_and_end(9))
1444
        self.assertEqual((10, 1000), index._find_layer_first_and_end(10))
1445
        self.assertEqual((10, 1000), index._find_layer_first_and_end(99))
1446
        self.assertEqual((10, 1000), index._find_layer_first_and_end(999))
1447
3763.8.7 by John Arbash Meinel
A bit of doc updates, start putting in tests for current behavior.
1448
    def test_unknown_size(self):
1449
        # We should not expand if we don't know the file size
1450
        index = self.make_index(None, 10)
3763.8.15 by John Arbash Meinel
Review comments from Martin. Code clarity/variable name/docstring updates.
1451
        self.assertExpandOffsets([0], index, [0])
1452
        self.assertExpandOffsets([1, 4, 9], index, [1, 4, 9])
3763.8.7 by John Arbash Meinel
A bit of doc updates, start putting in tests for current behavior.
1453
1454
    def test_more_than_recommended(self):
1455
        index = self.make_index(4096*100, 2)
3763.8.15 by John Arbash Meinel
Review comments from Martin. Code clarity/variable name/docstring updates.
1456
        self.assertExpandOffsets([1, 10], index, [1, 10])
1457
        self.assertExpandOffsets([1, 10, 20], index, [1, 10, 20])
3763.8.7 by John Arbash Meinel
A bit of doc updates, start putting in tests for current behavior.
1458
1459
    def test_read_all_from_root(self):
1460
        index = self.make_index(4096*10, 20)
6651.2.2 by Martin
Apply 2to3 xrange fix and fix up with sixish range
1461
        self.assertExpandOffsets(list(range(10)), index, [0])
3763.8.7 by John Arbash Meinel
A bit of doc updates, start putting in tests for current behavior.
1462
1463
    def test_read_all_when_cached(self):
1464
        # We've read enough that we can grab all the rest in a single request
1465
        index = self.make_index(4096*10, 5)
1466
        self.prepare_index(index, node_ref_lists=0, key_length=1,
1467
                           key_count=1000, row_lengths=[1, 9],
3763.8.15 by John Arbash Meinel
Review comments from Martin. Code clarity/variable name/docstring updates.
1468
                           cached_offsets=[0, 1, 2, 5, 6])
3763.8.7 by John Arbash Meinel
A bit of doc updates, start putting in tests for current behavior.
1469
        # It should fill the remaining nodes, regardless of the one requested
3763.8.15 by John Arbash Meinel
Review comments from Martin. Code clarity/variable name/docstring updates.
1470
        self.assertExpandOffsets([3, 4, 7, 8, 9], index, [3])
1471
        self.assertExpandOffsets([3, 4, 7, 8, 9], index, [8])
1472
        self.assertExpandOffsets([3, 4, 7, 8, 9], index, [9])
3763.8.7 by John Arbash Meinel
A bit of doc updates, start putting in tests for current behavior.
1473
1474
    def test_no_root_node(self):
1475
        index = self.make_index(4096*10, 5)
3763.8.15 by John Arbash Meinel
Review comments from Martin. Code clarity/variable name/docstring updates.
1476
        self.assertExpandOffsets([0], index, [0])
3763.8.7 by John Arbash Meinel
A bit of doc updates, start putting in tests for current behavior.
1477
1478
    def test_include_neighbors(self):
1479
        index = self.make_100_node_index()
1480
        # We expand in both directions, until we have at least 'recommended'
1481
        # pages
3763.8.15 by John Arbash Meinel
Review comments from Martin. Code clarity/variable name/docstring updates.
1482
        self.assertExpandOffsets([9, 10, 11, 12, 13, 14, 15], index, [12])
1483
        self.assertExpandOffsets([88, 89, 90, 91, 92, 93, 94], index, [91])
3763.8.7 by John Arbash Meinel
A bit of doc updates, start putting in tests for current behavior.
1484
        # If we hit an 'edge' we continue in the other direction
3763.8.15 by John Arbash Meinel
Review comments from Martin. Code clarity/variable name/docstring updates.
1485
        self.assertExpandOffsets([1, 2, 3, 4, 5, 6], index, [2])
1486
        self.assertExpandOffsets([94, 95, 96, 97, 98, 99], index, [98])
3763.8.7 by John Arbash Meinel
A bit of doc updates, start putting in tests for current behavior.
1487
1488
        # Requesting many nodes will expand all locations equally
3763.8.15 by John Arbash Meinel
Review comments from Martin. Code clarity/variable name/docstring updates.
1489
        self.assertExpandOffsets([1, 2, 3, 80, 81, 82], index, [2, 81])
1490
        self.assertExpandOffsets([1, 2, 3, 9, 10, 11, 80, 81, 82], index,
3763.8.7 by John Arbash Meinel
A bit of doc updates, start putting in tests for current behavior.
1491
                               [2, 10, 81])
1492
3763.8.8 by John Arbash Meinel
simple test of overlapped behavior
1493
    def test_stop_at_cached(self):
3763.8.7 by John Arbash Meinel
A bit of doc updates, start putting in tests for current behavior.
1494
        index = self.make_100_node_index()
3763.8.15 by John Arbash Meinel
Review comments from Martin. Code clarity/variable name/docstring updates.
1495
        self.set_cached_offsets(index, [0, 10, 19])
1496
        self.assertExpandOffsets([11, 12, 13, 14, 15, 16], index, [11])
1497
        self.assertExpandOffsets([11, 12, 13, 14, 15, 16], index, [12])
1498
        self.assertExpandOffsets([12, 13, 14, 15, 16, 17, 18], index, [15])
1499
        self.assertExpandOffsets([13, 14, 15, 16, 17, 18], index, [16])
1500
        self.assertExpandOffsets([13, 14, 15, 16, 17, 18], index, [17])
1501
        self.assertExpandOffsets([13, 14, 15, 16, 17, 18], index, [18])
3763.8.7 by John Arbash Meinel
A bit of doc updates, start putting in tests for current behavior.
1502
3763.8.8 by John Arbash Meinel
simple test of overlapped behavior
1503
    def test_cannot_fully_expand(self):
3763.8.7 by John Arbash Meinel
A bit of doc updates, start putting in tests for current behavior.
1504
        index = self.make_100_node_index()
3763.8.15 by John Arbash Meinel
Review comments from Martin. Code clarity/variable name/docstring updates.
1505
        self.set_cached_offsets(index, [0, 10, 12])
3763.8.7 by John Arbash Meinel
A bit of doc updates, start putting in tests for current behavior.
1506
        # We don't go into an endless loop if we are bound by cached nodes
3763.8.15 by John Arbash Meinel
Review comments from Martin. Code clarity/variable name/docstring updates.
1507
        self.assertExpandOffsets([11], index, [11])
3763.8.8 by John Arbash Meinel
simple test of overlapped behavior
1508
1509
    def test_overlap(self):
1510
        index = self.make_100_node_index()
3763.8.15 by John Arbash Meinel
Review comments from Martin. Code clarity/variable name/docstring updates.
1511
        self.assertExpandOffsets([10, 11, 12, 13, 14, 15], index, [12, 13])
1512
        self.assertExpandOffsets([10, 11, 12, 13, 14, 15], index, [11, 14])
3763.8.9 by John Arbash Meinel
Finish up the algorithm to stay within a given layer.
1513
1514
    def test_stay_within_layer(self):
1515
        index = self.make_1000_node_index()
1516
        # When expanding a request, we won't read nodes from the next layer
3763.8.15 by John Arbash Meinel
Review comments from Martin. Code clarity/variable name/docstring updates.
1517
        self.assertExpandOffsets([1, 2, 3, 4], index, [2])
1518
        self.assertExpandOffsets([6, 7, 8, 9], index, [6])
1519
        self.assertExpandOffsets([6, 7, 8, 9], index, [9])
1520
        self.assertExpandOffsets([10, 11, 12, 13, 14, 15], index, [10])
1521
        self.assertExpandOffsets([10, 11, 12, 13, 14, 15, 16], index, [13])
3763.8.9 by John Arbash Meinel
Finish up the algorithm to stay within a given layer.
1522
3763.8.15 by John Arbash Meinel
Review comments from Martin. Code clarity/variable name/docstring updates.
1523
        self.set_cached_offsets(index, [0, 4, 12])
1524
        self.assertExpandOffsets([5, 6, 7, 8, 9], index, [7])
1525
        self.assertExpandOffsets([10, 11], index, [11])
3763.8.14 by John Arbash Meinel
Add in a shortcut when we haven't cached much yet.
1526
1527
    def test_small_requests_unexpanded(self):
1528
        index = self.make_100_node_index()
3763.8.15 by John Arbash Meinel
Review comments from Martin. Code clarity/variable name/docstring updates.
1529
        self.set_cached_offsets(index, [0])
1530
        self.assertExpandOffsets([1], index, [1])
1531
        self.assertExpandOffsets([50], index, [50])
3763.8.14 by John Arbash Meinel
Add in a shortcut when we haven't cached much yet.
1532
        # If we request more than one node, then we'll expand
3763.8.15 by John Arbash Meinel
Review comments from Martin. Code clarity/variable name/docstring updates.
1533
        self.assertExpandOffsets([49, 50, 51, 59, 60, 61], index, [50, 60])
3763.8.14 by John Arbash Meinel
Add in a shortcut when we haven't cached much yet.
1534
1535
        # The first pass does not expand
1536
        index = self.make_1000_node_index()
3763.8.15 by John Arbash Meinel
Review comments from Martin. Code clarity/variable name/docstring updates.
1537
        self.set_cached_offsets(index, [0])
1538
        self.assertExpandOffsets([1], index, [1])
1539
        self.set_cached_offsets(index, [0, 1])
1540
        self.assertExpandOffsets([100], index, [100])
1541
        self.set_cached_offsets(index, [0, 1, 100])
3763.8.14 by John Arbash Meinel
Add in a shortcut when we haven't cached much yet.
1542
        # But after the first depth, we will expand
3763.8.15 by John Arbash Meinel
Review comments from Martin. Code clarity/variable name/docstring updates.
1543
        self.assertExpandOffsets([2, 3, 4, 5, 6, 7], index, [2])
1544
        self.assertExpandOffsets([2, 3, 4, 5, 6, 7], index, [4])
1545
        self.set_cached_offsets(index, [0, 1, 2, 3, 4, 5, 6, 7, 100])
1546
        self.assertExpandOffsets([102, 103, 104, 105, 106, 107, 108], index,
1547
                                 [105])