~bzr-pqm/bzr/bzr.dev

4634.71.1 by John Arbash Meinel
Work around bug #402623 by allowing BTreeGraphIndex(...,unlimited_cache=True).
1
# Copyright (C) 2007, 2009 Canonical Ltd
2592.1.4 by Robert Collins
Create a GraphIndexBuilder.
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
2592.1.4 by Robert Collins
Create a GraphIndexBuilder.
16
17
"""Tests for indices."""
18
2592.1.5 by Robert Collins
Trivial index reading.
19
from bzrlib import errors
2592.1.38 by Robert Collins
Create an InMemoryGraphIndex for temporary indexing.
20
from bzrlib.index import *
2592.1.4 by Robert Collins
Create a GraphIndexBuilder.
21
from bzrlib.tests import TestCaseWithMemoryTransport
2890.2.5 by Robert Collins
Create a content lookup function for bisection in GraphIndex.
22
from bzrlib.transport import get_transport
2592.1.4 by Robert Collins
Create a GraphIndexBuilder.
23
24
25
class TestGraphIndexBuilder(TestCaseWithMemoryTransport):
26
27
    def test_build_index_empty(self):
28
        builder = GraphIndexBuilder()
29
        stream = builder.finish()
30
        contents = stream.read()
2624.2.16 by Robert Collins
Add a key_count method to GraphIndex and friends, allowing optimisation of length calculations by the index.
31
        self.assertEqual(
32
            "Bazaar Graph Index 1\nnode_ref_lists=0\nkey_elements=1\nlen=0\n\n",
33
            contents)
2592.1.6 by Robert Collins
Record the number of node reference lists a particular index has.
34
2624.2.9 by Robert Collins
Introduce multiple component keys, which is what is needed to combine multiple knit indices into one.
35
    def test_build_index_empty_two_element_keys(self):
36
        builder = GraphIndexBuilder(key_elements=2)
37
        stream = builder.finish()
38
        contents = stream.read()
2624.2.16 by Robert Collins
Add a key_count method to GraphIndex and friends, allowing optimisation of length calculations by the index.
39
        self.assertEqual(
40
            "Bazaar Graph Index 1\nnode_ref_lists=0\nkey_elements=2\nlen=0\n\n",
41
            contents)
2624.2.9 by Robert Collins
Introduce multiple component keys, which is what is needed to combine multiple knit indices into one.
42
2592.1.6 by Robert Collins
Record the number of node reference lists a particular index has.
43
    def test_build_index_one_reference_list_empty(self):
44
        builder = GraphIndexBuilder(reference_lists=1)
45
        stream = builder.finish()
46
        contents = stream.read()
2624.2.16 by Robert Collins
Add a key_count method to GraphIndex and friends, allowing optimisation of length calculations by the index.
47
        self.assertEqual(
48
            "Bazaar Graph Index 1\nnode_ref_lists=1\nkey_elements=1\nlen=0\n\n",
49
            contents)
2592.1.4 by Robert Collins
Create a GraphIndexBuilder.
50
2592.1.10 by Robert Collins
Make validate detect node reference parsing errors.
51
    def test_build_index_two_reference_list_empty(self):
52
        builder = GraphIndexBuilder(reference_lists=2)
53
        stream = builder.finish()
54
        contents = stream.read()
2624.2.16 by Robert Collins
Add a key_count method to GraphIndex and friends, allowing optimisation of length calculations by the index.
55
        self.assertEqual(
56
            "Bazaar Graph Index 1\nnode_ref_lists=2\nkey_elements=1\nlen=0\n\n",
57
            contents)
2592.1.10 by Robert Collins
Make validate detect node reference parsing errors.
58
2592.1.46 by Robert Collins
Make GraphIndex accept nodes as key, value, references, so that the method
59
    def test_build_index_one_node_no_refs(self):
60
        builder = GraphIndexBuilder()
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
61
        builder.add_node(('akey', ), 'data')
2592.1.46 by Robert Collins
Make GraphIndex accept nodes as key, value, references, so that the method
62
        stream = builder.finish()
63
        contents = stream.read()
2624.2.16 by Robert Collins
Add a key_count method to GraphIndex and friends, allowing optimisation of length calculations by the index.
64
        self.assertEqual(
65
            "Bazaar Graph Index 1\nnode_ref_lists=0\nkey_elements=1\nlen=1\n"
2592.1.46 by Robert Collins
Make GraphIndex accept nodes as key, value, references, so that the method
66
            "akey\x00\x00\x00data\n\n", contents)
67
68
    def test_build_index_one_node_no_refs_accepts_empty_reflist(self):
69
        builder = GraphIndexBuilder()
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
70
        builder.add_node(('akey', ), 'data', ())
2592.1.12 by Robert Collins
Handle basic node adds.
71
        stream = builder.finish()
72
        contents = stream.read()
2624.2.16 by Robert Collins
Add a key_count method to GraphIndex and friends, allowing optimisation of length calculations by the index.
73
        self.assertEqual(
74
            "Bazaar Graph Index 1\nnode_ref_lists=0\nkey_elements=1\nlen=1\n"
2592.1.43 by Robert Collins
Various index tweaks and test clarity from John's review.
75
            "akey\x00\x00\x00data\n\n", contents)
2592.1.12 by Robert Collins
Handle basic node adds.
76
2624.2.9 by Robert Collins
Introduce multiple component keys, which is what is needed to combine multiple knit indices into one.
77
    def test_build_index_one_node_2_element_keys(self):
2624.2.11 by Robert Collins
Review comments.
78
        # multipart keys are separated by \x00 - because they are fixed length,
79
        # not variable this does not cause any issues, and seems clearer to the
80
        # author.
2624.2.9 by Robert Collins
Introduce multiple component keys, which is what is needed to combine multiple knit indices into one.
81
        builder = GraphIndexBuilder(key_elements=2)
82
        builder.add_node(('akey', 'secondpart'), 'data')
83
        stream = builder.finish()
84
        contents = stream.read()
2624.2.16 by Robert Collins
Add a key_count method to GraphIndex and friends, allowing optimisation of length calculations by the index.
85
        self.assertEqual(
86
            "Bazaar Graph Index 1\nnode_ref_lists=0\nkey_elements=2\nlen=1\n"
2624.2.9 by Robert Collins
Introduce multiple component keys, which is what is needed to combine multiple knit indices into one.
87
            "akey\x00secondpart\x00\x00\x00data\n\n", contents)
88
2592.1.21 by Robert Collins
Empty values are ok.
89
    def test_add_node_empty_value(self):
90
        builder = GraphIndexBuilder()
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
91
        builder.add_node(('akey', ), '')
2592.1.21 by Robert Collins
Empty values are ok.
92
        stream = builder.finish()
93
        contents = stream.read()
2624.2.16 by Robert Collins
Add a key_count method to GraphIndex and friends, allowing optimisation of length calculations by the index.
94
        self.assertEqual(
95
            "Bazaar Graph Index 1\nnode_ref_lists=0\nkey_elements=1\nlen=1\n"
2592.1.43 by Robert Collins
Various index tweaks and test clarity from John's review.
96
            "akey\x00\x00\x00\n\n", contents)
2592.1.21 by Robert Collins
Empty values are ok.
97
2624.2.9 by Robert Collins
Introduce multiple component keys, which is what is needed to combine multiple knit indices into one.
98
    def test_build_index_nodes_sorted(self):
2592.1.17 by Robert Collins
Multi node sort order is defined.
99
        # the highest sorted node comes first.
100
        builder = GraphIndexBuilder()
101
        # use three to have a good chance of glitching dictionary hash
102
        # lookups etc. Insert in randomish order that is not correct
103
        # and not the reverse of the correct order.
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
104
        builder.add_node(('2002', ), 'data')
105
        builder.add_node(('2000', ), 'data')
106
        builder.add_node(('2001', ), 'data')
2592.1.17 by Robert Collins
Multi node sort order is defined.
107
        stream = builder.finish()
108
        contents = stream.read()
2624.2.16 by Robert Collins
Add a key_count method to GraphIndex and friends, allowing optimisation of length calculations by the index.
109
        self.assertEqual(
110
            "Bazaar Graph Index 1\nnode_ref_lists=0\nkey_elements=1\nlen=3\n"
2592.1.43 by Robert Collins
Various index tweaks and test clarity from John's review.
111
            "2000\x00\x00\x00data\n"
112
            "2001\x00\x00\x00data\n"
113
            "2002\x00\x00\x00data\n"
2592.1.17 by Robert Collins
Multi node sort order is defined.
114
            "\n", contents)
115
2624.2.9 by Robert Collins
Introduce multiple component keys, which is what is needed to combine multiple knit indices into one.
116
    def test_build_index_2_element_key_nodes_sorted(self):
117
        # multiple element keys are sorted first-key, second-key.
118
        builder = GraphIndexBuilder(key_elements=2)
119
        # use three values of each key element, to have a good chance of
120
        # glitching dictionary hash lookups etc. Insert in randomish order that
121
        # is not correct and not the reverse of the correct order.
122
        builder.add_node(('2002', '2002'), 'data')
123
        builder.add_node(('2002', '2000'), 'data')
124
        builder.add_node(('2002', '2001'), 'data')
125
        builder.add_node(('2000', '2002'), 'data')
126
        builder.add_node(('2000', '2000'), 'data')
127
        builder.add_node(('2000', '2001'), 'data')
128
        builder.add_node(('2001', '2002'), 'data')
129
        builder.add_node(('2001', '2000'), 'data')
130
        builder.add_node(('2001', '2001'), 'data')
131
        stream = builder.finish()
132
        contents = stream.read()
2624.2.16 by Robert Collins
Add a key_count method to GraphIndex and friends, allowing optimisation of length calculations by the index.
133
        self.assertEqual(
134
            "Bazaar Graph Index 1\nnode_ref_lists=0\nkey_elements=2\nlen=9\n"
2624.2.9 by Robert Collins
Introduce multiple component keys, which is what is needed to combine multiple knit indices into one.
135
            "2000\x002000\x00\x00\x00data\n"
136
            "2000\x002001\x00\x00\x00data\n"
137
            "2000\x002002\x00\x00\x00data\n"
138
            "2001\x002000\x00\x00\x00data\n"
139
            "2001\x002001\x00\x00\x00data\n"
140
            "2001\x002002\x00\x00\x00data\n"
141
            "2002\x002000\x00\x00\x00data\n"
142
            "2002\x002001\x00\x00\x00data\n"
143
            "2002\x002002\x00\x00\x00data\n"
144
            "\n", contents)
145
2592.1.19 by Robert Collins
Node references are tab separated.
146
    def test_build_index_reference_lists_are_included_one(self):
147
        builder = GraphIndexBuilder(reference_lists=1)
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
148
        builder.add_node(('key', ), 'data', ([], ))
2592.1.19 by Robert Collins
Node references are tab separated.
149
        stream = builder.finish()
150
        contents = stream.read()
2624.2.16 by Robert Collins
Add a key_count method to GraphIndex and friends, allowing optimisation of length calculations by the index.
151
        self.assertEqual(
152
            "Bazaar Graph Index 1\nnode_ref_lists=1\nkey_elements=1\nlen=1\n"
2592.1.43 by Robert Collins
Various index tweaks and test clarity from John's review.
153
            "key\x00\x00\x00data\n"
2592.1.19 by Robert Collins
Node references are tab separated.
154
            "\n", contents)
155
2624.2.9 by Robert Collins
Introduce multiple component keys, which is what is needed to combine multiple knit indices into one.
156
    def test_build_index_reference_lists_with_2_element_keys(self):
157
        builder = GraphIndexBuilder(reference_lists=1, key_elements=2)
158
        builder.add_node(('key', 'key2'), 'data', ([], ))
159
        stream = builder.finish()
160
        contents = stream.read()
2624.2.16 by Robert Collins
Add a key_count method to GraphIndex and friends, allowing optimisation of length calculations by the index.
161
        self.assertEqual(
162
            "Bazaar Graph Index 1\nnode_ref_lists=1\nkey_elements=2\nlen=1\n"
2624.2.9 by Robert Collins
Introduce multiple component keys, which is what is needed to combine multiple knit indices into one.
163
            "key\x00key2\x00\x00\x00data\n"
164
            "\n", contents)
165
2592.1.19 by Robert Collins
Node references are tab separated.
166
    def test_build_index_reference_lists_are_included_two(self):
167
        builder = GraphIndexBuilder(reference_lists=2)
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
168
        builder.add_node(('key', ), 'data', ([], []))
2592.1.19 by Robert Collins
Node references are tab separated.
169
        stream = builder.finish()
170
        contents = stream.read()
2624.2.16 by Robert Collins
Add a key_count method to GraphIndex and friends, allowing optimisation of length calculations by the index.
171
        self.assertEqual(
172
            "Bazaar Graph Index 1\nnode_ref_lists=2\nkey_elements=1\nlen=1\n"
2592.1.43 by Robert Collins
Various index tweaks and test clarity from John's review.
173
            "key\x00\x00\t\x00data\n"
2592.1.19 by Robert Collins
Node references are tab separated.
174
            "\n", contents)
175
2592.1.22 by Robert Collins
Node references are byte offsets.
176
    def test_node_references_are_byte_offsets(self):
177
        builder = GraphIndexBuilder(reference_lists=1)
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
178
        builder.add_node(('reference', ), 'data', ([], ))
179
        builder.add_node(('key', ), 'data', ([('reference', )], ))
2592.1.22 by Robert Collins
Node references are byte offsets.
180
        stream = builder.finish()
181
        contents = stream.read()
2624.2.16 by Robert Collins
Add a key_count method to GraphIndex and friends, allowing optimisation of length calculations by the index.
182
        self.assertEqual(
183
            "Bazaar Graph Index 1\nnode_ref_lists=1\nkey_elements=1\nlen=2\n"
184
            "key\x00\x0072\x00data\n"
2592.1.40 by Robert Collins
Reverse index ordering - we do not have date prefixed revids.
185
            "reference\x00\x00\x00data\n"
2592.1.22 by Robert Collins
Node references are byte offsets.
186
            "\n", contents)
187
2592.1.23 by Robert Collins
node reference delimiting tested.
188
    def test_node_references_are_cr_delimited(self):
189
        builder = GraphIndexBuilder(reference_lists=1)
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
190
        builder.add_node(('reference', ), 'data', ([], ))
191
        builder.add_node(('reference2', ), 'data', ([], ))
192
        builder.add_node(('key', ), 'data', ([('reference', ), ('reference2', )], ))
2592.1.23 by Robert Collins
node reference delimiting tested.
193
        stream = builder.finish()
194
        contents = stream.read()
2624.2.16 by Robert Collins
Add a key_count method to GraphIndex and friends, allowing optimisation of length calculations by the index.
195
        self.assertEqual(
196
            "Bazaar Graph Index 1\nnode_ref_lists=1\nkey_elements=1\nlen=3\n"
197
            "key\x00\x00077\r094\x00data\n"
2592.1.43 by Robert Collins
Various index tweaks and test clarity from John's review.
198
            "reference\x00\x00\x00data\n"
199
            "reference2\x00\x00\x00data\n"
2592.1.23 by Robert Collins
node reference delimiting tested.
200
            "\n", contents)
201
2592.1.24 by Robert Collins
Delimiting of multiple reference lists is by \t
202
    def test_multiple_reference_lists_are_tab_delimited(self):
