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