/brz/remove-bazaar

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