203
        builder = GraphIndexBuilder(reference_lists=2)
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
204
        builder.add_node(('keference', ), 'data', ([], []))
205
        builder.add_node(('rey', ), 'data', ([('keference', )], [('keference', )]))
2592.1.24 by Robert Collins
Delimiting of multiple reference lists is by \t
206
        stream = builder.finish()
207
        contents = stream.read()
2624.2.16 by Robert Collins
Add a key_count method to GraphIndex and friends, allowing optimisation of length calculations by the index.
208
        self.assertEqual(
209
            "Bazaar Graph Index 1\nnode_ref_lists=2\nkey_elements=1\nlen=2\n"
2592.1.43 by Robert Collins
Various index tweaks and test clarity from John's review.
210
            "keference\x00\x00\t\x00data\n"
2624.2.16 by Robert Collins
Add a key_count method to GraphIndex and friends, allowing optimisation of length calculations by the index.
211
            "rey\x00\x0059\t59\x00data\n"
2592.1.24 by Robert Collins
Delimiting of multiple reference lists is by \t
212
            "\n", contents)
213
2592.1.25 by Robert Collins
Fix and tune node offset calculation.
214
    def test_add_node_referencing_missing_key_makes_absent(self):
215
        builder = GraphIndexBuilder(reference_lists=1)
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
216
        builder.add_node(('rey', ), 'data', ([('beference', ), ('aeference2', )], ))
2592.1.25 by Robert Collins
Fix and tune node offset calculation.
217
        stream = builder.finish()
218
        contents = stream.read()
2624.2.16 by Robert Collins
Add a key_count method to GraphIndex and friends, allowing optimisation of length calculations by the index.
219
        self.assertEqual(
220
            "Bazaar Graph Index 1\nnode_ref_lists=1\nkey_elements=1\nlen=1\n"
2592.1.43 by Robert Collins
Various index tweaks and test clarity from John's review.
221
            "aeference2\x00a\x00\x00\n"
222
            "beference\x00a\x00\x00\n"
2624.2.16 by Robert Collins
Add a key_count method to GraphIndex and friends, allowing optimisation of length calculations by the index.
223
            "rey\x00\x00074\r059\x00data\n"
2592.1.25 by Robert Collins
Fix and tune node offset calculation.
224
            "\n", contents)
225
2592.1.26 by Robert Collins
Test digit buffering is accurate.
226
    def test_node_references_three_digits(self):
227
        # test the node digit expands as needed.
228
        builder = GraphIndexBuilder(reference_lists=1)
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
229
        references = [(str(val), ) for val in reversed(range(9))]
230
        builder.add_node(('2-key', ), '', (references, ))
2592.1.26 by Robert Collins
Test digit buffering is accurate.
231
        stream = builder.finish()
232
        contents = stream.read()
2624.2.16 by Robert Collins
Add a key_count method to GraphIndex and friends, allowing optimisation of length calculations by the index.
233
        self.assertEqual(
234
            "Bazaar Graph Index 1\nnode_ref_lists=1\nkey_elements=1\nlen=1\n"
2592.1.40 by Robert Collins
Reverse index ordering - we do not have date prefixed revids.
235
            "0\x00a\x00\x00\n"
236
            "1\x00a\x00\x00\n"
237
            "2\x00a\x00\x00\n"
2624.2.16 by Robert Collins
Add a key_count method to GraphIndex and friends, allowing optimisation of length calculations by the index.
238
            "2-key\x00\x00151\r145\r139\r133\r127\r121\r071\r065\r059\x00\n"
2592.1.40 by Robert Collins
Reverse index ordering - we do not have date prefixed revids.
239
            "3\x00a\x00\x00\n"
240
            "4\x00a\x00\x00\n"
241
            "5\x00a\x00\x00\n"
242
            "6\x00a\x00\x00\n"
243
            "7\x00a\x00\x00\n"
2592.1.26 by Robert Collins
Test digit buffering is accurate.
244
            "8\x00a\x00\x00\n"
2592.1.40 by Robert Collins
Reverse index ordering - we do not have date prefixed revids.
245
            "\n", contents)
246
247
    def test_absent_has_no_reference_overhead(self):
248
        # the offsets after an absent record should be correct when there are
249
        # >1 reference lists.
250
        builder = GraphIndexBuilder(reference_lists=2)
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
251
        builder.add_node(('parent', ), '', ([('aail', ), ('zther', )], []))
2592.1.40 by Robert Collins
Reverse index ordering - we do not have date prefixed revids.
252
        stream = builder.finish()
253
        contents = stream.read()
2624.2.16 by Robert Collins
Add a key_count method to GraphIndex and friends, allowing optimisation of length calculations by the index.
254
        self.assertEqual(
255
            "Bazaar Graph Index 1\nnode_ref_lists=2\nkey_elements=1\nlen=1\n"
2592.1.40 by Robert Collins
Reverse index ordering - we do not have date prefixed revids.
256
            "aail\x00a\x00\x00\n"
2624.2.16 by Robert Collins
Add a key_count method to GraphIndex and friends, allowing optimisation of length calculations by the index.
257
            "parent\x00\x0059\r84\t\x00\n"
2592.1.40 by Robert Collins
Reverse index ordering - we do not have date prefixed revids.
258
            "zther\x00a\x00\x00\n"
2592.1.26 by Robert Collins
Test digit buffering is accurate.
259
            "\n", contents)
260
2592.1.13 by Robert Collins
Handle mismatched numbers of reference lists.
261
    def test_add_node_bad_key(self):
2592.1.12 by Robert Collins
Handle basic node adds.
262
        builder = GraphIndexBuilder()
2592.1.14 by Robert Collins
Detect bad reference key values.
263
        for bad_char in '\t\n\x0b\x0c\r\x00 ':
264
            self.assertRaises(errors.BadIndexKey, builder.add_node,
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
265
                ('a%skey' % bad_char, ), 'data')
266
        self.assertRaises(errors.BadIndexKey, builder.add_node,
267
                ('', ), 'data')
268
        self.assertRaises(errors.BadIndexKey, builder.add_node,
269
                'not-a-tuple', 'data')
270
        # not enough length
271
        self.assertRaises(errors.BadIndexKey, builder.add_node,
272
                (), 'data')
273
        # too long
274
        self.assertRaises(errors.BadIndexKey, builder.add_node,
275
                ('primary', 'secondary'), 'data')
2624.2.9 by Robert Collins
Introduce multiple component keys, which is what is needed to combine multiple knit indices into one.
276
        # secondary key elements get checked too:
277
        builder = GraphIndexBuilder(key_elements=2)
278
        for bad_char in '\t\n\x0b\x0c\r\x00 ':
279
            self.assertRaises(errors.BadIndexKey, builder.add_node,
280
                ('prefix', 'a%skey' % bad_char), 'data')
2592.1.12 by Robert Collins
Handle basic node adds.
281
2592.1.13 by Robert Collins
Handle mismatched numbers of reference lists.
282
    def test_add_node_bad_data(self):
2592.1.12 by Robert Collins
Handle basic node adds.
283
        builder = GraphIndexBuilder()
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
284
        self.assertRaises(errors.BadIndexValue, builder.add_node, ('akey', ),
2592.1.46 by Robert Collins
Make GraphIndex accept nodes as key, value, references, so that the method
285
            'data\naa')
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
286
        self.assertRaises(errors.BadIndexValue, builder.add_node, ('akey', ),
2592.1.46 by Robert Collins
Make GraphIndex accept nodes as key, value, references, so that the method
287
            'data\x00aa')
2592.1.12 by Robert Collins
Handle basic node adds.
288
2592.1.13 by Robert Collins
Handle mismatched numbers of reference lists.
289
    def test_add_node_bad_mismatched_ref_lists_length(self):
290
        builder = GraphIndexBuilder()
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
291
        self.assertRaises(errors.BadIndexValue, builder.add_node, ('akey', ),
2592.1.46 by Robert Collins
Make GraphIndex accept nodes as key, value, references, so that the method
292
            'data aa', ([], ))
2592.1.13 by Robert Collins
Handle mismatched numbers of reference lists.
293
        builder = GraphIndexBuilder(reference_lists=1)
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
294
        self.assertRaises(errors.BadIndexValue, builder.add_node, ('akey', ),
2592.1.46 by Robert Collins
Make GraphIndex accept nodes as key, value, references, so that the method
295
            'data aa')
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
296
        self.assertRaises(errors.BadIndexValue, builder.add_node, ('akey', ),
2592.1.46 by Robert Collins
Make GraphIndex accept nodes as key, value, references, so that the method
297
            'data aa', (), )
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
298
        self.assertRaises(errors.BadIndexValue, builder.add_node, ('akey', ),
2592.1.46 by Robert Collins
Make GraphIndex accept nodes as key, value, references, so that the method
299
            'data aa', ([], []))
2592.1.13 by Robert Collins
Handle mismatched numbers of reference lists.
300
        builder = GraphIndexBuilder(reference_lists=2)
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
301
        self.assertRaises(errors.BadIndexValue, builder.add_node, ('akey', ),
2592.1.46 by Robert Collins
Make GraphIndex accept nodes as key, value, references, so that the method
302
            'data aa')
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
303
        self.assertRaises(errors.BadIndexValue, builder.add_node, ('akey', ),
2592.1.46 by Robert Collins
Make GraphIndex accept nodes as key, value, references, so that the method
304
            'data aa', ([], ))
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
305
        self.assertRaises(errors.BadIndexValue, builder.add_node, ('akey', ),
2592.1.46 by Robert Collins
Make GraphIndex accept nodes as key, value, references, so that the method
306
            'data aa', ([], [], []))
2592.1.13 by Robert Collins
Handle mismatched numbers of reference lists.
307
2592.1.14 by Robert Collins
Detect bad reference key values.
308
    def test_add_node_bad_key_in_reference_lists(self):
309
        # first list, first key - trivial
310
        builder = GraphIndexBuilder(reference_lists=1)
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
311
        self.assertRaises(errors.BadIndexKey, builder.add_node, ('akey', ),
312
            'data aa', ([('a key', )], ))
313
        # references keys must be tuples too
314
        self.assertRaises(errors.BadIndexKey, builder.add_node, ('akey', ),
315
            'data aa', (['not-a-tuple'], ))
316
        # not enough length
317
        self.assertRaises(errors.BadIndexKey, builder.add_node, ('akey', ),
318
            'data aa', ([()], ))
319
        # too long
320
        self.assertRaises(errors.BadIndexKey, builder.add_node, ('akey', ),
321
            'data aa', ([('primary', 'secondary')], ))
2592.1.14 by Robert Collins
Detect bad reference key values.
322
        # need to check more than the first key in the list
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
323
        self.assertRaises(errors.BadIndexKey, builder.add_node, ('akey', ),
324
            'data aa', ([('agoodkey', ), ('that is a bad key', )], ))
2592.1.14 by Robert Collins
Detect bad reference key values.
325
        # and if there is more than one list it should be getting checked
326
        # too
327
        builder = GraphIndexBuilder(reference_lists=2)
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
328
        self.assertRaises(errors.BadIndexKey, builder.add_node, ('akey', ),
2592.1.46 by Robert Collins
Make GraphIndex accept nodes as key, value, references, so that the method
329
            'data aa', ([], ['a bad key']))
2592.1.14 by Robert Collins
Detect bad reference key values.
330
2592.1.15 by Robert Collins
Detect duplicate key insertion.
331
    def test_add_duplicate_key(self):
332
        builder = GraphIndexBuilder()
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
333
        builder.add_node(('key', ), 'data')
334
        self.assertRaises(errors.BadIndexDuplicateKey, builder.add_node, ('key', ),
2592.1.46 by Robert Collins
Make GraphIndex accept nodes as key, value, references, so that the method
335
            'data')
2592.1.15 by Robert Collins
Detect duplicate key insertion.
336
2624.2.9 by Robert Collins
Introduce multiple component keys, which is what is needed to combine multiple knit indices into one.
337
    def test_add_duplicate_key_2_elements(self):
338
        builder = GraphIndexBuilder(key_elements=2)
339
        builder.add_node(('key', 'key'), 'data')
340
        self.assertRaises(errors.BadIndexDuplicateKey, builder.add_node,
341
            ('key', 'key'), 'data')
342
2592.1.16 by Robert Collins
Can add keys after referencing them.
343
    def test_add_key_after_referencing_key(self):
344
        builder = GraphIndexBuilder(reference_lists=1)
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
345
        builder.add_node(('key', ), 'data', ([('reference', )], ))
346
        builder.add_node(('reference', ), 'data', ([],))
2592.1.16 by Robert Collins
Can add keys after referencing them.
347
2624.2.9 by Robert Collins
Introduce multiple component keys, which is what is needed to combine multiple knit indices into one.
348
    def test_add_key_after_referencing_key_2_elements(self):
349
        builder = GraphIndexBuilder(reference_lists=1, key_elements=2)
350
        builder.add_node(('k', 'ey'), 'data', ([('reference', 'tokey')], ))
351
        builder.add_node(('reference', 'tokey'), 'data', ([],))
352
3777.5.3 by John Arbash Meinel
Add Builder.set_optimize(for_size=True) for GraphIndexBuilder and BTreeBuilder.
353
    def test_set_optimize(self):
354
        builder = GraphIndexBuilder(reference_lists=1, key_elements=2)
355
        builder.set_optimize(for_size=True)
356
        self.assertTrue(builder._optimize_for_size)
357
        builder.set_optimize(for_size=False)
358
        self.assertFalse(builder._optimize_for_size)
359
2592.1.5 by Robert Collins
Trivial index reading.
360
361
class TestGraphIndex(TestCaseWithMemoryTransport):
362
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
363
    def make_key(self, number):
364
        return (str(number) + 'X'*100,)
365
366
    def make_value(self, number):
367
            return str(number) + 'Y'*100
368
369
    def make_nodes(self, count=64):
370
        # generate a big enough index that we only read some of it on a typical
371
        # bisection lookup.
372
        nodes = []
373
        for counter in range(count):
374
            nodes.append((self.make_key(counter), self.make_value(counter), ()))
375
        return nodes
376
2624.2.9 by Robert Collins
Introduce multiple component keys, which is what is needed to combine multiple knit indices into one.
377
    def make_index(self, ref_lists=0, key_elements=1, nodes=[]):
378
        builder = GraphIndexBuilder(ref_lists, key_elements=key_elements)
2624.2.17 by Robert Collins
Review feedback.
379
        for key, value, references in nodes:
380
            builder.add_node(key, value, references)
2592.1.5 by Robert Collins
Trivial index reading.
381
        stream = builder.finish()
2890.2.5 by Robert Collins
Create a content lookup function for bisection in GraphIndex.
382
        trans = get_transport('trace+' + self.get_url())
2890.2.1 by Robert Collins
* ``bzrlib.index.GraphIndex`` now requires a size parameter to the
383
        size = trans.put_file('index', stream)
384
        return GraphIndex(trans, 'index', size)
2592.1.5 by Robert Collins
Trivial index reading.
385
2592.1.7 by Robert Collins
A validate that goes boom.
386
    def test_open_bad_index_no_error(self):
387
        trans = self.get_transport()
388
        trans.put_bytes('name', "not an index\n")
2890.2.1 by Robert Collins
* ``bzrlib.index.GraphIndex`` now requires a size parameter to the
389
        index = GraphIndex(trans, 'name', 13)
2592.1.7 by Robert Collins
A validate that goes boom.
390
2890.2.2 by Robert Collins
Opening an index creates a map for the parsed bytes.
391
    def test_open_sets_parsed_map_empty(self):
392
        index = self.make_index()
393
        self.assertEqual([], index._parsed_byte_map)
2890.2.5 by Robert Collins
Create a content lookup function for bisection in GraphIndex.
394
        self.assertEqual([], index._parsed_key_map)
395
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
396
    def test_key_count_buffers(self):
397
        index = self.make_index(nodes=self.make_nodes(2))
398
        # reset the transport log
399
        del index._transport._activity[:]
400
        self.assertEqual(2, index.key_count())
401
        # We should have requested reading the header bytes
402
        self.assertEqual([
403
            ('readv', 'index', [(0, 200)], True, index._size),
404
            ],
405
            index._transport._activity)
406
        # And that should have been enough to trigger reading the whole index
407
        # with buffering
408
        self.assertIsNot(None, index._nodes)
409
410
    def test_lookup_key_via_location_buffers(self):
411
        index = self.make_index()
412
        # reset the transport log
413
        del index._transport._activity[:]
414
        # do a _lookup_keys_via_location call for the middle of the file, which
415
        # is what bisection uses.
416
        result = index._lookup_keys_via_location(
417
            [(index._size // 2, ('missing', ))])
418
        # this should have asked for a readv request, with adjust_for_latency,
419
        # and two regions: the header, and half-way into the file.
420
        self.assertEqual([
421
            ('readv', 'index', [(30, 30), (0, 200)], True, 60),
422
            ],
423
            index._transport._activity)
424
        # and the result should be that the key cannot be present, because this
425
        # is a trivial index.
426
        self.assertEqual([((index._size // 2, ('missing', )), False)],
427
            result)
428
        # And this should have caused the file to be fully buffered
429
        self.assertIsNot(None, index._nodes)
430
        self.assertEqual([], index._parsed_byte_map)
431
2890.2.5 by Robert Collins
Create a content lookup function for bisection in GraphIndex.
432
    def test_first_lookup_key_via_location(self):
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
433
        # We need enough data so that the _HEADER_READV doesn't consume the
434
        # whole file. We always read 800 bytes for every key, and the local
435
        # transport natural expansion is 4096 bytes. So we have to have >8192
436
        # bytes or we will trigger "buffer_all".
437
        # We also want the 'missing' key to fall within the range that *did*
438
        # read
439
        nodes = []
440
        index = self.make_index(nodes=self.make_nodes(64))
2890.2.5 by Robert Collins
Create a content lookup function for bisection in GraphIndex.
441
        # reset the transport log
442
        del index._transport._activity[:]
2890.2.18 by Robert Collins
Review feedback.
443
        # do a _lookup_keys_via_location call for the middle of the file, which
2890.2.5 by Robert Collins
Create a content lookup function for bisection in GraphIndex.
444
        # is what bisection uses.
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
445
        start_lookup = index._size // 2
2890.2.18 by Robert Collins
Review feedback.
446
        result = index._lookup_keys_via_location(
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
447
            [(start_lookup, ('40missing', ))])
2890.2.5 by Robert Collins
Create a content lookup function for bisection in GraphIndex.
448
        # this should have asked for a readv request, with adjust_for_latency,
449
        # and two regions: the header, and half-way into the file.
450
        self.assertEqual([
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
451
            ('readv', 'index',
452
             [(start_lookup, 800), (0, 200)], True, index._size),
2890.2.5 by Robert Collins
Create a content lookup function for bisection in GraphIndex.
453
            ],
454
            index._transport._activity)
455
        # and the result should be that the key cannot be present, because this
456
        # is a trivial index.
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
457
        self.assertEqual([((start_lookup, ('40missing', )), False)],
458
            result)
459
        # And this should not have caused the file to be fully buffered
460
        self.assertIs(None, index._nodes)
461
        # And the regions of the file that have been parsed should be in the
462
        # parsed_byte_map and the parsed_key_map
463
        self.assertEqual([(0, 4008), (5046, 8996)], index._parsed_byte_map)
464
        self.assertEqual([(None, self.make_key(26)),
465
                          (self.make_key(31), self.make_key(48))],
466
                         index._parsed_key_map)
2890.2.5 by Robert Collins
Create a content lookup function for bisection in GraphIndex.
467
468
    def test_parsing_non_adjacent_data_trims(self):
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
469
        index = self.make_index(nodes=self.make_nodes(64))
2890.2.18 by Robert Collins
Review feedback.
470
        result = index._lookup_keys_via_location(
2890.2.5 by Robert Collins
Create a content lookup function for bisection in GraphIndex.
471
            [(index._size // 2, ('40', ))])
472
        # and the result should be that the key cannot be present, because key is
473
        # in the middle of the observed data from a 4K read - the smallest transport
474
        # will do today with this api.
475
        self.assertEqual([((index._size // 2, ('40', )), False)],
476
            result)
477
        # and we should have a parse map that includes the header and the
478
        # region that was parsed after trimming.
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
479
        self.assertEqual([(0, 4008), (5046, 8996)], index._parsed_byte_map)
480
        self.assertEqual([(None, self.make_key(26)),
481
                          (self.make_key(31), self.make_key(48))],
2890.2.5 by Robert Collins
Create a content lookup function for bisection in GraphIndex.
482
            index._parsed_key_map)
483
2890.2.14 by Robert Collins
Parse more than one segment of data from a single readv response if needed.
484
    def test_parsing_data_handles_parsed_contained_regions(self):
485
        # the following patten creates a parsed region that is wholly within a
486
        # single result from the readv layer:
487
        # .... single-read (readv-minimum-size) ...
488
        # which then trims the start and end so the parsed size is < readv
489
        # miniumum.
490
        # then a dual lookup (or a reference lookup for that matter) which
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
491
        # abuts or overlaps the parsed region on both sides will need to
2890.2.14 by Robert Collins
Parse more than one segment of data from a single readv response if needed.
492
        # discard the data in the middle, but parse the end as well.
493
        #
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
494
        # we test this by doing a single lookup to seed the data, then
495
        # a lookup for two keys that are present, and adjacent -
2890.2.14 by Robert Collins
Parse more than one segment of data from a single readv response if needed.
496
        # we except both to be found, and the parsed byte map to include the
497
        # locations of both keys.
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
498
        index = self.make_index(nodes=self.make_nodes(128))
2890.2.18 by Robert Collins
Review feedback.
499
        result = index._lookup_keys_via_location(
2890.2.14 by Robert Collins
Parse more than one segment of data from a single readv response if needed.
500
            [(index._size // 2, ('40', ))])
501
        # and we should have a parse map that includes the header and the
502
        # region that was parsed after trimming.
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
503
        self.assertEqual([(0, 4045), (11759, 15707)], index._parsed_byte_map)
504
        self.assertEqual([(None, self.make_key(116)),
505
                          (self.make_key(35), self.make_key(51))],
2890.2.14 by Robert Collins
Parse more than one segment of data from a single readv response if needed.
506
            index._parsed_key_map)
507
        # now ask for two keys, right before and after the parsed region
2890.2.18 by Robert Collins
Review feedback.
508
        result = index._lookup_keys_via_location(
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
509
            [(11450, self.make_key(34)), (15707, self.make_key(52))])
2890.2.14 by Robert Collins
Parse more than one segment of data from a single readv response if needed.
510
        self.assertEqual([
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
511
            ((11450, self.make_key(34)),
512
             (index, self.make_key(34), self.make_value(34))),
513
            ((15707, self.make_key(52)),
514
             (index, self.make_key(52), self.make_value(52))),
2890.2.14 by Robert Collins
Parse more than one segment of data from a single readv response if needed.
515
            ],
516
            result)
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
517
        self.assertEqual([(0, 4045), (9889, 17993)], index._parsed_byte_map)
2890.2.14 by Robert Collins
Parse more than one segment of data from a single readv response if needed.
518
2890.2.5 by Robert Collins
Create a content lookup function for bisection in GraphIndex.
519
    def test_lookup_missing_key_answers_without_io_when_map_permits(self):
520
        # generate a big enough index that we only read some of it on a typical
521
        # bisection lookup.
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
522
        index = self.make_index(nodes=self.make_nodes(64))
2890.2.5 by Robert Collins
Create a content lookup function for bisection in GraphIndex.
523
        # lookup the keys in the middle of the file
2890.2.18 by Robert Collins
Review feedback.
524
        result =index._lookup_keys_via_location(
2890.2.5 by Robert Collins
Create a content lookup function for bisection in GraphIndex.
525
            [(index._size // 2, ('40', ))])
526
        # check the parse map, this determines the test validity
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
527
        self.assertEqual([(0, 4008), (5046, 8996)], index._parsed_byte_map)
528
        self.assertEqual([(None, self.make_key(26)),
529
                          (self.make_key(31), self.make_key(48))],
2890.2.5 by Robert Collins
Create a content lookup function for bisection in GraphIndex.
530
            index._parsed_key_map)
531
        # reset the transport log
532
        del index._transport._activity[:]
533
        # now looking up a key in the portion of the file already parsed should
534
        # not create a new transport request, and should return False (cannot
535
        # be in the index) - even when the byte location we ask for is outside
536
        # the parsed region
2890.2.18 by Robert Collins
Review feedback.
537
        result = index._lookup_keys_via_location(
2890.2.5 by Robert Collins
Create a content lookup function for bisection in GraphIndex.
538
            [(4000, ('40', ))])
539
        self.assertEqual([((4000, ('40', )), False)],
540
            result)
541
        self.assertEqual([], index._transport._activity)
542
543
    def test_lookup_present_key_answers_without_io_when_map_permits(self):
544
        # generate a big enough index that we only read some of it on a typical
545
        # bisection lookup.
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
546
        index = self.make_index(nodes=self.make_nodes(64))
2890.2.5 by Robert Collins
Create a content lookup function for bisection in GraphIndex.
547
        # lookup the keys in the middle of the file
2890.2.18 by Robert Collins
Review feedback.
548
        result =index._lookup_keys_via_location(
2890.2.5 by Robert Collins
Create a content lookup function for bisection in GraphIndex.
549
            [(index._size // 2, ('40', ))])
550
        # check the parse map, this determines the test validity
551
        self.assertEqual([(0, 4008), (5046, 8996)], index._parsed_byte_map)
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
552
        self.assertEqual([(None, self.make_key(26)),
553
                          (self.make_key(31), self.make_key(48))],
2890.2.5 by Robert Collins
Create a content lookup function for bisection in GraphIndex.
554
            index._parsed_key_map)
555
        # reset the transport log
556
        del index._transport._activity[:]
557
        # now looking up a key in the portion of the file already parsed should
558
        # not create a new transport request, and should return False (cannot
559
        # be in the index) - even when the byte location we ask for is outside
560
        # the parsed region
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
561
        #
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
562
        result = index._lookup_keys_via_location([(4000, self.make_key(40))])
2890.2.5 by Robert Collins
Create a content lookup function for bisection in GraphIndex.
563
        self.assertEqual(
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
564
            [((4000, self.make_key(40)),
565
              (index, self.make_key(40), self.make_value(40)))],
2890.2.5 by Robert Collins
Create a content lookup function for bisection in GraphIndex.
566
            result)
567
        self.assertEqual([], index._transport._activity)
568
569
    def test_lookup_key_below_probed_area(self):
570
        # generate a big enough index that we only read some of it on a typical
571
        # bisection lookup.
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
572
        index = self.make_index(nodes=self.make_nodes(64))
2890.2.5 by Robert Collins
Create a content lookup function for bisection in GraphIndex.
573
        # ask for the key in the middle, but a key that is located in the
574
        # unparsed region before the middle.
2890.2.18 by Robert Collins
Review feedback.
575
        result =index._lookup_keys_via_location(
2890.2.5 by Robert Collins
Create a content lookup function for bisection in GraphIndex.
576
            [(index._size // 2, ('30', ))])
577
        # check the parse map, this determines the test validity
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
578
        self.assertEqual([(0, 4008), (5046, 8996)], index._parsed_byte_map)
579
        self.assertEqual([(None, self.make_key(26)),
580
                          (self.make_key(31), self.make_key(48))],
2890.2.5 by Robert Collins
Create a content lookup function for bisection in GraphIndex.
581
            index._parsed_key_map)
582
        self.assertEqual([((index._size // 2, ('30', )), -1)],
583
            result)
584
585
    def test_lookup_key_above_probed_area(self):
586
        # generate a big enough index that we only read some of it on a typical
587
        # bisection lookup.
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
588
        index = self.make_index(nodes=self.make_nodes(64))
2890.2.5 by Robert Collins
Create a content lookup function for bisection in GraphIndex.
589
        # ask for the key in the middle, but a key that is located in the
590
        # unparsed region after the middle.
2890.2.18 by Robert Collins
Review feedback.
591
        result =index._lookup_keys_via_location(
2890.2.5 by Robert Collins
Create a content lookup function for bisection in GraphIndex.
592
            [(index._size // 2, ('50', ))])
593
        # check the parse map, this determines the test validity
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
594
        self.assertEqual([(0, 4008), (5046, 8996)], index._parsed_byte_map)
595
        self.assertEqual([(None, self.make_key(26)),
596
                          (self.make_key(31), self.make_key(48))],
2890.2.5 by Robert Collins
Create a content lookup function for bisection in GraphIndex.
597
            index._parsed_key_map)
598
        self.assertEqual([((index._size // 2, ('50', )), +1)],
599
            result)
2890.2.2 by Robert Collins
Opening an index creates a map for the parsed bytes.
600
2890.2.6 by Robert Collins
Add support for key references to the index lookup_keys_via_location bisection interface.
601
    def test_lookup_key_resolves_references(self):
602
        # generate a big enough index that we only read some of it on a typical
603
        # bisection lookup.
604
        nodes = []
3665.3.5 by John Arbash Meinel
Move the point at which we 'buffer_all' if we've read >50% of the index.
605
        for counter in range(99):
606
            nodes.append((self.make_key(counter), self.make_value(counter),
607
                ((self.make_key(counter + 20),),)  ))
608
        index = self.make_index(ref_lists=1, nodes=nodes)
609
        # lookup a key in the middle that does not exist, so that when we can
610
        # check that the referred-to-keys are not accessed automatically.
611
        index_size = index._size
612
        index_center = index_size // 2
613
        result = index._lookup_keys_via_location(
614
            [(index_center, ('40', ))])
615
        # check the parse map - only the start and middle should have been
616
        # parsed.
617
        self.assertEqual([(0, 4027), (10198, 14028)], index._parsed_byte_map)
618
        self.assertEqual([(None, self.make_key(17)),
619
                          (self.make_key(44), self.make_key(5))],
620
            index._parsed_key_map)
621
        # and check the transport activity likewise.
622
        self.assertEqual(
623
            [('readv', 'index', [(index_center, 800), (0, 200)], True,
624
                                  index_size)],
625
            index._transport._activity)
626
        # reset the transport log for testing the reference lookup
627
        del index._transport._activity[:]
628
        # now looking up a key in the portion of the file already parsed should
629
        # only perform IO to resolve its key references.
630
        result = index._lookup_keys_via_location([(11000, self.make_key(45))])
631
        self.assertEqual(
632
            [((11000, self.make_key(45)),
633
              (index, self.make_key(45), self.make_value(45),
634
               ((self.make_key(65),),)))],
635
            result)
636
        self.assertEqual([('readv', 'index', [(15093, 800)], True, index_size)],
637
            index._transport._activity)
638
639
    def test_lookup_key_can_buffer_all(self):
640
        nodes = []
2890.2.6 by Robert Collins
Add support for key references to the index lookup_keys_via_location bisection interface.
641
        for counter in range(64):
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
642
            nodes.append((self.make_key(counter), self.make_value(counter),
643
                ((self.make_key(counter + 20),),)  ))
2890.2.6 by Robert Collins
Add support for key references to the index lookup_keys_via_location bisection interface.
644
        index = self.make_index(ref_lists=1, nodes=nodes)
645
        # lookup a key in the middle that does not exist, so that when we can
646
        # check that the referred-to-keys are not accessed automatically.
3665.3.5 by John Arbash Meinel
Move the point at which we 'buffer_all' if we've read >50% of the index.
647
        index_size = index._size
648
        index_center = index_size // 2
649
        result = index._lookup_keys_via_location([(index_center, ('40', ))])
2890.2.6 by Robert Collins
Add support for key references to the index lookup_keys_via_location bisection interface.
650
        # check the parse map - only the start and middle should have been
651
        # parsed.
652
        self.assertEqual([(0, 3890), (6444, 10274)], index._parsed_byte_map)
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
653
        self.assertEqual([(None, self.make_key(25)),
654
                          (self.make_key(37), self.make_key(52))],
2890.2.6 by Robert Collins
Add support for key references to the index lookup_keys_via_location bisection interface.
655
            index._parsed_key_map)
656
        # and check the transport activity likewise.
657
        self.assertEqual(
3665.3.5 by John Arbash Meinel
Move the point at which we 'buffer_all' if we've read >50% of the index.
658
            [('readv', 'index', [(index_center, 800), (0, 200)], True,
659
                                  index_size)],
2890.2.6 by Robert Collins
Add support for key references to the index lookup_keys_via_location bisection interface.
660
            index._transport._activity)
661
        # reset the transport log for testing the reference lookup
662
        del index._transport._activity[:]
663
        # now looking up a key in the portion of the file already parsed should
664
        # only perform IO to resolve its key references.
3665.3.5 by John Arbash Meinel
Move the point at which we 'buffer_all' if we've read >50% of the index.
665
        result = index._lookup_keys_via_location([(7000, self.make_key(40))])
2890.2.6 by Robert Collins
Add support for key references to the index lookup_keys_via_location bisection interface.
666
        self.assertEqual(
3665.3.5 by John Arbash Meinel
Move the point at which we 'buffer_all' if we've read >50% of the index.
667
            [((7000, self.make_key(40)),
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
668
              (index, self.make_key(40), self.make_value(40),
669
               ((self.make_key(60),),)))],
2890.2.6 by Robert Collins
Add support for key references to the index lookup_keys_via_location bisection interface.
670
            result)
3665.3.5 by John Arbash Meinel
Move the point at which we 'buffer_all' if we've read >50% of the index.
671
        # Resolving the references would have required more data read, and we
672
        # are already above the 50% threshold, so it triggered a _buffer_all
673
        self.assertEqual([('get', 'index')], index._transport._activity)
2890.2.6 by Robert Collins
Add support for key references to the index lookup_keys_via_location bisection interface.
674
2592.1.5 by Robert Collins
Trivial index reading.
675
    def test_iter_all_entries_empty(self):
676
        index = self.make_index()
677
        self.assertEqual([], list(index.iter_all_entries()))
678
2592.1.27 by Robert Collins
Test missing end lines with non-empty indices.
679
    def test_iter_all_entries_simple(self):
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
680
        index = self.make_index(nodes=[(('name', ), 'data', ())])
2624.2.14 by Robert Collins
Add source index to the index iteration API to allow mapping back to the origin of retrieved data.
681
        self.assertEqual([(index, ('name', ), 'data')],
2592.1.27 by Robert Collins
Test missing end lines with non-empty indices.
682
            list(index.iter_all_entries()))
683
2624.2.9 by Robert Collins
Introduce multiple component keys, which is what is needed to combine multiple knit indices into one.
684
    def test_iter_all_entries_simple_2_elements(self):
685
        index = self.make_index(key_elements=2,
686
            nodes=[(('name', 'surname'), 'data', ())])
2624.2.14 by Robert Collins
Add source index to the index iteration API to allow mapping back to the origin of retrieved data.
687
        self.assertEqual([(index, ('name', 'surname'), 'data')],
2624.2.9 by Robert Collins
Introduce multiple component keys, which is what is needed to combine multiple knit indices into one.
688
            list(index.iter_all_entries()))
689
2592.1.28 by Robert Collins
Basic two pass iter_all_entries.
690
    def test_iter_all_entries_references_resolved(self):
691
        index = self.make_index(1, nodes=[
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
692
            (('name', ), 'data', ([('ref', )], )),
693
            (('ref', ), 'refdata', ([], ))])
2624.2.14 by Robert Collins
Add source index to the index iteration API to allow mapping back to the origin of retrieved data.
694
        self.assertEqual(set([(index, ('name', ), 'data', ((('ref',),),)),
695
            (index, ('ref', ), 'refdata', ((), ))]),
2592.1.28 by Robert Collins
Basic two pass iter_all_entries.
696
            set(index.iter_all_entries()))
697
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
698
    def test_iter_entries_buffers_once(self):
699
        index = self.make_index(nodes=self.make_nodes(2))
700
        # reset the transport log
701
        del index._transport._activity[:]
702
        self.assertEqual(set([(index, self.make_key(1), self.make_value(1))]),
703
                         set(index.iter_entries([self.make_key(1)])))
704
        # We should have requested reading the header bytes
705
        # But not needed any more than that because it would have triggered a
706
        # buffer all
707
        self.assertEqual([
708
            ('readv', 'index', [(0, 200)], True, index._size),
709
            ],
710
            index._transport._activity)
711
        # And that should have been enough to trigger reading the whole index
712
        # with buffering
713
        self.assertIsNot(None, index._nodes)
714
3665.3.3 by John Arbash Meinel
If we read more than 50% of the whole index,
715
    def test_iter_entries_buffers_by_bytes_read(self):
716
        index = self.make_index(nodes=self.make_nodes(64))
717
        list(index.iter_entries([self.make_key(10)]))
718
        # The first time through isn't enough to trigger a buffer all
719
        self.assertIs(None, index._nodes)
720
        self.assertEqual(4096, index._bytes_read)
721
        # Grabbing a key in that same page won't trigger a buffer all, as we
722
        # still haven't read 50% of the file
723
        list(index.iter_entries([self.make_key(11)]))
724
        self.assertIs(None, index._nodes)
725
        self.assertEqual(4096, index._bytes_read)
726
        # We haven't read more data, so reading outside the range won't trigger
727
        # a buffer all right away
728
        list(index.iter_entries([self.make_key(40)]))
729
        self.assertIs(None, index._nodes)
730
        self.assertEqual(8192, index._bytes_read)
3665.3.5 by John Arbash Meinel
Move the point at which we 'buffer_all' if we've read >50% of the index.
731
        # On the next pass, we will not trigger buffer all if the key is
732
        # available without reading more
3665.3.3 by John Arbash Meinel
If we read more than 50% of the whole index,
733
        list(index.iter_entries([self.make_key(32)]))
3665.3.5 by John Arbash Meinel
Move the point at which we 'buffer_all' if we've read >50% of the index.
734
        self.assertIs(None, index._nodes)
735
        # But if we *would* need to read more to resolve it, then we will
736
        # buffer all.
737
        list(index.iter_entries([self.make_key(60)]))
3665.3.3 by John Arbash Meinel
If we read more than 50% of the whole index,
738
        self.assertIsNot(None, index._nodes)
739
2890.2.10 by Robert Collins
Add test coverage to ensure \r's are not mangled by bisection parsing.
740
    def test_iter_entries_references_resolved(self):
741
        index = self.make_index(1, nodes=[
742
            (('name', ), 'data', ([('ref', ), ('ref', )], )),
743
            (('ref', ), 'refdata', ([], ))])
744
        self.assertEqual(set([(index, ('name', ), 'data', ((('ref',),('ref',)),)),
745
            (index, ('ref', ), 'refdata', ((), ))]),
746
            set(index.iter_entries([('name',), ('ref',)])))
747
748
    def test_iter_entries_references_2_refs_resolved(self):
749
        index = self.make_index(2, nodes=[
750
            (('name', ), 'data', ([('ref', )], [('ref', )])),
751
            (('ref', ), 'refdata', ([], []))])
752
        self.assertEqual(set([(index, ('name', ), 'data', ((('ref',),), (('ref',),))),
753
            (index, ('ref', ), 'refdata', ((), ()))]),
754
            set(index.iter_entries([('name',), ('ref',)])))
755
2592.1.30 by Robert Collins
Absent entries are not yeilded.
756
    def test_iteration_absent_skipped(self):
757
        index = self.make_index(1, nodes=[
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
758
            (('name', ), 'data', ([('ref', )], ))])
2624.2.14 by Robert Collins
Add source index to the index iteration API to allow mapping back to the origin of retrieved data.
759
        self.assertEqual(set([(index, ('name', ), 'data', ((('ref',),),))]),
2592.1.30 by Robert Collins
Absent entries are not yeilded.
760
            set(index.iter_all_entries()))
2624.2.14 by Robert Collins
Add source index to the index iteration API to allow mapping back to the origin of retrieved data.
761
        self.assertEqual(set([(index, ('name', ), 'data', ((('ref',),),))]),
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
762
            set(index.iter_entries([('name', )])))
763
        self.assertEqual([], list(index.iter_entries([('ref', )])))
2592.1.30 by Robert Collins
Absent entries are not yeilded.
764
2624.2.9 by Robert Collins
Introduce multiple component keys, which is what is needed to combine multiple knit indices into one.
765
    def test_iteration_absent_skipped_2_element_keys(self):
766
        index = self.make_index(1, key_elements=2, nodes=[
767
            (('name', 'fin'), 'data', ([('ref', 'erence')], ))])
2624.2.14 by Robert Collins
Add source index to the index iteration API to allow mapping back to the origin of retrieved data.
768
        self.assertEqual(set([(index, ('name', 'fin'), 'data', ((('ref', 'erence'),),))]),
2624.2.9 by Robert Collins
Introduce multiple component keys, which is what is needed to combine multiple knit indices into one.
769
            set(index.iter_all_entries()))
2624.2.14 by Robert Collins
Add source index to the index iteration API to allow mapping back to the origin of retrieved data.
770
        self.assertEqual(set([(index, ('name', 'fin'), 'data', ((('ref', 'erence'),),))]),
2624.2.9 by Robert Collins
Introduce multiple component keys, which is what is needed to combine multiple knit indices into one.
771
            set(index.iter_entries([('name', 'fin')])))
772
        self.assertEqual([], list(index.iter_entries([('ref', 'erence')])))
773
2592.1.31 by Robert Collins
Build a combined graph index to use multiple indices at once.
774
    def test_iter_all_keys(self):
2592.1.29 by Robert Collins
Basic iter_entries working.
775
        index = self.make_index(1, nodes=[
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
776
            (('name', ), 'data', ([('ref', )], )),
777
            (('ref', ), 'refdata', ([], ))])
2624.2.14 by Robert Collins
Add source index to the index iteration API to allow mapping back to the origin of retrieved data.
778
        self.assertEqual(set([(index, ('name', ), 'data', ((('ref',),),)),
779
            (index, ('ref', ), 'refdata', ((), ))]),
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
780
            set(index.iter_entries([('name', ), ('ref', )])))
2592.1.29 by Robert Collins
Basic iter_entries working.
781
2592.1.31 by Robert Collins
Build a combined graph index to use multiple indices at once.
782
    def test_iter_nothing_empty(self):
2592.1.9 by Robert Collins
Iterating no keys should work too.
783
        index = self.make_index()
784
        self.assertEqual([], list(index.iter_entries([])))
785
2592.1.5 by Robert Collins
Trivial index reading.
786
    def test_iter_missing_entry_empty(self):
787
        index = self.make_index()
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
788
        self.assertEqual([], list(index.iter_entries([('a', )])))
2592.1.7 by Robert Collins
A validate that goes boom.
789
2890.2.8 by Robert Collins
Make the size of the index optionally None for the pack-names index.
790
    def test_iter_missing_entry_empty_no_size(self):
791
        index = self.make_index()
792
        index = GraphIndex(index._transport, 'index', None)
793
        self.assertEqual([], list(index.iter_entries([('a', )])))
794
2624.2.9 by Robert Collins
Introduce multiple component keys, which is what is needed to combine multiple knit indices into one.
795
    def test_iter_key_prefix_1_element_key_None(self):
796
        index = self.make_index()
797
        self.assertRaises(errors.BadIndexKey, list,
798
            index.iter_entries_prefix([(None, )]))
799
800
    def test_iter_key_prefix_wrong_length(self):
801
        index = self.make_index()
802
        self.assertRaises(errors.BadIndexKey, list,
803
            index.iter_entries_prefix([('foo', None)]))
804
        index = self.make_index(key_elements=2)
805
        self.assertRaises(errors.BadIndexKey, list,
806
            index.iter_entries_prefix([('foo', )]))
807
        self.assertRaises(errors.BadIndexKey, list,
808
            index.iter_entries_prefix([('foo', None, None)]))
809
810
    def test_iter_key_prefix_1_key_element_no_refs(self):
811
        index = self.make_index( nodes=[
812
            (('name', ), 'data', ()),
813
            (('ref', ), 'refdata', ())])
2624.2.14 by Robert Collins
Add source index to the index iteration API to allow mapping back to the origin of retrieved data.
814
        self.assertEqual(set([(index, ('name', ), 'data'),
815
            (index, ('ref', ), 'refdata')]),
2624.2.9 by Robert Collins
Introduce multiple component keys, which is what is needed to combine multiple knit indices into one.
816
            set(index.iter_entries_prefix([('name', ), ('ref', )])))
817
818
    def test_iter_key_prefix_1_key_element_refs(self):
819
        index = self.make_index(1, nodes=[
820
            (('name', ), 'data', ([('ref', )], )),
821
            (('ref', ), 'refdata', ([], ))])
2624.2.14 by Robert Collins
Add source index to the index iteration API to allow mapping back to the origin of retrieved data.
822
        self.assertEqual(set([(index, ('name', ), 'data', ((('ref',),),)),
823
            (index, ('ref', ), 'refdata', ((), ))]),
2624.2.9 by Robert Collins
Introduce multiple component keys, which is what is needed to combine multiple knit indices into one.
824
            set(index.iter_entries_prefix([('name', ), ('ref', )])))
825
826
    def test_iter_key_prefix_2_key_element_no_refs(self):
827
        index = self.make_index(key_elements=2, nodes=[
828
            (('name', 'fin1'), 'data', ()),
829
            (('name', 'fin2'), 'beta', ()),
830
            (('ref', 'erence'), 'refdata', ())])
2624.2.14 by Robert Collins
Add source index to the index iteration API to allow mapping back to the origin of retrieved data.
831
        self.assertEqual(set([(index, ('name', 'fin1'), 'data'),
832
            (index, ('ref', 'erence'), 'refdata')]),
2624.2.9 by Robert Collins
Introduce multiple component keys, which is what is needed to combine multiple knit indices into one.
833
            set(index.iter_entries_prefix([('name', 'fin1'), ('ref', 'erence')])))
2624.2.14 by Robert Collins
Add source index to the index iteration API to allow mapping back to the origin of retrieved data.
834
        self.assertEqual(set([(index, ('name', 'fin1'), 'data'),
835
            (index, ('name', 'fin2'), 'beta')]),
2624.2.9 by Robert Collins
Introduce multiple component keys, which is what is needed to combine multiple knit indices into one.
836
            set(index.iter_entries_prefix([('name', None)])))
837
838
    def test_iter_key_prefix_2_key_element_refs(self):
839
        index = self.make_index(1, key_elements=2, nodes=[
840
            (('name', 'fin1'), 'data', ([('ref', 'erence')], )),
841
            (('name', 'fin2'), 'beta', ([], )),
842
            (('ref', 'erence'), 'refdata', ([], ))])
2624.2.14 by Robert Collins
Add source index to the index iteration API to allow mapping back to the origin of retrieved data.
843
        self.assertEqual(set([(index, ('name', 'fin1'), 'data', ((('ref', 'erence'),),)),
844
            (index, ('ref', 'erence'), 'refdata', ((), ))]),
2624.2.9 by Robert Collins
Introduce multiple component keys, which is what is needed to combine multiple knit indices into one.
845
            set(index.iter_entries_prefix([('name', 'fin1'), ('ref', 'erence')])))
2624.2.14 by Robert Collins
Add source index to the index iteration API to allow mapping back to the origin of retrieved data.
846
        self.assertEqual(set([(index, ('name', 'fin1'), 'data', ((('ref', 'erence'),),)),
847
            (index, ('name', 'fin2'), 'beta', ((), ))]),
2624.2.9 by Robert Collins
Introduce multiple component keys, which is what is needed to combine multiple knit indices into one.
848
            set(index.iter_entries_prefix([('name', None)])))
849
2624.2.16 by Robert Collins
Add a key_count method to GraphIndex and friends, allowing optimisation of length calculations by the index.
850
    def test_key_count_empty(self):
851
        index = self.make_index()
852
        self.assertEqual(0, index.key_count())
853
854
    def test_key_count_one(self):
855
        index = self.make_index(nodes=[(('name', ), '', ())])
856
        self.assertEqual(1, index.key_count())
857
858
    def test_key_count_two(self):
859
        index = self.make_index(nodes=[
860
            (('name', ), '', ()), (('foo', ), '', ())])
861
        self.assertEqual(2, index.key_count())
862
3665.3.3 by John Arbash Meinel
If we read more than 50% of the whole index,
863
    def test_read_and_parse_tracks_real_read_value(self):
864
        index = self.make_index(nodes=self.make_nodes(10))
865
        del index._transport._activity[:]
866
        index._read_and_parse([(0, 200)])
867
        self.assertEqual([
868
            ('readv', 'index', [(0, 200)], True, index._size),
869
            ],
870
            index._transport._activity)
871
        # The readv expansion code will expand the initial request to 4096
872
        # bytes, which is more than enough to read the entire index, and we
873
        # will track the fact that we read that many bytes.
874
        self.assertEqual(index._size, index._bytes_read)
875
876
    def test_read_and_parse_triggers_buffer_all(self):
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
877
        index = self.make_index(key_elements=2, nodes=[
878
            (('name', 'fin1'), 'data', ()),
879
            (('name', 'fin2'), 'beta', ()),
880
            (('ref', 'erence'), 'refdata', ())])
881
        self.assertTrue(index._size > 0)
882
        self.assertIs(None, index._nodes)
883
        index._read_and_parse([(0, index._size)])
884
        self.assertIsNot(None, index._nodes)
885
2592.1.7 by Robert Collins
A validate that goes boom.
886
    def test_validate_bad_index_errors(self):
887
        trans = self.get_transport()
888
        trans.put_bytes('name', "not an index\n")
2890.2.1 by Robert Collins
* ``bzrlib.index.GraphIndex`` now requires a size parameter to the
889
        index = GraphIndex(trans, 'name', 13)
2592.1.7 by Robert Collins
A validate that goes boom.
890
        self.assertRaises(errors.BadIndexFormatSignature, index.validate)
2592.1.8 by Robert Collins
Empty files should validate ok.
891
2592.1.10 by Robert Collins
Make validate detect node reference parsing errors.
892
    def test_validate_bad_node_refs(self):
893
        index = self.make_index(2)
894
        trans = self.get_transport()
895
        content = trans.get_bytes('index')
896
        # change the options line to end with a rather than a parseable number
897
        new_content = content[:-2] + 'a\n\n'
898
        trans.put_bytes('index', new_content)
899
        self.assertRaises(errors.BadIndexOptions, index.validate)
900
2592.1.27 by Robert Collins
Test missing end lines with non-empty indices.
901
    def test_validate_missing_end_line_empty(self):
2592.1.11 by Robert Collins
Detect truncated indices.
902
        index = self.make_index(2)
903
        trans = self.get_transport()
904
        content = trans.get_bytes('index')
905
        # truncate the last byte
906
        trans.put_bytes('index', content[:-1])
907
        self.assertRaises(errors.BadIndexData, index.validate)
908
2592.1.27 by Robert Collins
Test missing end lines with non-empty indices.
909
    def test_validate_missing_end_line_nonempty(self):
2624.2.9 by Robert Collins
Introduce multiple component keys, which is what is needed to combine multiple knit indices into one.
910
        index = self.make_index(2, nodes=[(('key', ), '', ([], []))])
2592.1.27 by Robert Collins
Test missing end lines with non-empty indices.
911
        trans = self.get_transport()
912
        content = trans.get_bytes('index')
913
        # truncate the last byte
914
        trans.put_bytes('index', content[:-1])
915
        self.assertRaises(errors.BadIndexData, index.validate)
916
2592.1.8 by Robert Collins
Empty files should validate ok.
917
    def test_validate_empty(self):
918
        index = self.make_index()
919
        index.validate()
2592.1.12 by Robert Collins
Handle basic node adds.
920
921
    def test_validate_no_refs_content(self):
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
922
        index = self.make_index(nodes=[(('key', ), 'value', ())])
2592.1.12 by Robert Collins
Handle basic node adds.
923
        index.validate()
2592.1.31 by Robert Collins
Build a combined graph index to use multiple indices at once.
924
4011.5.3 by Andrew Bennetts
Implement and test external_references on GraphIndex and BTreeGraphIndex.
925
    # XXX: external_references tests are duplicated in test_btree_index.  We
926
    # probably should have per_graph_index tests...
927
    def test_external_references_no_refs(self):
928
        index = self.make_index(ref_lists=0, nodes=[])
929
        self.assertRaises(ValueError, index.external_references, 0)
930
931
    def test_external_references_no_results(self):
932
        index = self.make_index(ref_lists=1, nodes=[
933
            (('key',), 'value', ([],))])
934
        self.assertEqual(set(), index.external_references(0))
935
936
    def test_external_references_missing_ref(self):
937
        missing_key = ('missing',)
938
        index = self.make_index(ref_lists=1, nodes=[
939
            (('key',), 'value', ([missing_key],))])
940
        self.assertEqual(set([missing_key]), index.external_references(0))
941
942
    def test_external_references_multiple_ref_lists(self):
943
        missing_key = ('missing',)
944
        index = self.make_index(ref_lists=2, nodes=[
945
            (('key',), 'value', ([], [missing_key]))])
946
        self.assertEqual(set([]), index.external_references(0))
947
        self.assertEqual(set([missing_key]), index.external_references(1))
948
949
    def test_external_references_two_records(self):
950
        index = self.make_index(ref_lists=1, nodes=[
951
            (('key-1',), 'value', ([('key-2',)],)),
952
            (('key-2',), 'value', ([],)),
953
            ])
954
        self.assertEqual(set([]), index.external_references(0))
955
4593.4.12 by John Arbash Meinel
Name the specific index api _find_ancestors, and the public CombinedGraphIndex api find_ancestry()
956
    def test__find_ancestors(self):
4593.4.7 by John Arbash Meinel
Basic implementation of a conforming interface for GraphIndex.
957
        key1 = ('key-1',)
958
        key2 = ('key-2',)
959
        index = self.make_index(ref_lists=1, key_elements=1, nodes=[
960
            (key1, 'value', ([key2],)),
961
            (key2, 'value', ([],)),
962
            ])
963
        parent_map = {}
964
        missing_keys = set()
4593.4.12 by John Arbash Meinel
Name the specific index api _find_ancestors, and the public CombinedGraphIndex api find_ancestry()
965
        search_keys = index._find_ancestors([key1], 0, parent_map, missing_keys)
4593.4.7 by John Arbash Meinel
Basic implementation of a conforming interface for GraphIndex.
966
        self.assertEqual({key1: (key2,)}, parent_map)
967
        self.assertEqual(set(), missing_keys)
968
        self.assertEqual(set([key2]), search_keys)
4593.4.12 by John Arbash Meinel
Name the specific index api _find_ancestors, and the public CombinedGraphIndex api find_ancestry()
969
        search_keys = index._find_ancestors(search_keys, 0, parent_map,
970
                                            missing_keys)
4593.4.7 by John Arbash Meinel
Basic implementation of a conforming interface for GraphIndex.
971
        self.assertEqual({key1: (key2,), key2: ()}, parent_map)
972
        self.assertEqual(set(), missing_keys)
973
        self.assertEqual(set(), search_keys)
974
4593.4.12 by John Arbash Meinel
Name the specific index api _find_ancestors, and the public CombinedGraphIndex api find_ancestry()
975
    def test__find_ancestors_w_missing(self):
4593.4.7 by John Arbash Meinel
Basic implementation of a conforming interface for GraphIndex.
976
        key1 = ('key-1',)
977
        key2 = ('key-2',)
978
        key3 = ('key-3',)
979
        index = self.make_index(ref_lists=1, key_elements=1, nodes=[
980
            (key1, 'value', ([key2],)),
981
            (key2, 'value', ([],)),
982
            ])
983
        parent_map = {}
984
        missing_keys = set()
4593.4.12 by John Arbash Meinel
Name the specific index api _find_ancestors, and the public CombinedGraphIndex api find_ancestry()
985
        search_keys = index._find_ancestors([key2, key3], 0, parent_map,
986
                                            missing_keys)
4593.4.7 by John Arbash Meinel
Basic implementation of a conforming interface for GraphIndex.
987
        self.assertEqual({key2: ()}, parent_map)
988
        self.assertEqual(set([key3]), missing_keys)
989
        self.assertEqual(set(), search_keys)
990
4593.4.12 by John Arbash Meinel
Name the specific index api _find_ancestors, and the public CombinedGraphIndex api find_ancestry()
991
    def test__find_ancestors_dont_search_known(self):
4593.4.7 by John Arbash Meinel
Basic implementation of a conforming interface for GraphIndex.
992
        key1 = ('key-1',)
993
        key2 = ('key-2',)
994
        key3 = ('key-3',)
995
        index = self.make_index(ref_lists=1, key_elements=1, nodes=[
996
            (key1, 'value', ([key2],)),
997
            (key2, 'value', ([key3],)),
998
            (key3, 'value', ([],)),
999
            ])
1000
        # We already know about key2, so we won't try to search for key3
1001
        parent_map = {key2: (key3,)}
1002
        missing_keys = set()
4593.4.12 by John Arbash Meinel
Name the specific index api _find_ancestors, and the public CombinedGraphIndex api find_ancestry()
1003
        search_keys = index._find_ancestors([key1], 0, parent_map,
1004
                                            missing_keys)
4593.4.7 by John Arbash Meinel
Basic implementation of a conforming interface for GraphIndex.
1005
        self.assertEqual({key1: (key2,), key2: (key3,)}, parent_map)
1006
        self.assertEqual(set(), missing_keys)
1007
        self.assertEqual(set(), search_keys)
1008
4634.71.1 by John Arbash Meinel
Work around bug #402623 by allowing BTreeGraphIndex(...,unlimited_cache=True).
1009
    def test_supports_unlimited_cache(self):
1010
        builder = GraphIndexBuilder(0, key_elements=1)
1011
        stream = builder.finish()
1012
        trans = get_transport(self.get_url())
1013
        size = trans.put_file('index', stream)
1014
        # It doesn't matter what unlimited_cache does here, just that it can be
1015
        # passed
1016
        index = GraphIndex(trans, 'index', size, unlimited_cache=True)
1017
4011.5.3 by Andrew Bennetts
Implement and test external_references on GraphIndex and BTreeGraphIndex.
1018
2592.1.31 by Robert Collins
Build a combined graph index to use multiple indices at once.
1019
class TestCombinedGraphIndex(TestCaseWithMemoryTransport):
1020
2624.2.9 by Robert Collins
Introduce multiple component keys, which is what is needed to combine multiple knit indices into one.
1021
    def make_index(self, name, ref_lists=0, key_elements=1, nodes=[]):
1022
        builder = GraphIndexBuilder(ref_lists, key_elements=key_elements)
2624.2.17 by Robert Collins
Review feedback.
1023
        for key, value, references in nodes:
1024
            builder.add_node(key, value, references)
2592.1.31 by Robert Collins
Build a combined graph index to use multiple indices at once.
1025
        stream = builder.finish()
1026
        trans = self.get_transport()
2890.2.1 by Robert Collins
* ``bzrlib.index.GraphIndex`` now requires a size parameter to the
1027
        size = trans.put_file(name, stream)
1028
        return GraphIndex(trans, name, size)
2592.1.31 by Robert Collins
Build a combined graph index to use multiple indices at once.
1029
3789.1.4 by John Arbash Meinel
CombinedGraphIndex.iter_entries() is now able to reload on request.
1030
    def make_combined_index_with_missing(self, missing=['1', '2']):
3789.1.3 by John Arbash Meinel
CombinedGraphIndex can now reload when calling key_count().
1031
        """Create a CombinedGraphIndex which will have missing indexes.
1032
1033
        This creates a CGI which thinks it has 2 indexes, however they have
1034
        been deleted. If CGI._reload_func() is called, then it will repopulate
1035
        with a new index.
1036
3789.1.4 by John Arbash Meinel
CombinedGraphIndex.iter_entries() is now able to reload on request.
1037
        :param missing: The underlying indexes to delete
3789.1.3 by John Arbash Meinel
CombinedGraphIndex can now reload when calling key_count().
1038
        :return: (CombinedGraphIndex, reload_counter)
1039
        """
1040
        index1 = self.make_index('1', nodes=[(('1',), '', ())])
1041
        index2 = self.make_index('2', nodes=[(('2',), '', ())])
1042
        index3 = self.make_index('3', nodes=[
1043
            (('1',), '', ()),
1044
            (('2',), '', ())])
1045
1046
        # total_reloads, num_changed, num_unchanged
1047
        reload_counter = [0, 0, 0]
1048
        def reload():
1049
            reload_counter[0] += 1
1050
            new_indices = [index3]
1051
            if index._indices == new_indices:
1052
                reload_counter[2] += 1
1053
                return False
1054
            reload_counter[1] += 1
1055
            index._indices[:] = new_indices
1056
            return True
1057
        index = CombinedGraphIndex([index1, index2], reload_func=reload)
1058
        trans = self.get_transport()
3789.1.4 by John Arbash Meinel
CombinedGraphIndex.iter_entries() is now able to reload on request.
1059
        for fname in missing:
1060
            trans.delete(fname)
3789.1.3 by John Arbash Meinel
CombinedGraphIndex can now reload when calling key_count().
1061
        return index, reload_counter
1062
2592.1.31 by Robert Collins
Build a combined graph index to use multiple indices at once.
1063
    def test_open_missing_index_no_error(self):
1064
        trans = self.get_transport()
2890.2.1 by Robert Collins
* ``bzrlib.index.GraphIndex`` now requires a size parameter to the
1065
        index1 = GraphIndex(trans, 'missing', 100)
2592.1.31 by Robert Collins
Build a combined graph index to use multiple indices at once.
1066
        index = CombinedGraphIndex([index1])
1067
2592.1.37 by Robert Collins
Add CombinedGraphIndex.insert_index.
1068
    def test_add_index(self):
1069
        index = CombinedGraphIndex([])
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1070
        index1 = self.make_index('name', 0, nodes=[(('key', ), '', ())])
2592.1.37 by Robert Collins
Add CombinedGraphIndex.insert_index.
1071
        index.insert_index(0, index1)
2624.2.14 by Robert Collins
Add source index to the index iteration API to allow mapping back to the origin of retrieved data.
1072
        self.assertEqual([(index1, ('key', ), '')], list(index.iter_all_entries()))
2592.1.37 by Robert Collins
Add CombinedGraphIndex.insert_index.
1073
2592.1.31 by Robert Collins
Build a combined graph index to use multiple indices at once.
1074
    def test_iter_all_entries_empty(self):
1075
        index = CombinedGraphIndex([])
1076
        self.assertEqual([], list(index.iter_all_entries()))
1077
1078
    def test_iter_all_entries_children_empty(self):
1079
        index1 = self.make_index('name')
1080
        index = CombinedGraphIndex([index1])
1081
        self.assertEqual([], list(index.iter_all_entries()))
1082
1083
    def test_iter_all_entries_simple(self):
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1084
        index1 = self.make_index('name', nodes=[(('name', ), 'data', ())])
2592.1.31 by Robert Collins
Build a combined graph index to use multiple indices at once.
1085
        index = CombinedGraphIndex([index1])
2624.2.14 by Robert Collins
Add source index to the index iteration API to allow mapping back to the origin of retrieved data.
1086
        self.assertEqual([(index1, ('name', ), 'data')],
2592.1.31 by Robert Collins
Build a combined graph index to use multiple indices at once.
1087
            list(index.iter_all_entries()))
1088
1089
    def test_iter_all_entries_two_indices(self):
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1090
        index1 = self.make_index('name1', nodes=[(('name', ), 'data', ())])
1091
        index2 = self.make_index('name2', nodes=[(('2', ), '', ())])
2592.1.31 by Robert Collins
Build a combined graph index to use multiple indices at once.
1092
        index = CombinedGraphIndex([index1, index2])
2624.2.14 by Robert Collins
Add source index to the index iteration API to allow mapping back to the origin of retrieved data.
1093
        self.assertEqual([(index1, ('name', ), 'data'),
1094
            (index2, ('2', ), '')],
2592.1.31 by Robert Collins
Build a combined graph index to use multiple indices at once.
1095
            list(index.iter_all_entries()))
1096
2592.1.39 by Robert Collins
CombinedGraphIndex.iter_entries does not need to see all entries.
1097
    def test_iter_entries_two_indices_dup_key(self):
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1098
        index1 = self.make_index('name1', nodes=[(('name', ), 'data', ())])
1099
        index2 = self.make_index('name2', nodes=[(('name', ), 'data', ())])
2592.1.39 by Robert Collins
CombinedGraphIndex.iter_entries does not need to see all entries.
1100
        index = CombinedGraphIndex([index1, index2])
2624.2.14 by Robert Collins
Add source index to the index iteration API to allow mapping back to the origin of retrieved data.
1101
        self.assertEqual([(index1, ('name', ), 'data')],
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1102
            list(index.iter_entries([('name', )])))
2592.1.39 by Robert Collins
CombinedGraphIndex.iter_entries does not need to see all entries.
1103
2592.1.31 by Robert Collins
Build a combined graph index to use multiple indices at once.
1104
    def test_iter_all_entries_two_indices_dup_key(self):
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1105
        index1 = self.make_index('name1', nodes=[(('name', ), 'data', ())])
1106
        index2 = self.make_index('name2', nodes=[(('name', ), 'data', ())])
2592.1.31 by Robert Collins
Build a combined graph index to use multiple indices at once.
1107
        index = CombinedGraphIndex([index1, index2])
2624.2.14 by Robert Collins
Add source index to the index iteration API to allow mapping back to the origin of retrieved data.
1108
        self.assertEqual([(index1, ('name', ), 'data')],
2592.1.31 by Robert Collins
Build a combined graph index to use multiple indices at once.
1109
            list(index.iter_all_entries()))
1110
2624.2.9 by Robert Collins
Introduce multiple component keys, which is what is needed to combine multiple knit indices into one.
1111
    def test_iter_key_prefix_2_key_element_refs(self):
1112
        index1 = self.make_index('1', 1, key_elements=2, nodes=[
1113
            (('name', 'fin1'), 'data', ([('ref', 'erence')], ))])
1114
        index2 = self.make_index('2', 1, key_elements=2, nodes=[
1115
            (('name', 'fin2'), 'beta', ([], )),
1116
            (('ref', 'erence'), 'refdata', ([], ))])
1117
        index = CombinedGraphIndex([index1, index2])
2624.2.14 by Robert Collins
Add source index to the index iteration API to allow mapping back to the origin of retrieved data.
1118
        self.assertEqual(set([(index1, ('name', 'fin1'), 'data', ((('ref', 'erence'),),)),
1119
            (index2, ('ref', 'erence'), 'refdata', ((), ))]),
2624.2.9 by Robert Collins
Introduce multiple component keys, which is what is needed to combine multiple knit indices into one.
1120
            set(index.iter_entries_prefix([('name', 'fin1'), ('ref', 'erence')])))
2624.2.14 by Robert Collins
Add source index to the index iteration API to allow mapping back to the origin of retrieved data.
1121
        self.assertEqual(set([(index1, ('name', 'fin1'), 'data', ((('ref', 'erence'),),)),
1122
            (index2, ('name', 'fin2'), 'beta', ((), ))]),
2624.2.9 by Robert Collins
Introduce multiple component keys, which is what is needed to combine multiple knit indices into one.
1123
            set(index.iter_entries_prefix([('name', None)])))
1124
2592.1.31 by Robert Collins
Build a combined graph index to use multiple indices at once.
1125
    def test_iter_nothing_empty(self):
1126
        index = CombinedGraphIndex([])
1127
        self.assertEqual([], list(index.iter_entries([])))
1128
1129
    def test_iter_nothing_children_empty(self):
1130
        index1 = self.make_index('name')
1131
        index = CombinedGraphIndex([index1])
1132
        self.assertEqual([], list(index.iter_entries([])))
1133
1134
    def test_iter_all_keys(self):
1135
        index1 = self.make_index('1', 1, nodes=[
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1136
            (('name', ), 'data', ([('ref', )], ))])
2592.1.31 by Robert Collins
Build a combined graph index to use multiple indices at once.
1137
        index2 = self.make_index('2', 1, nodes=[
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1138
            (('ref', ), 'refdata', ((), ))])
2592.1.31 by Robert Collins
Build a combined graph index to use multiple indices at once.
1139
        index = CombinedGraphIndex([index1, index2])
2624.2.14 by Robert Collins
Add source index to the index iteration API to allow mapping back to the origin of retrieved data.
1140
        self.assertEqual(set([(index1, ('name', ), 'data', ((('ref', ), ), )),
1141
            (index2, ('ref', ), 'refdata', ((), ))]),
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1142
            set(index.iter_entries([('name', ), ('ref', )])))
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
1143
2592.1.31 by Robert Collins
Build a combined graph index to use multiple indices at once.
1144
    def test_iter_all_keys_dup_entry(self):
1145
        index1 = self.make_index('1', 1, nodes=[
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1146
            (('name', ), 'data', ([('ref', )], )),
1147
            (('ref', ), 'refdata', ([], ))])
2592.1.31 by Robert Collins
Build a combined graph index to use multiple indices at once.
1148
        index2 = self.make_index('2', 1, nodes=[
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1149
            (('ref', ), 'refdata', ([], ))])
2592.1.31 by Robert Collins
Build a combined graph index to use multiple indices at once.
1150
        index = CombinedGraphIndex([index1, index2])
2624.2.14 by Robert Collins
Add source index to the index iteration API to allow mapping back to the origin of retrieved data.
1151
        self.assertEqual(set([(index1, ('name', ), 'data', ((('ref',),),)),
1152
            (index1, ('ref', ), 'refdata', ((), ))]),
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1153
            set(index.iter_entries([('name', ), ('ref', )])))
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
1154
2592.1.31 by Robert Collins
Build a combined graph index to use multiple indices at once.
1155
    def test_iter_missing_entry_empty(self):
1156
        index = CombinedGraphIndex([])
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1157
        self.assertEqual([], list(index.iter_entries([('a', )])))
2592.1.31 by Robert Collins
Build a combined graph index to use multiple indices at once.
1158
1159
    def test_iter_missing_entry_one_index(self):
1160
        index1 = self.make_index('1')
1161
        index = CombinedGraphIndex([index1])
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1162
        self.assertEqual([], list(index.iter_entries([('a', )])))
2592.1.31 by Robert Collins
Build a combined graph index to use multiple indices at once.
1163
1164
    def test_iter_missing_entry_two_index(self):
1165
        index1 = self.make_index('1')
1166
        index2 = self.make_index('2')
1167
        index = CombinedGraphIndex([index1, index2])
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1168
        self.assertEqual([], list(index.iter_entries([('a', )])))
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
1169
2592.1.31 by Robert Collins
Build a combined graph index to use multiple indices at once.
1170
    def test_iter_entry_present_one_index_only(self):
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1171
        index1 = self.make_index('1', nodes=[(('key', ), '', ())])
2592.1.31 by Robert Collins
Build a combined graph index to use multiple indices at once.
1172
        index2 = self.make_index('2', nodes=[])
1173
        index = CombinedGraphIndex([index1, index2])
2624.2.14 by Robert Collins
Add source index to the index iteration API to allow mapping back to the origin of retrieved data.
1174
        self.assertEqual([(index1, ('key', ), '')],
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1175
            list(index.iter_entries([('key', )])))
2592.1.31 by Robert Collins
Build a combined graph index to use multiple indices at once.
1176
        # and in the other direction
1177
        index = CombinedGraphIndex([index2, index1])
2624.2.14 by Robert Collins
Add source index to the index iteration API to allow mapping back to the origin of retrieved data.
1178
        self.assertEqual([(index1, ('key', ), '')],
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1179
            list(index.iter_entries([('key', )])))
2592.1.31 by Robert Collins
Build a combined graph index to use multiple indices at once.
1180
2624.2.16 by Robert Collins
Add a key_count method to GraphIndex and friends, allowing optimisation of length calculations by the index.
1181
    def test_key_count_empty(self):
1182
        index1 = self.make_index('1', nodes=[])
1183
        index2 = self.make_index('2', nodes=[])
1184
        index = CombinedGraphIndex([index1, index2])
1185
        self.assertEqual(0, index.key_count())
1186
1187
    def test_key_count_sums_index_keys(self):
1188
        index1 = self.make_index('1', nodes=[
1189
            (('1',), '', ()),
1190
            (('2',), '', ())])
1191
        index2 = self.make_index('2', nodes=[(('1',), '', ())])
1192
        index = CombinedGraphIndex([index1, index2])
1193
        self.assertEqual(3, index.key_count())
1194
2592.1.31 by Robert Collins
Build a combined graph index to use multiple indices at once.
1195
    def test_validate_bad_child_index_errors(self):
1196
        trans = self.get_transport()
1197
        trans.put_bytes('name', "not an index\n")
2890.2.1 by Robert Collins
* ``bzrlib.index.GraphIndex`` now requires a size parameter to the
1198
        index1 = GraphIndex(trans, 'name', 13)
2592.1.31 by Robert Collins
Build a combined graph index to use multiple indices at once.
1199
        index = CombinedGraphIndex([index1])
1200
        self.assertRaises(errors.BadIndexFormatSignature, index.validate)
1201
1202
    def test_validate_empty(self):
1203
        index = CombinedGraphIndex([])
1204
        index.validate()
2592.1.38 by Robert Collins
Create an InMemoryGraphIndex for temporary indexing.
1205
3789.1.3 by John Arbash Meinel
CombinedGraphIndex can now reload when calling key_count().
1206
    def test_key_count_reloads(self):
3789.1.4 by John Arbash Meinel
CombinedGraphIndex.iter_entries() is now able to reload on request.
1207
        index, reload_counter = self.make_combined_index_with_missing()
3789.1.3 by John Arbash Meinel
CombinedGraphIndex can now reload when calling key_count().
1208
        self.assertEqual(2, index.key_count())
1209
        self.assertEqual([1, 1, 0], reload_counter)
1210
3789.1.4 by John Arbash Meinel
CombinedGraphIndex.iter_entries() is now able to reload on request.
1211
    def test_key_count_no_reload(self):
1212
        index, reload_counter = self.make_combined_index_with_missing()
1213
        index._reload_func = None
1214
        # Without a _reload_func we just raise the exception
1215
        self.assertRaises(errors.NoSuchFile, index.key_count)
1216
3789.1.3 by John Arbash Meinel
CombinedGraphIndex can now reload when calling key_count().
1217
    def test_key_count_reloads_and_fails(self):
3789.1.4 by John Arbash Meinel
CombinedGraphIndex.iter_entries() is now able to reload on request.
1218
        # We have deleted all underlying indexes, so we will try to reload, but
3789.1.3 by John Arbash Meinel
CombinedGraphIndex can now reload when calling key_count().
1219
        # still fail. This is mostly to test we don't get stuck in an infinite
1220
        # loop trying to reload
3789.1.4 by John Arbash Meinel
CombinedGraphIndex.iter_entries() is now able to reload on request.
1221
        index, reload_counter = self.make_combined_index_with_missing(
1222
                                    ['1', '2', '3'])
3789.1.3 by John Arbash Meinel
CombinedGraphIndex can now reload when calling key_count().
1223
        self.assertRaises(errors.NoSuchFile, index.key_count)
1224
        self.assertEqual([2, 1, 1], reload_counter)
1225
3789.1.4 by John Arbash Meinel
CombinedGraphIndex.iter_entries() is now able to reload on request.
1226
    def test_iter_entries_reloads(self):
1227
        index, reload_counter = self.make_combined_index_with_missing()
1228
        result = list(index.iter_entries([('1',), ('2',), ('3',)]))
1229
        index3 = index._indices[0]
1230
        self.assertEqual([(index3, ('1',), ''), (index3, ('2',), '')],
1231
                         result)
1232
        self.assertEqual([1, 1, 0], reload_counter)
1233
1234
    def test_iter_entries_reloads_midway(self):
1235
        # The first index still looks present, so we get interrupted mid-way
1236
        # through
1237
        index, reload_counter = self.make_combined_index_with_missing(['2'])
1238
        index1, index2 = index._indices
1239
        result = list(index.iter_entries([('1',), ('2',), ('3',)]))
1240
        index3 = index._indices[0]
3789.1.5 by John Arbash Meinel
CombinedGraphIndex.iter_all_entries() can now reload when needed.
1241
        # We had already yielded '1', so we just go on to the next, we should
3789.1.6 by John Arbash Meinel
CombinedGraphIndex.iter_entries_prefix can now reload when needed.
1242
        # not yield '1' twice.
3789.1.4 by John Arbash Meinel
CombinedGraphIndex.iter_entries() is now able to reload on request.
1243
        self.assertEqual([(index1, ('1',), ''), (index3, ('2',), '')],
1244
                         result)
1245
        self.assertEqual([1, 1, 0], reload_counter)
1246
1247
    def test_iter_entries_no_reload(self):
1248
        index, reload_counter = self.make_combined_index_with_missing()
1249
        index._reload_func = None
1250
        # Without a _reload_func we just raise the exception
1251
        self.assertListRaises(errors.NoSuchFile, index.iter_entries, [('3',)])
1252
1253
    def test_iter_entries_reloads_and_fails(self):
1254
        index, reload_counter = self.make_combined_index_with_missing(
1255
                                    ['1', '2', '3'])
1256
        self.assertListRaises(errors.NoSuchFile, index.iter_entries, [('3',)])
1257
        self.assertEqual([2, 1, 1], reload_counter)
1258
3789.1.5 by John Arbash Meinel
CombinedGraphIndex.iter_all_entries() can now reload when needed.
1259
    def test_iter_all_entries_reloads(self):
1260
        index, reload_counter = self.make_combined_index_with_missing()
1261
        result = list(index.iter_all_entries())
1262
        index3 = index._indices[0]
1263
        self.assertEqual([(index3, ('1',), ''), (index3, ('2',), '')],
1264
                         result)
1265
        self.assertEqual([1, 1, 0], reload_counter)
1266
1267
    def test_iter_all_entries_reloads_midway(self):
1268
        index, reload_counter = self.make_combined_index_with_missing(['2'])
1269
        index1, index2 = index._indices
1270
        result = list(index.iter_all_entries())
1271
        index3 = index._indices[0]
1272
        # We had already yielded '1', so we just go on to the next, we should
3789.1.6 by John Arbash Meinel
CombinedGraphIndex.iter_entries_prefix can now reload when needed.
1273
        # not yield '1' twice.
3789.1.5 by John Arbash Meinel
CombinedGraphIndex.iter_all_entries() can now reload when needed.
1274
        self.assertEqual([(index1, ('1',), ''), (index3, ('2',), '')],
1275
                         result)
1276
        self.assertEqual([1, 1, 0], reload_counter)
1277
1278
    def test_iter_all_entries_no_reload(self):
1279
        index, reload_counter = self.make_combined_index_with_missing()
1280
        index._reload_func = None
1281
        self.assertListRaises(errors.NoSuchFile, index.iter_all_entries)
1282
1283
    def test_iter_all_entries_reloads_and_fails(self):
1284
        index, reload_counter = self.make_combined_index_with_missing(
1285
                                    ['1', '2', '3'])
1286
        self.assertListRaises(errors.NoSuchFile, index.iter_all_entries)
3789.1.4 by John Arbash Meinel
CombinedGraphIndex.iter_entries() is now able to reload on request.
1287
3789.1.6 by John Arbash Meinel
CombinedGraphIndex.iter_entries_prefix can now reload when needed.
1288
    def test_iter_entries_prefix_reloads(self):
1289
        index, reload_counter = self.make_combined_index_with_missing()
1290
        result = list(index.iter_entries_prefix([('1',)]))
1291
        index3 = index._indices[0]
1292
        self.assertEqual([(index3, ('1',), '')], result)
1293
        self.assertEqual([1, 1, 0], reload_counter)
1294
1295
    def test_iter_entries_prefix_reloads_midway(self):
1296
        index, reload_counter = self.make_combined_index_with_missing(['2'])
1297
        index1, index2 = index._indices
1298
        result = list(index.iter_entries_prefix([('1',)]))
1299
        index3 = index._indices[0]
1300
        # We had already yielded '1', so we just go on to the next, we should
1301
        # not yield '1' twice.
1302
        self.assertEqual([(index1, ('1',), '')], result)
1303
        self.assertEqual([1, 1, 0], reload_counter)
1304
1305
    def test_iter_entries_prefix_no_reload(self):
1306
        index, reload_counter = self.make_combined_index_with_missing()
1307
        index._reload_func = None
1308
        self.assertListRaises(errors.NoSuchFile, index.iter_entries_prefix,
1309
                                                 [('1',)])
1310
1311
    def test_iter_entries_prefix_reloads_and_fails(self):
1312
        index, reload_counter = self.make_combined_index_with_missing(
1313
                                    ['1', '2', '3'])
1314
        self.assertListRaises(errors.NoSuchFile, index.iter_entries_prefix,
1315
                                                 [('1',)])
1316
3789.1.7 by John Arbash Meinel
CombinedGraphIndex.validate() will now reload.
1317
    def test_validate_reloads(self):
1318
        index, reload_counter = self.make_combined_index_with_missing()
1319
        index.validate()
1320
        self.assertEqual([1, 1, 0], reload_counter)
1321
1322
    def test_validate_reloads_midway(self):
1323
        index, reload_counter = self.make_combined_index_with_missing(['2'])
1324
        index.validate()
1325
1326
    def test_validate_no_reload(self):
1327
        index, reload_counter = self.make_combined_index_with_missing()
1328
        index._reload_func = None
1329
        self.assertRaises(errors.NoSuchFile, index.validate)
1330
1331
    def test_validate_reloads_and_fails(self):
1332
        index, reload_counter = self.make_combined_index_with_missing(
1333
                                    ['1', '2', '3'])
1334
        self.assertRaises(errors.NoSuchFile, index.validate)
1335
4593.4.12 by John Arbash Meinel
Name the specific index api _find_ancestors, and the public CombinedGraphIndex api find_ancestry()
1336
    def test_find_ancestors_across_indexes(self):
4593.4.8 by John Arbash Meinel
Implement CombinedGraphIndex.get_ancestry()
1337
        key1 = ('key-1',)
1338
        key2 = ('key-2',)
1339
        key3 = ('key-3',)
1340
        key4 = ('key-4',)
1341
        index1 = self.make_index('12', ref_lists=1, nodes=[
1342
            (key1, 'value', ([],)),
1343
            (key2, 'value', ([key1],)),
1344
            ])
1345
        index2 = self.make_index('34', ref_lists=1, nodes=[
1346
            (key3, 'value', ([key2],)),
1347
            (key4, 'value', ([key3],)),
1348
            ])
1349
        c_index = CombinedGraphIndex([index1, index2])
4593.4.12 by John Arbash Meinel
Name the specific index api _find_ancestors, and the public CombinedGraphIndex api find_ancestry()
1350
        parent_map, missing_keys = c_index.find_ancestry([key1], 0)
4593.4.8 by John Arbash Meinel
Implement CombinedGraphIndex.get_ancestry()
1351
        self.assertEqual({key1: ()}, parent_map)
1352
        self.assertEqual(set(), missing_keys)
1353
        # Now look for a key from index2 which requires us to find the key in
1354
        # the second index, and then continue searching for parents in the
1355
        # first index
4593.4.12 by John Arbash Meinel
Name the specific index api _find_ancestors, and the public CombinedGraphIndex api find_ancestry()
1356
        parent_map, missing_keys = c_index.find_ancestry([key3], 0)
4593.4.8 by John Arbash Meinel
Implement CombinedGraphIndex.get_ancestry()
1357
        self.assertEqual({key1: (), key2: (key1,), key3: (key2,)}, parent_map)
1358
        self.assertEqual(set(), missing_keys)
1359
4593.4.12 by John Arbash Meinel
Name the specific index api _find_ancestors, and the public CombinedGraphIndex api find_ancestry()
1360
    def test_find_ancestors_missing_keys(self):
4593.4.8 by John Arbash Meinel
Implement CombinedGraphIndex.get_ancestry()
1361
        key1 = ('key-1',)
1362
        key2 = ('key-2',)
1363
        key3 = ('key-3',)
1364
        key4 = ('key-4',)
1365
        index1 = self.make_index('12', ref_lists=1, nodes=[
1366
            (key1, 'value', ([],)),
1367
            (key2, 'value', ([key1],)),
1368
            ])
1369
        index2 = self.make_index('34', ref_lists=1, nodes=[
1370
            (key3, 'value', ([key2],)),
1371
            ])
1372
        c_index = CombinedGraphIndex([index1, index2])
1373
        # Searching for a key which is actually not present at all should
1374
        # eventually converge
4593.4.12 by John Arbash Meinel
Name the specific index api _find_ancestors, and the public CombinedGraphIndex api find_ancestry()
1375
        parent_map, missing_keys = c_index.find_ancestry([key4], 0)
4593.4.8 by John Arbash Meinel
Implement CombinedGraphIndex.get_ancestry()
1376
        self.assertEqual({}, parent_map)
1377
        self.assertEqual(set([key4]), missing_keys)
1378
4593.4.12 by John Arbash Meinel
Name the specific index api _find_ancestors, and the public CombinedGraphIndex api find_ancestry()
1379
    def test_find_ancestors_no_indexes(self):
4593.4.8 by John Arbash Meinel
Implement CombinedGraphIndex.get_ancestry()
1380
        c_index = CombinedGraphIndex([])
1381
        key1 = ('key-1',)
4593.4.12 by John Arbash Meinel
Name the specific index api _find_ancestors, and the public CombinedGraphIndex api find_ancestry()
1382
        parent_map, missing_keys = c_index.find_ancestry([key1], 0)
4593.4.8 by John Arbash Meinel
Implement CombinedGraphIndex.get_ancestry()
1383
        self.assertEqual({}, parent_map)
1384
        self.assertEqual(set([key1]), missing_keys)
1385
4593.4.12 by John Arbash Meinel
Name the specific index api _find_ancestors, and the public CombinedGraphIndex api find_ancestry()
1386
    def test_find_ancestors_ghost_parent(self):
4593.4.8 by John Arbash Meinel
Implement CombinedGraphIndex.get_ancestry()
1387
        key1 = ('key-1',)
1388
        key2 = ('key-2',)
1389
        key3 = ('key-3',)
1390
        key4 = ('key-4',)
1391
        index1 = self.make_index('12', ref_lists=1, nodes=[
1392
            (key1, 'value', ([],)),
1393
            (key2, 'value', ([key1],)),
1394
            ])
1395
        index2 = self.make_index('34', ref_lists=1, nodes=[
1396
            (key4, 'value', ([key2, key3],)),
1397
            ])
1398
        c_index = CombinedGraphIndex([index1, index2])
1399
        # Searching for a key which is actually not present at all should
1400
        # eventually converge
4593.4.12 by John Arbash Meinel
Name the specific index api _find_ancestors, and the public CombinedGraphIndex api find_ancestry()
1401
        parent_map, missing_keys = c_index.find_ancestry([key4], 0)
4593.4.8 by John Arbash Meinel
Implement CombinedGraphIndex.get_ancestry()
1402
        self.assertEqual({key4: (key2, key3), key2: (key1,), key1: ()},
1403
                         parent_map)
1404
        self.assertEqual(set([key3]), missing_keys)
1405
4593.4.12 by John Arbash Meinel
Name the specific index api _find_ancestors, and the public CombinedGraphIndex api find_ancestry()
1406
    def test__find_ancestors_empty_index(self):
1407
        index = self.make_index('test', ref_lists=1, key_elements=1, nodes=[])
4593.4.11 by John Arbash Meinel
Snapshot the work in progress.
1408
        parent_map = {}
1409
        missing_keys = set()
4593.4.12 by John Arbash Meinel
Name the specific index api _find_ancestors, and the public CombinedGraphIndex api find_ancestry()
1410
        search_keys = index._find_ancestors([('one',), ('two',)], 0, parent_map,
1411
                                            missing_keys)
4593.4.11 by John Arbash Meinel
Snapshot the work in progress.
1412
        self.assertEqual(set(), search_keys)
1413
        self.assertEqual({}, parent_map)
1414
        self.assertEqual(set([('one',), ('two',)]), missing_keys)
1415
2592.1.38 by Robert Collins
Create an InMemoryGraphIndex for temporary indexing.
1416
1417
class TestInMemoryGraphIndex(TestCaseWithMemoryTransport):
1418
2624.2.10 by Robert Collins
Also add iter_key_prefix support to InMemoryGraphIndex.
1419
    def make_index(self, ref_lists=0, key_elements=1, nodes=[]):
1420
        result = InMemoryGraphIndex(ref_lists, key_elements=key_elements)
2592.1.38 by Robert Collins
Create an InMemoryGraphIndex for temporary indexing.
1421
        result.add_nodes(nodes)
1422
        return result
1423
2624.2.1 by Robert Collins
InMemoryGraphIndex.add_nodes was inconsistent with other metods for non-node-reference indices.
1424
    def test_add_nodes_no_refs(self):
1425
        index = self.make_index(0)
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1426
        index.add_nodes([(('name', ), 'data')])
1427
        index.add_nodes([(('name2', ), ''), (('name3', ), '')])
2624.2.1 by Robert Collins
InMemoryGraphIndex.add_nodes was inconsistent with other metods for non-node-reference indices.
1428
        self.assertEqual(set([
2624.2.14 by Robert Collins
Add source index to the index iteration API to allow mapping back to the origin of retrieved data.
1429
            (index, ('name', ), 'data'),
1430
            (index, ('name2', ), ''),
1431
            (index, ('name3', ), ''),
2624.2.1 by Robert Collins
InMemoryGraphIndex.add_nodes was inconsistent with other metods for non-node-reference indices.
1432
            ]), set(index.iter_all_entries()))
1433
2592.1.38 by Robert Collins
Create an InMemoryGraphIndex for temporary indexing.
1434
    def test_add_nodes(self):
1435
        index = self.make_index(1)
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1436
        index.add_nodes([(('name', ), 'data', ([],))])
1437
        index.add_nodes([(('name2', ), '', ([],)), (('name3', ), '', ([('r', )],))])
2592.1.38 by Robert Collins
Create an InMemoryGraphIndex for temporary indexing.
1438
        self.assertEqual(set([
2624.2.14 by Robert Collins
Add source index to the index iteration API to allow mapping back to the origin of retrieved data.
1439
            (index, ('name', ), 'data', ((),)),
1440
            (index, ('name2', ), '', ((),)),
1441
            (index, ('name3', ), '', ((('r', ), ), )),
2592.1.38 by Robert Collins
Create an InMemoryGraphIndex for temporary indexing.
1442
            ]), set(index.iter_all_entries()))
1443
1444
    def test_iter_all_entries_empty(self):
1445
        index = self.make_index()
1446
        self.assertEqual([], list(index.iter_all_entries()))
1447
1448
    def test_iter_all_entries_simple(self):
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1449
        index = self.make_index(nodes=[(('name', ), 'data')])
2624.2.14 by Robert Collins
Add source index to the index iteration API to allow mapping back to the origin of retrieved data.
1450
        self.assertEqual([(index, ('name', ), 'data')],
2592.1.38 by Robert Collins
Create an InMemoryGraphIndex for temporary indexing.
1451
            list(index.iter_all_entries()))
1452
1453
    def test_iter_all_entries_references(self):
1454
        index = self.make_index(1, nodes=[
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1455
            (('name', ), 'data', ([('ref', )], )),
1456
            (('ref', ), 'refdata', ([], ))])
2624.2.14 by Robert Collins
Add source index to the index iteration API to allow mapping back to the origin of retrieved data.
1457
        self.assertEqual(set([(index, ('name', ), 'data', ((('ref', ),),)),
1458
            (index, ('ref', ), 'refdata', ((), ))]),
2592.1.38 by Robert Collins
Create an InMemoryGraphIndex for temporary indexing.
1459
            set(index.iter_all_entries()))
1460
1461
    def test_iteration_absent_skipped(self):
1462
        index = self.make_index(1, nodes=[
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1463
            (('name', ), 'data', ([('ref', )], ))])
2624.2.14 by Robert Collins
Add source index to the index iteration API to allow mapping back to the origin of retrieved data.
1464
        self.assertEqual(set([(index, ('name', ), 'data', ((('ref',),),))]),
2592.1.38 by Robert Collins
Create an InMemoryGraphIndex for temporary indexing.
1465
            set(index.iter_all_entries()))
2624.2.14 by Robert Collins
Add source index to the index iteration API to allow mapping back to the origin of retrieved data.
1466
        self.assertEqual(set([(index, ('name', ), 'data', ((('ref',),),))]),
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1467
            set(index.iter_entries([('name', )])))
1468
        self.assertEqual([], list(index.iter_entries([('ref', )])))
2592.1.38 by Robert Collins
Create an InMemoryGraphIndex for temporary indexing.
1469
1470
    def test_iter_all_keys(self):
1471
        index = self.make_index(1, nodes=[
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1472
            (('name', ), 'data', ([('ref', )], )),
1473
            (('ref', ), 'refdata', ([], ))])
2624.2.14 by Robert Collins
Add source index to the index iteration API to allow mapping back to the origin of retrieved data.
1474
        self.assertEqual(set([(index, ('name', ), 'data', ((('ref',),),)),
1475
            (index, ('ref', ), 'refdata', ((), ))]),
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1476
            set(index.iter_entries([('name', ), ('ref', )])))
2592.1.38 by Robert Collins
Create an InMemoryGraphIndex for temporary indexing.
1477
2624.2.10 by Robert Collins
Also add iter_key_prefix support to InMemoryGraphIndex.
1478
    def test_iter_key_prefix_1_key_element_no_refs(self):
1479
        index = self.make_index( nodes=[
1480
            (('name', ), 'data'),
1481
            (('ref', ), 'refdata')])
2624.2.14 by Robert Collins
Add source index to the index iteration API to allow mapping back to the origin of retrieved data.
1482
        self.assertEqual(set([(index, ('name', ), 'data'),
1483
            (index, ('ref', ), 'refdata')]),
2624.2.10 by Robert Collins
Also add iter_key_prefix support to InMemoryGraphIndex.
1484
            set(index.iter_entries_prefix([('name', ), ('ref', )])))
1485
1486
    def test_iter_key_prefix_1_key_element_refs(self):
1487
        index = self.make_index(1, nodes=[
1488
            (('name', ), 'data', ([('ref', )], )),
1489
            (('ref', ), 'refdata', ([], ))])
2624.2.14 by Robert Collins
Add source index to the index iteration API to allow mapping back to the origin of retrieved data.
1490
        self.assertEqual(set([(index, ('name', ), 'data', ((('ref',),),)),
1491
            (index, ('ref', ), 'refdata', ((), ))]),
2624.2.10 by Robert Collins
Also add iter_key_prefix support to InMemoryGraphIndex.
1492
            set(index.iter_entries_prefix([('name', ), ('ref', )])))
1493
1494
    def test_iter_key_prefix_2_key_element_no_refs(self):
1495
        index = self.make_index(key_elements=2, nodes=[
1496
            (('name', 'fin1'), 'data'),
1497
            (('name', 'fin2'), 'beta'),
1498
            (('ref', 'erence'), 'refdata')])
2624.2.14 by Robert Collins
Add source index to the index iteration API to allow mapping back to the origin of retrieved data.
1499
        self.assertEqual(set([(index, ('name', 'fin1'), 'data'),
1500
            (index, ('ref', 'erence'), 'refdata')]),
2624.2.10 by Robert Collins
Also add iter_key_prefix support to InMemoryGraphIndex.
1501
            set(index.iter_entries_prefix([('name', 'fin1'), ('ref', 'erence')])))
2624.2.14 by Robert Collins
Add source index to the index iteration API to allow mapping back to the origin of retrieved data.
1502
        self.assertEqual(set([(index, ('name', 'fin1'), 'data'),
1503
            (index, ('name', 'fin2'), 'beta')]),
2624.2.10 by Robert Collins
Also add iter_key_prefix support to InMemoryGraphIndex.
1504
            set(index.iter_entries_prefix([('name', None)])))
1505
1506
    def test_iter_key_prefix_2_key_element_refs(self):
1507
        index = self.make_index(1, key_elements=2, nodes=[
1508
            (('name', 'fin1'), 'data', ([('ref', 'erence')], )),
1509
            (('name', 'fin2'), 'beta', ([], )),
1510
            (('ref', 'erence'), 'refdata', ([], ))])
2624.2.14 by Robert Collins
Add source index to the index iteration API to allow mapping back to the origin of retrieved data.
1511
        self.assertEqual(set([(index, ('name', 'fin1'), 'data', ((('ref', 'erence'),),)),
1512
            (index, ('ref', 'erence'), 'refdata', ((), ))]),
2624.2.10 by Robert Collins
Also add iter_key_prefix support to InMemoryGraphIndex.
1513
            set(index.iter_entries_prefix([('name', 'fin1'), ('ref', 'erence')])))
2624.2.14 by Robert Collins
Add source index to the index iteration API to allow mapping back to the origin of retrieved data.
1514
        self.assertEqual(set([(index, ('name', 'fin1'), 'data', ((('ref', 'erence'),),)),
1515
            (index, ('name', 'fin2'), 'beta', ((), ))]),
2624.2.10 by Robert Collins
Also add iter_key_prefix support to InMemoryGraphIndex.
1516
            set(index.iter_entries_prefix([('name', None)])))
1517
2592.1.38 by Robert Collins
Create an InMemoryGraphIndex for temporary indexing.
1518
    def test_iter_nothing_empty(self):
1519
        index = self.make_index()
1520
        self.assertEqual([], list(index.iter_entries([])))
1521
1522
    def test_iter_missing_entry_empty(self):
1523
        index = self.make_index()
1524
        self.assertEqual([], list(index.iter_entries(['a'])))
1525
2624.2.16 by Robert Collins
Add a key_count method to GraphIndex and friends, allowing optimisation of length calculations by the index.
1526
    def test_key_count_empty(self):
1527
        index = self.make_index()
1528
        self.assertEqual(0, index.key_count())
1529
1530
    def test_key_count_one(self):
1531
        index = self.make_index(nodes=[(('name', ), '')])
1532
        self.assertEqual(1, index.key_count())
1533
1534
    def test_key_count_two(self):
1535
        index = self.make_index(nodes=[(('name', ), ''), (('foo', ), '')])
1536
        self.assertEqual(2, index.key_count())
1537
2592.1.38 by Robert Collins
Create an InMemoryGraphIndex for temporary indexing.
1538
    def test_validate_empty(self):
1539
        index = self.make_index()
1540
        index.validate()
1541
1542
    def test_validate_no_refs_content(self):
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1543
        index = self.make_index(nodes=[(('key', ), 'value')])
2592.1.38 by Robert Collins
Create an InMemoryGraphIndex for temporary indexing.
1544
        index.validate()
1545
1546
2624.2.12 by Robert Collins
Create an adapter between indices with differing key lengths.
1547
class TestGraphIndexPrefixAdapter(TestCaseWithMemoryTransport):
1548
2624.2.13 by Robert Collins
Implement add_node/add_nodes to the GraphIndexPrefixAdapter.
1549
    def make_index(self, ref_lists=1, key_elements=2, nodes=[], add_callback=False):
2624.2.12 by Robert Collins
Create an adapter between indices with differing key lengths.
1550
        result = InMemoryGraphIndex(ref_lists, key_elements=key_elements)
1551
        result.add_nodes(nodes)
2624.2.13 by Robert Collins
Implement add_node/add_nodes to the GraphIndexPrefixAdapter.
1552
        if add_callback:
2624.2.17 by Robert Collins
Review feedback.
1553
            add_nodes_callback = result.add_nodes
2624.2.13 by Robert Collins
Implement add_node/add_nodes to the GraphIndexPrefixAdapter.
1554
        else:
2624.2.17 by Robert Collins
Review feedback.
1555
            add_nodes_callback = None
2624.2.13 by Robert Collins
Implement add_node/add_nodes to the GraphIndexPrefixAdapter.
1556
        adapter = GraphIndexPrefixAdapter(result, ('prefix', ), key_elements - 1,
1557
            add_nodes_callback=add_nodes_callback)
2624.2.12 by Robert Collins
Create an adapter between indices with differing key lengths.
1558
        return result, adapter
1559
2624.2.13 by Robert Collins
Implement add_node/add_nodes to the GraphIndexPrefixAdapter.
1560
    def test_add_node(self):
1561
        index, adapter = self.make_index(add_callback=True)
1562
        adapter.add_node(('key',), 'value', ((('ref',),),))
2624.2.14 by Robert Collins
Add source index to the index iteration API to allow mapping back to the origin of retrieved data.
1563
        self.assertEqual(set([(index, ('prefix', 'key'), 'value', ((('prefix', 'ref'),),))]),
2624.2.13 by Robert Collins
Implement add_node/add_nodes to the GraphIndexPrefixAdapter.
1564
            set(index.iter_all_entries()))
1565
1566
    def test_add_nodes(self):
1567
        index, adapter = self.make_index(add_callback=True)
1568
        adapter.add_nodes((
1569
            (('key',), 'value', ((('ref',),),)),
1570
            (('key2',), 'value2', ((),)),
1571
            ))
1572
        self.assertEqual(set([
2624.2.14 by Robert Collins
Add source index to the index iteration API to allow mapping back to the origin of retrieved data.
1573
            (index, ('prefix', 'key2'), 'value2', ((),)),
1574
            (index, ('prefix', 'key'), 'value', ((('prefix', 'ref'),),))
2624.2.13 by Robert Collins
Implement add_node/add_nodes to the GraphIndexPrefixAdapter.
1575
            ]),
1576
            set(index.iter_all_entries()))
1577
2624.2.12 by Robert Collins
Create an adapter between indices with differing key lengths.
1578
    def test_construct(self):
1579
        index = InMemoryGraphIndex()
1580
        adapter = GraphIndexPrefixAdapter(index, ('prefix', ), 1)
1581
1582
    def test_construct_with_callback(self):
1583
        index = InMemoryGraphIndex()
1584
        adapter = GraphIndexPrefixAdapter(index, ('prefix', ), 1, index.add_nodes)
1585
1586
    def test_iter_all_entries_cross_prefix_map_errors(self):
1587
        index, adapter = self.make_index(nodes=[
1588
            (('prefix', 'key1'), 'data1', ((('prefixaltered', 'key2'),),))])
1589
        self.assertRaises(errors.BadIndexData, list, adapter.iter_all_entries())
1590
1591
    def test_iter_all_entries(self):
1592
        index, adapter = self.make_index(nodes=[
1593
            (('notprefix', 'key1'), 'data', ((), )),
1594
            (('prefix', 'key1'), 'data1', ((), )),
1595
            (('prefix', 'key2'), 'data2', ((('prefix', 'key1'),),))])
2624.2.14 by Robert Collins
Add source index to the index iteration API to allow mapping back to the origin of retrieved data.
1596
        self.assertEqual(set([(index, ('key1', ), 'data1', ((),)),
1597
            (index, ('key2', ), 'data2', ((('key1',),),))]),
2624.2.12 by Robert Collins
Create an adapter between indices with differing key lengths.
1598
            set(adapter.iter_all_entries()))
1599
1600
    def test_iter_entries(self):
1601
        index, adapter = self.make_index(nodes=[
1602
            (('notprefix', 'key1'), 'data', ((), )),
1603
            (('prefix', 'key1'), 'data1', ((), )),
1604
            (('prefix', 'key2'), 'data2', ((('prefix', 'key1'),),))])
1605
        # ask for many - get all
2624.2.14 by Robert Collins
Add source index to the index iteration API to allow mapping back to the origin of retrieved data.
1606
        self.assertEqual(set([(index, ('key1', ), 'data1', ((),)),
1607
            (index, ('key2', ), 'data2', ((('key1', ),),))]),
2624.2.12 by Robert Collins
Create an adapter between indices with differing key lengths.
1608
            set(adapter.iter_entries([('key1', ), ('key2', )])))
1609
        # ask for one, get one
2624.2.14 by Robert Collins
Add source index to the index iteration API to allow mapping back to the origin of retrieved data.
1610
        self.assertEqual(set([(index, ('key1', ), 'data1', ((),))]),
2624.2.12 by Robert Collins
Create an adapter between indices with differing key lengths.
1611
            set(adapter.iter_entries([('key1', )])))
1612
        # ask for missing, get none
1613
        self.assertEqual(set(),
1614
            set(adapter.iter_entries([('key3', )])))
1615
1616
    def test_iter_entries_prefix(self):
1617
        index, adapter = self.make_index(key_elements=3, nodes=[
1618
            (('notprefix', 'foo', 'key1'), 'data', ((), )),
1619
            (('prefix', 'prefix2', 'key1'), 'data1', ((), )),
1620
            (('prefix', 'prefix2', 'key2'), 'data2', ((('prefix', 'prefix2', 'key1'),),))])
1621
        # ask for a prefix, get the results for just that prefix, adjusted.
2624.2.14 by Robert Collins
Add source index to the index iteration API to allow mapping back to the origin of retrieved data.
1622
        self.assertEqual(set([(index, ('prefix2', 'key1', ), 'data1', ((),)),
1623
            (index, ('prefix2', 'key2', ), 'data2', ((('prefix2', 'key1', ),),))]),
2624.2.12 by Robert Collins
Create an adapter between indices with differing key lengths.
1624
            set(adapter.iter_entries_prefix([('prefix2', None)])))
1625
2624.2.16 by Robert Collins
Add a key_count method to GraphIndex and friends, allowing optimisation of length calculations by the index.
1626
    def test_key_count_no_matching_keys(self):
1627
        index, adapter = self.make_index(nodes=[
1628
            (('notprefix', 'key1'), 'data', ((), ))])
1629
        self.assertEqual(0, adapter.key_count())
1630
1631
    def test_key_count_some_keys(self):
1632
        index, adapter = self.make_index(nodes=[
1633
            (('notprefix', 'key1'), 'data', ((), )),
1634
            (('prefix', 'key1'), 'data1', ((), )),
1635
            (('prefix', 'key2'), 'data2', ((('prefix', 'key1'),),))])
1636
        self.assertEqual(2, adapter.key_count())
1637
2624.2.12 by Robert Collins
Create an adapter between indices with differing key lengths.
1638
    def test_validate(self):
1639
        index, adapter = self.make_index()
1640
        calls = []
1641
        def validate():
1642
            calls.append('called')
1643
        index.validate = validate
1644
        adapter.validate()
1645
        self.assertEqual(['called'], calls)