~bzr-pqm/bzr/bzr.dev

2592.1.4 by Robert Collins
Create a GraphIndexBuilder.
1
# Copyright (C) 2007 Canonical Ltd
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
17
"""Tests for 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
2592.1.5 by Robert Collins
Trivial index reading.
353
354
class TestGraphIndex(TestCaseWithMemoryTransport):
355
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
356
    def make_key(self, number):
357
        return (str(number) + 'X'*100,)
358
359
    def make_value(self, number):
360
            return str(number) + 'Y'*100
361
362
    def make_nodes(self, count=64):
363
        # generate a big enough index that we only read some of it on a typical
364
        # bisection lookup.
365
        nodes = []
366
        for counter in range(count):
367
            nodes.append((self.make_key(counter), self.make_value(counter), ()))
368
        return nodes
369
2624.2.9 by Robert Collins
Introduce multiple component keys, which is what is needed to combine multiple knit indices into one.
370
    def make_index(self, ref_lists=0, key_elements=1, nodes=[]):
371
        builder = GraphIndexBuilder(ref_lists, key_elements=key_elements)
2624.2.17 by Robert Collins
Review feedback.
372
        for key, value, references in nodes:
373
            builder.add_node(key, value, references)
2592.1.5 by Robert Collins
Trivial index reading.
374
        stream = builder.finish()
2890.2.5 by Robert Collins
Create a content lookup function for bisection in GraphIndex.
375
        trans = get_transport('trace+' + self.get_url())
2890.2.1 by Robert Collins
* ``bzrlib.index.GraphIndex`` now requires a size parameter to the
376
        size = trans.put_file('index', stream)
377
        return GraphIndex(trans, 'index', size)
2592.1.5 by Robert Collins
Trivial index reading.
378
2592.1.7 by Robert Collins
A validate that goes boom.
379
    def test_open_bad_index_no_error(self):
380
        trans = self.get_transport()
381
        trans.put_bytes('name', "not an index\n")
2890.2.1 by Robert Collins
* ``bzrlib.index.GraphIndex`` now requires a size parameter to the
382
        index = GraphIndex(trans, 'name', 13)
2592.1.7 by Robert Collins
A validate that goes boom.
383
2890.2.2 by Robert Collins
Opening an index creates a map for the parsed bytes.
384
    def test_open_sets_parsed_map_empty(self):
385
        index = self.make_index()
386
        self.assertEqual([], index._parsed_byte_map)
2890.2.5 by Robert Collins
Create a content lookup function for bisection in GraphIndex.
387
        self.assertEqual([], index._parsed_key_map)
388
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
389
    def test_key_count_buffers(self):
390
        index = self.make_index(nodes=self.make_nodes(2))
391
        # reset the transport log
392
        del index._transport._activity[:]
393
        self.assertEqual(2, index.key_count())
394
        # We should have requested reading the header bytes
395
        self.assertEqual([
396
            ('readv', 'index', [(0, 200)], True, index._size),
397
            ],
398
            index._transport._activity)
399
        # And that should have been enough to trigger reading the whole index
400
        # with buffering
401
        self.assertIsNot(None, index._nodes)
402
403
    def test_lookup_key_via_location_buffers(self):
404
        index = self.make_index()
405
        # reset the transport log
406
        del index._transport._activity[:]
407
        # do a _lookup_keys_via_location call for the middle of the file, which
408
        # is what bisection uses.
409
        result = index._lookup_keys_via_location(
410
            [(index._size // 2, ('missing', ))])
411
        # this should have asked for a readv request, with adjust_for_latency,
412
        # and two regions: the header, and half-way into the file.
413
        self.assertEqual([
414
            ('readv', 'index', [(30, 30), (0, 200)], True, 60),
415
            ],
416
            index._transport._activity)
417
        # and the result should be that the key cannot be present, because this
418
        # is a trivial index.
419
        self.assertEqual([((index._size // 2, ('missing', )), False)],
420
            result)
421
        # And this should have caused the file to be fully buffered
422
        self.assertIsNot(None, index._nodes)
423
        self.assertEqual([], index._parsed_byte_map)
424
2890.2.5 by Robert Collins
Create a content lookup function for bisection in GraphIndex.
425
    def test_first_lookup_key_via_location(self):
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
426
        # We need enough data so that the _HEADER_READV doesn't consume the
427
        # whole file. We always read 800 bytes for every key, and the local
428
        # transport natural expansion is 4096 bytes. So we have to have >8192
429
        # bytes or we will trigger "buffer_all".
430
        # We also want the 'missing' key to fall within the range that *did*
431
        # read
432
        nodes = []
433
        index = self.make_index(nodes=self.make_nodes(64))
2890.2.5 by Robert Collins
Create a content lookup function for bisection in GraphIndex.
434
        # reset the transport log
435
        del index._transport._activity[:]
2890.2.18 by Robert Collins
Review feedback.
436
        # 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.
437
        # is what bisection uses.
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
438
        start_lookup = index._size // 2
2890.2.18 by Robert Collins
Review feedback.
439
        result = index._lookup_keys_via_location(
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
440
            [(start_lookup, ('40missing', ))])
2890.2.5 by Robert Collins
Create a content lookup function for bisection in GraphIndex.
441
        # this should have asked for a readv request, with adjust_for_latency,
442
        # and two regions: the header, and half-way into the file.
443
        self.assertEqual([
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
444
            ('readv', 'index',
445
             [(start_lookup, 800), (0, 200)], True, index._size),
2890.2.5 by Robert Collins
Create a content lookup function for bisection in GraphIndex.
446
            ],
447
            index._transport._activity)
448
        # and the result should be that the key cannot be present, because this
449
        # is a trivial index.
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
450
        self.assertEqual([((start_lookup, ('40missing', )), False)],
451
            result)
452
        # And this should not have caused the file to be fully buffered
453
        self.assertIs(None, index._nodes)
454
        # And the regions of the file that have been parsed should be in the
455
        # parsed_byte_map and the parsed_key_map
456
        self.assertEqual([(0, 4008), (5046, 8996)], index._parsed_byte_map)
457
        self.assertEqual([(None, self.make_key(26)),
458
                          (self.make_key(31), self.make_key(48))],
459
                         index._parsed_key_map)
2890.2.5 by Robert Collins
Create a content lookup function for bisection in GraphIndex.
460
461
    def test_parsing_non_adjacent_data_trims(self):
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
462
        index = self.make_index(nodes=self.make_nodes(64))
2890.2.18 by Robert Collins
Review feedback.
463
        result = index._lookup_keys_via_location(
2890.2.5 by Robert Collins
Create a content lookup function for bisection in GraphIndex.
464
            [(index._size // 2, ('40', ))])
465
        # and the result should be that the key cannot be present, because key is
466
        # in the middle of the observed data from a 4K read - the smallest transport
467
        # will do today with this api.
468
        self.assertEqual([((index._size // 2, ('40', )), False)],
469
            result)
470
        # and we should have a parse map that includes the header and the
471
        # region that was parsed after trimming.
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
472
        self.assertEqual([(0, 4008), (5046, 8996)], index._parsed_byte_map)
473
        self.assertEqual([(None, self.make_key(26)),
474
                          (self.make_key(31), self.make_key(48))],
2890.2.5 by Robert Collins
Create a content lookup function for bisection in GraphIndex.
475
            index._parsed_key_map)
476
2890.2.14 by Robert Collins
Parse more than one segment of data from a single readv response if needed.
477
    def test_parsing_data_handles_parsed_contained_regions(self):
478
        # the following patten creates a parsed region that is wholly within a
479
        # single result from the readv layer:
480
        # .... single-read (readv-minimum-size) ...
481
        # which then trims the start and end so the parsed size is < readv
482
        # miniumum.
483
        # then a dual lookup (or a reference lookup for that matter) which
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
484
        # 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.
485
        # discard the data in the middle, but parse the end as well.
486
        #
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
487
        # we test this by doing a single lookup to seed the data, then
488
        # 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.
489
        # we except both to be found, and the parsed byte map to include the
490
        # locations of both keys.
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
491
        index = self.make_index(nodes=self.make_nodes(128))
2890.2.18 by Robert Collins
Review feedback.
492
        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.
493
            [(index._size // 2, ('40', ))])
494
        # and we should have a parse map that includes the header and the
495
        # region that was parsed after trimming.
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
496
        self.assertEqual([(0, 4045), (11759, 15707)], index._parsed_byte_map)
497
        self.assertEqual([(None, self.make_key(116)),
498
                          (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.
499
            index._parsed_key_map)
500
        # now ask for two keys, right before and after the parsed region
2890.2.18 by Robert Collins
Review feedback.
501
        result = index._lookup_keys_via_location(
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
502
            [(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.
503
        self.assertEqual([
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
504
            ((11450, self.make_key(34)),
505
             (index, self.make_key(34), self.make_value(34))),
506
            ((15707, self.make_key(52)),
507
             (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.
508
            ],
509
            result)
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
510
        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.
511
2890.2.5 by Robert Collins
Create a content lookup function for bisection in GraphIndex.
512
    def test_lookup_missing_key_answers_without_io_when_map_permits(self):
513
        # generate a big enough index that we only read some of it on a typical
514
        # bisection lookup.
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
515
        index = self.make_index(nodes=self.make_nodes(64))
2890.2.5 by Robert Collins
Create a content lookup function for bisection in GraphIndex.
516
        # lookup the keys in the middle of the file
2890.2.18 by Robert Collins
Review feedback.
517
        result =index._lookup_keys_via_location(
2890.2.5 by Robert Collins
Create a content lookup function for bisection in GraphIndex.
518
            [(index._size // 2, ('40', ))])
519
        # check the parse map, this determines the test validity
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
520
        self.assertEqual([(0, 4008), (5046, 8996)], index._parsed_byte_map)
521
        self.assertEqual([(None, self.make_key(26)),
522
                          (self.make_key(31), self.make_key(48))],
2890.2.5 by Robert Collins
Create a content lookup function for bisection in GraphIndex.
523
            index._parsed_key_map)
524
        # reset the transport log
525
        del index._transport._activity[:]
526
        # now looking up a key in the portion of the file already parsed should
527
        # not create a new transport request, and should return False (cannot
528
        # be in the index) - even when the byte location we ask for is outside
529
        # the parsed region
2890.2.18 by Robert Collins
Review feedback.
530
        result = index._lookup_keys_via_location(
2890.2.5 by Robert Collins
Create a content lookup function for bisection in GraphIndex.
531
            [(4000, ('40', ))])
532
        self.assertEqual([((4000, ('40', )), False)],
533
            result)
534
        self.assertEqual([], index._transport._activity)
535
536
    def test_lookup_present_key_answers_without_io_when_map_permits(self):
537
        # generate a big enough index that we only read some of it on a typical
538
        # bisection lookup.
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
539
        index = self.make_index(nodes=self.make_nodes(64))
2890.2.5 by Robert Collins
Create a content lookup function for bisection in GraphIndex.
540
        # lookup the keys in the middle of the file
2890.2.18 by Robert Collins
Review feedback.
541
        result =index._lookup_keys_via_location(
2890.2.5 by Robert Collins
Create a content lookup function for bisection in GraphIndex.
542
            [(index._size // 2, ('40', ))])
543
        # check the parse map, this determines the test validity
544
        self.assertEqual([(0, 4008), (5046, 8996)], index._parsed_byte_map)
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
545
        self.assertEqual([(None, self.make_key(26)),
546
                          (self.make_key(31), self.make_key(48))],
2890.2.5 by Robert Collins
Create a content lookup function for bisection in GraphIndex.
547
            index._parsed_key_map)
548
        # reset the transport log
549
        del index._transport._activity[:]
550
        # now looking up a key in the portion of the file already parsed should
551
        # not create a new transport request, and should return False (cannot
552
        # be in the index) - even when the byte location we ask for is outside
553
        # the parsed region
554
        # 
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
555
        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.
556
        self.assertEqual(
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
557
            [((4000, self.make_key(40)),
558
              (index, self.make_key(40), self.make_value(40)))],
2890.2.5 by Robert Collins
Create a content lookup function for bisection in GraphIndex.
559
            result)
560
        self.assertEqual([], index._transport._activity)
561
562
    def test_lookup_key_below_probed_area(self):
563
        # generate a big enough index that we only read some of it on a typical
564
        # bisection lookup.
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
565
        index = self.make_index(nodes=self.make_nodes(64))
2890.2.5 by Robert Collins
Create a content lookup function for bisection in GraphIndex.
566
        # ask for the key in the middle, but a key that is located in the
567
        # unparsed region before the middle.
2890.2.18 by Robert Collins
Review feedback.
568
        result =index._lookup_keys_via_location(
2890.2.5 by Robert Collins
Create a content lookup function for bisection in GraphIndex.
569
            [(index._size // 2, ('30', ))])
570
        # check the parse map, this determines the test validity
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
571
        self.assertEqual([(0, 4008), (5046, 8996)], index._parsed_byte_map)
572
        self.assertEqual([(None, self.make_key(26)),
573
                          (self.make_key(31), self.make_key(48))],
2890.2.5 by Robert Collins
Create a content lookup function for bisection in GraphIndex.
574
            index._parsed_key_map)
575
        self.assertEqual([((index._size // 2, ('30', )), -1)],
576
            result)
577
578
    def test_lookup_key_above_probed_area(self):
579
        # generate a big enough index that we only read some of it on a typical
580
        # bisection lookup.
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
581
        index = self.make_index(nodes=self.make_nodes(64))
2890.2.5 by Robert Collins
Create a content lookup function for bisection in GraphIndex.
582
        # ask for the key in the middle, but a key that is located in the
583
        # unparsed region after the middle.
2890.2.18 by Robert Collins
Review feedback.
584
        result =index._lookup_keys_via_location(
2890.2.5 by Robert Collins
Create a content lookup function for bisection in GraphIndex.
585
            [(index._size // 2, ('50', ))])
586
        # check the parse map, this determines the test validity
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
587
        self.assertEqual([(0, 4008), (5046, 8996)], index._parsed_byte_map)
588
        self.assertEqual([(None, self.make_key(26)),
589
                          (self.make_key(31), self.make_key(48))],
2890.2.5 by Robert Collins
Create a content lookup function for bisection in GraphIndex.
590
            index._parsed_key_map)
591
        self.assertEqual([((index._size // 2, ('50', )), +1)],
592
            result)
2890.2.2 by Robert Collins
Opening an index creates a map for the parsed bytes.
593
2890.2.6 by Robert Collins
Add support for key references to the index lookup_keys_via_location bisection interface.
594
    def test_lookup_key_resolves_references(self):
595
        # generate a big enough index that we only read some of it on a typical
596
        # bisection lookup.
597
        nodes = []
3665.3.5 by John Arbash Meinel
Move the point at which we 'buffer_all' if we've read >50% of the index.
598
        for counter in range(99):
599
            nodes.append((self.make_key(counter), self.make_value(counter),
600
                ((self.make_key(counter + 20),),)  ))
601
        index = self.make_index(ref_lists=1, nodes=nodes)
602
        # lookup a key in the middle that does not exist, so that when we can
603
        # check that the referred-to-keys are not accessed automatically.
604
        index_size = index._size
605
        index_center = index_size // 2
606
        result = index._lookup_keys_via_location(
607
            [(index_center, ('40', ))])
608
        # check the parse map - only the start and middle should have been
609
        # parsed.
610
        self.assertEqual([(0, 4027), (10198, 14028)], index._parsed_byte_map)
611
        self.assertEqual([(None, self.make_key(17)),
612
                          (self.make_key(44), self.make_key(5))],
613
            index._parsed_key_map)
614
        # and check the transport activity likewise.
615
        self.assertEqual(
616
            [('readv', 'index', [(index_center, 800), (0, 200)], True,
617
                                  index_size)],
618
            index._transport._activity)
619
        # reset the transport log for testing the reference lookup
620
        del index._transport._activity[:]
621
        # now looking up a key in the portion of the file already parsed should
622
        # only perform IO to resolve its key references.
623
        result = index._lookup_keys_via_location([(11000, self.make_key(45))])
624
        self.assertEqual(
625
            [((11000, self.make_key(45)),
626
              (index, self.make_key(45), self.make_value(45),
627
               ((self.make_key(65),),)))],
628
            result)
629
        self.assertEqual([('readv', 'index', [(15093, 800)], True, index_size)],
630
            index._transport._activity)
631
632
    def test_lookup_key_can_buffer_all(self):
633
        nodes = []
2890.2.6 by Robert Collins
Add support for key references to the index lookup_keys_via_location bisection interface.
634
        for counter in range(64):
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
635
            nodes.append((self.make_key(counter), self.make_value(counter),
636
                ((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.
637
        index = self.make_index(ref_lists=1, nodes=nodes)
638
        # lookup a key in the middle that does not exist, so that when we can
639
        # 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.
640
        index_size = index._size
641
        index_center = index_size // 2
642
        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.
643
        # check the parse map - only the start and middle should have been
644
        # parsed.
645
        self.assertEqual([(0, 3890), (6444, 10274)], index._parsed_byte_map)
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
646
        self.assertEqual([(None, self.make_key(25)),
647
                          (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.
648
            index._parsed_key_map)
649
        # and check the transport activity likewise.
650
        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.
651
            [('readv', 'index', [(index_center, 800), (0, 200)], True,
652
                                  index_size)],
2890.2.6 by Robert Collins
Add support for key references to the index lookup_keys_via_location bisection interface.
653
            index._transport._activity)
654
        # reset the transport log for testing the reference lookup
655
        del index._transport._activity[:]
656
        # now looking up a key in the portion of the file already parsed should
657
        # 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.
658
        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.
659
        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.
660
            [((7000, self.make_key(40)),
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
661
              (index, self.make_key(40), self.make_value(40),
662
               ((self.make_key(60),),)))],
2890.2.6 by Robert Collins
Add support for key references to the index lookup_keys_via_location bisection interface.
663
            result)
3665.3.5 by John Arbash Meinel
Move the point at which we 'buffer_all' if we've read >50% of the index.
664
        # Resolving the references would have required more data read, and we
665
        # are already above the 50% threshold, so it triggered a _buffer_all
666
        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.
667
2592.1.5 by Robert Collins
Trivial index reading.
668
    def test_iter_all_entries_empty(self):
669
        index = self.make_index()
670
        self.assertEqual([], list(index.iter_all_entries()))
671
2592.1.27 by Robert Collins
Test missing end lines with non-empty indices.
672
    def test_iter_all_entries_simple(self):
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
673
        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.
674
        self.assertEqual([(index, ('name', ), 'data')],
2592.1.27 by Robert Collins
Test missing end lines with non-empty indices.
675
            list(index.iter_all_entries()))
676
2624.2.9 by Robert Collins
Introduce multiple component keys, which is what is needed to combine multiple knit indices into one.
677
    def test_iter_all_entries_simple_2_elements(self):
678
        index = self.make_index(key_elements=2,
679
            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.
680
        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.
681
            list(index.iter_all_entries()))
682
2592.1.28 by Robert Collins
Basic two pass iter_all_entries.
683
    def test_iter_all_entries_references_resolved(self):
684
        index = self.make_index(1, nodes=[
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
685
            (('name', ), 'data', ([('ref', )], )),
686
            (('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.
687
        self.assertEqual(set([(index, ('name', ), 'data', ((('ref',),),)),
688
            (index, ('ref', ), 'refdata', ((), ))]),
2592.1.28 by Robert Collins
Basic two pass iter_all_entries.
689
            set(index.iter_all_entries()))
690
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
691
    def test_iter_entries_buffers_once(self):
692
        index = self.make_index(nodes=self.make_nodes(2))
693
        # reset the transport log
694
        del index._transport._activity[:]
695
        self.assertEqual(set([(index, self.make_key(1), self.make_value(1))]),
696
                         set(index.iter_entries([self.make_key(1)])))
697
        # We should have requested reading the header bytes
698
        # But not needed any more than that because it would have triggered a
699
        # buffer all
700
        self.assertEqual([
701
            ('readv', 'index', [(0, 200)], True, index._size),
702
            ],
703
            index._transport._activity)
704
        # And that should have been enough to trigger reading the whole index
705
        # with buffering
706
        self.assertIsNot(None, index._nodes)
707
3665.3.3 by John Arbash Meinel
If we read more than 50% of the whole index,
708
    def test_iter_entries_buffers_by_bytes_read(self):
709
        index = self.make_index(nodes=self.make_nodes(64))
710
        list(index.iter_entries([self.make_key(10)]))
711
        # The first time through isn't enough to trigger a buffer all
712
        self.assertIs(None, index._nodes)
713
        self.assertEqual(4096, index._bytes_read)
714
        # Grabbing a key in that same page won't trigger a buffer all, as we
715
        # still haven't read 50% of the file
716
        list(index.iter_entries([self.make_key(11)]))
717
        self.assertIs(None, index._nodes)
718
        self.assertEqual(4096, index._bytes_read)
719
        # We haven't read more data, so reading outside the range won't trigger
720
        # a buffer all right away
721
        list(index.iter_entries([self.make_key(40)]))
722
        self.assertIs(None, index._nodes)
723
        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.
724
        # On the next pass, we will not trigger buffer all if the key is
725
        # available without reading more
3665.3.3 by John Arbash Meinel
If we read more than 50% of the whole index,
726
        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.
727
        self.assertIs(None, index._nodes)
728
        # But if we *would* need to read more to resolve it, then we will
729
        # buffer all.
730
        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,
731
        self.assertIsNot(None, index._nodes)
732
2890.2.10 by Robert Collins
Add test coverage to ensure \r's are not mangled by bisection parsing.
733
    def test_iter_entries_references_resolved(self):
734
        index = self.make_index(1, nodes=[
735
            (('name', ), 'data', ([('ref', ), ('ref', )], )),
736
            (('ref', ), 'refdata', ([], ))])
737
        self.assertEqual(set([(index, ('name', ), 'data', ((('ref',),('ref',)),)),
738
            (index, ('ref', ), 'refdata', ((), ))]),
739
            set(index.iter_entries([('name',), ('ref',)])))
740
741
    def test_iter_entries_references_2_refs_resolved(self):
742
        index = self.make_index(2, nodes=[
743
            (('name', ), 'data', ([('ref', )], [('ref', )])),
744
            (('ref', ), 'refdata', ([], []))])
745
        self.assertEqual(set([(index, ('name', ), 'data', ((('ref',),), (('ref',),))),
746
            (index, ('ref', ), 'refdata', ((), ()))]),
747
            set(index.iter_entries([('name',), ('ref',)])))
748
2592.1.30 by Robert Collins
Absent entries are not yeilded.
749
    def test_iteration_absent_skipped(self):
750
        index = self.make_index(1, nodes=[
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
751
            (('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.
752
        self.assertEqual(set([(index, ('name', ), 'data', ((('ref',),),))]),
2592.1.30 by Robert Collins
Absent entries are not yeilded.
753
            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.
754
        self.assertEqual(set([(index, ('name', ), 'data', ((('ref',),),))]),
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
755
            set(index.iter_entries([('name', )])))
756
        self.assertEqual([], list(index.iter_entries([('ref', )])))
2592.1.30 by Robert Collins
Absent entries are not yeilded.
757
2624.2.9 by Robert Collins
Introduce multiple component keys, which is what is needed to combine multiple knit indices into one.
758
    def test_iteration_absent_skipped_2_element_keys(self):
759
        index = self.make_index(1, key_elements=2, nodes=[
760
            (('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.
761
        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.
762
            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.
763
        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.
764
            set(index.iter_entries([('name', 'fin')])))
765
        self.assertEqual([], list(index.iter_entries([('ref', 'erence')])))
766
2592.1.31 by Robert Collins
Build a combined graph index to use multiple indices at once.
767
    def test_iter_all_keys(self):
2592.1.29 by Robert Collins
Basic iter_entries working.
768
        index = self.make_index(1, nodes=[
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
769
            (('name', ), 'data', ([('ref', )], )),
770
            (('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.
771
        self.assertEqual(set([(index, ('name', ), 'data', ((('ref',),),)),
772
            (index, ('ref', ), 'refdata', ((), ))]),
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
773
            set(index.iter_entries([('name', ), ('ref', )])))
2592.1.29 by Robert Collins
Basic iter_entries working.
774
2592.1.31 by Robert Collins
Build a combined graph index to use multiple indices at once.
775
    def test_iter_nothing_empty(self):
2592.1.9 by Robert Collins
Iterating no keys should work too.
776
        index = self.make_index()
777
        self.assertEqual([], list(index.iter_entries([])))
778
2592.1.5 by Robert Collins
Trivial index reading.
779
    def test_iter_missing_entry_empty(self):
780
        index = self.make_index()
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
781
        self.assertEqual([], list(index.iter_entries([('a', )])))
2592.1.7 by Robert Collins
A validate that goes boom.
782
2890.2.8 by Robert Collins
Make the size of the index optionally None for the pack-names index.
783
    def test_iter_missing_entry_empty_no_size(self):
784
        index = self.make_index()
785
        index = GraphIndex(index._transport, 'index', None)
786
        self.assertEqual([], list(index.iter_entries([('a', )])))
787
2624.2.9 by Robert Collins
Introduce multiple component keys, which is what is needed to combine multiple knit indices into one.
788
    def test_iter_key_prefix_1_element_key_None(self):
789
        index = self.make_index()
790
        self.assertRaises(errors.BadIndexKey, list,
791
            index.iter_entries_prefix([(None, )]))
792
793
    def test_iter_key_prefix_wrong_length(self):
794
        index = self.make_index()
795
        self.assertRaises(errors.BadIndexKey, list,
796
            index.iter_entries_prefix([('foo', None)]))
797
        index = self.make_index(key_elements=2)
798
        self.assertRaises(errors.BadIndexKey, list,
799
            index.iter_entries_prefix([('foo', )]))
800
        self.assertRaises(errors.BadIndexKey, list,
801
            index.iter_entries_prefix([('foo', None, None)]))
802
803
    def test_iter_key_prefix_1_key_element_no_refs(self):
804
        index = self.make_index( nodes=[
805
            (('name', ), 'data', ()),
806
            (('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.
807
        self.assertEqual(set([(index, ('name', ), 'data'),
808
            (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.
809
            set(index.iter_entries_prefix([('name', ), ('ref', )])))
810
811
    def test_iter_key_prefix_1_key_element_refs(self):
812
        index = self.make_index(1, nodes=[
813
            (('name', ), 'data', ([('ref', )], )),
814
            (('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.
815
        self.assertEqual(set([(index, ('name', ), 'data', ((('ref',),),)),
816
            (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.
817
            set(index.iter_entries_prefix([('name', ), ('ref', )])))
818
819
    def test_iter_key_prefix_2_key_element_no_refs(self):
820
        index = self.make_index(key_elements=2, nodes=[
821
            (('name', 'fin1'), 'data', ()),
822
            (('name', 'fin2'), 'beta', ()),
823
            (('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.
824
        self.assertEqual(set([(index, ('name', 'fin1'), 'data'),
825
            (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.
826
            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.
827
        self.assertEqual(set([(index, ('name', 'fin1'), 'data'),
828
            (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.
829
            set(index.iter_entries_prefix([('name', None)])))
830
831
    def test_iter_key_prefix_2_key_element_refs(self):
832
        index = self.make_index(1, key_elements=2, nodes=[
833
            (('name', 'fin1'), 'data', ([('ref', 'erence')], )),
834
            (('name', 'fin2'), 'beta', ([], )),
835
            (('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.
836
        self.assertEqual(set([(index, ('name', 'fin1'), 'data', ((('ref', 'erence'),),)),
837
            (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.
838
            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.
839
        self.assertEqual(set([(index, ('name', 'fin1'), 'data', ((('ref', 'erence'),),)),
840
            (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.
841
            set(index.iter_entries_prefix([('name', None)])))
842
2624.2.16 by Robert Collins
Add a key_count method to GraphIndex and friends, allowing optimisation of length calculations by the index.
843
    def test_key_count_empty(self):
844
        index = self.make_index()
845
        self.assertEqual(0, index.key_count())
846
847
    def test_key_count_one(self):
848
        index = self.make_index(nodes=[(('name', ), '', ())])
849
        self.assertEqual(1, index.key_count())
850
851
    def test_key_count_two(self):
852
        index = self.make_index(nodes=[
853
            (('name', ), '', ()), (('foo', ), '', ())])
854
        self.assertEqual(2, index.key_count())
855
3665.3.3 by John Arbash Meinel
If we read more than 50% of the whole index,
856
    def test_read_and_parse_tracks_real_read_value(self):
857
        index = self.make_index(nodes=self.make_nodes(10))
858
        del index._transport._activity[:]
859
        index._read_and_parse([(0, 200)])
860
        self.assertEqual([
861
            ('readv', 'index', [(0, 200)], True, index._size),
862
            ],
863
            index._transport._activity)
864
        # The readv expansion code will expand the initial request to 4096
865
        # bytes, which is more than enough to read the entire index, and we
866
        # will track the fact that we read that many bytes.
867
        self.assertEqual(index._size, index._bytes_read)
868
869
    def test_read_and_parse_triggers_buffer_all(self):
3665.3.1 by John Arbash Meinel
Updates to GraphIndex processing.
870
        index = self.make_index(key_elements=2, nodes=[
871
            (('name', 'fin1'), 'data', ()),
872
            (('name', 'fin2'), 'beta', ()),
873
            (('ref', 'erence'), 'refdata', ())])
874
        self.assertTrue(index._size > 0)
875
        self.assertIs(None, index._nodes)
876
        index._read_and_parse([(0, index._size)])
877
        self.assertIsNot(None, index._nodes)
878
2592.1.7 by Robert Collins
A validate that goes boom.
879
    def test_validate_bad_index_errors(self):
880
        trans = self.get_transport()
881
        trans.put_bytes('name', "not an index\n")
2890.2.1 by Robert Collins
* ``bzrlib.index.GraphIndex`` now requires a size parameter to the
882
        index = GraphIndex(trans, 'name', 13)
2592.1.7 by Robert Collins
A validate that goes boom.
883
        self.assertRaises(errors.BadIndexFormatSignature, index.validate)
2592.1.8 by Robert Collins
Empty files should validate ok.
884
2592.1.10 by Robert Collins
Make validate detect node reference parsing errors.
885
    def test_validate_bad_node_refs(self):
886
        index = self.make_index(2)
887
        trans = self.get_transport()
888
        content = trans.get_bytes('index')
889
        # change the options line to end with a rather than a parseable number
890
        new_content = content[:-2] + 'a\n\n'
891
        trans.put_bytes('index', new_content)
892
        self.assertRaises(errors.BadIndexOptions, index.validate)
893
2592.1.27 by Robert Collins
Test missing end lines with non-empty indices.
894
    def test_validate_missing_end_line_empty(self):
2592.1.11 by Robert Collins
Detect truncated indices.
895
        index = self.make_index(2)
896
        trans = self.get_transport()
897
        content = trans.get_bytes('index')
898
        # truncate the last byte
899
        trans.put_bytes('index', content[:-1])
900
        self.assertRaises(errors.BadIndexData, index.validate)
901
2592.1.27 by Robert Collins
Test missing end lines with non-empty indices.
902
    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.
903
        index = self.make_index(2, nodes=[(('key', ), '', ([], []))])
2592.1.27 by Robert Collins
Test missing end lines with non-empty indices.
904
        trans = self.get_transport()
905
        content = trans.get_bytes('index')
906
        # truncate the last byte
907
        trans.put_bytes('index', content[:-1])
908
        self.assertRaises(errors.BadIndexData, index.validate)
909
2592.1.8 by Robert Collins
Empty files should validate ok.
910
    def test_validate_empty(self):
911
        index = self.make_index()
912
        index.validate()
2592.1.12 by Robert Collins
Handle basic node adds.
913
914
    def test_validate_no_refs_content(self):
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
915
        index = self.make_index(nodes=[(('key', ), 'value', ())])
2592.1.12 by Robert Collins
Handle basic node adds.
916
        index.validate()
2592.1.31 by Robert Collins
Build a combined graph index to use multiple indices at once.
917
918
919
class TestCombinedGraphIndex(TestCaseWithMemoryTransport):
920
2624.2.9 by Robert Collins
Introduce multiple component keys, which is what is needed to combine multiple knit indices into one.
921
    def make_index(self, name, ref_lists=0, key_elements=1, nodes=[]):
922
        builder = GraphIndexBuilder(ref_lists, key_elements=key_elements)
2624.2.17 by Robert Collins
Review feedback.
923
        for key, value, references in nodes:
924
            builder.add_node(key, value, references)
2592.1.31 by Robert Collins
Build a combined graph index to use multiple indices at once.
925
        stream = builder.finish()
926
        trans = self.get_transport()
2890.2.1 by Robert Collins
* ``bzrlib.index.GraphIndex`` now requires a size parameter to the
927
        size = trans.put_file(name, stream)
928
        return GraphIndex(trans, name, size)
2592.1.31 by Robert Collins
Build a combined graph index to use multiple indices at once.
929
930
    def test_open_missing_index_no_error(self):
931
        trans = self.get_transport()
2890.2.1 by Robert Collins
* ``bzrlib.index.GraphIndex`` now requires a size parameter to the
932
        index1 = GraphIndex(trans, 'missing', 100)
2592.1.31 by Robert Collins
Build a combined graph index to use multiple indices at once.
933
        index = CombinedGraphIndex([index1])
934
2592.1.37 by Robert Collins
Add CombinedGraphIndex.insert_index.
935
    def test_add_index(self):
936
        index = CombinedGraphIndex([])
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
937
        index1 = self.make_index('name', 0, nodes=[(('key', ), '', ())])
2592.1.37 by Robert Collins
Add CombinedGraphIndex.insert_index.
938
        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.
939
        self.assertEqual([(index1, ('key', ), '')], list(index.iter_all_entries()))
2592.1.37 by Robert Collins
Add CombinedGraphIndex.insert_index.
940
2592.1.31 by Robert Collins
Build a combined graph index to use multiple indices at once.
941
    def test_iter_all_entries_empty(self):
942
        index = CombinedGraphIndex([])
943
        self.assertEqual([], list(index.iter_all_entries()))
944
945
    def test_iter_all_entries_children_empty(self):
946
        index1 = self.make_index('name')
947
        index = CombinedGraphIndex([index1])
948
        self.assertEqual([], list(index.iter_all_entries()))
949
950
    def test_iter_all_entries_simple(self):
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
951
        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.
952
        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.
953
        self.assertEqual([(index1, ('name', ), 'data')],
2592.1.31 by Robert Collins
Build a combined graph index to use multiple indices at once.
954
            list(index.iter_all_entries()))
955
956
    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.
957
        index1 = self.make_index('name1', nodes=[(('name', ), 'data', ())])
958
        index2 = self.make_index('name2', nodes=[(('2', ), '', ())])
2592.1.31 by Robert Collins
Build a combined graph index to use multiple indices at once.
959
        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.
960
        self.assertEqual([(index1, ('name', ), 'data'),
961
            (index2, ('2', ), '')],
2592.1.31 by Robert Collins
Build a combined graph index to use multiple indices at once.
962
            list(index.iter_all_entries()))
963
2592.1.39 by Robert Collins
CombinedGraphIndex.iter_entries does not need to see all entries.
964
    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.
965
        index1 = self.make_index('name1', nodes=[(('name', ), 'data', ())])
966
        index2 = self.make_index('name2', nodes=[(('name', ), 'data', ())])
2592.1.39 by Robert Collins
CombinedGraphIndex.iter_entries does not need to see all entries.
967
        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.
968
        self.assertEqual([(index1, ('name', ), 'data')],
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
969
            list(index.iter_entries([('name', )])))
2592.1.39 by Robert Collins
CombinedGraphIndex.iter_entries does not need to see all entries.
970
2592.1.31 by Robert Collins
Build a combined graph index to use multiple indices at once.
971
    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.
972
        index1 = self.make_index('name1', nodes=[(('name', ), 'data', ())])
973
        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.
974
        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.
975
        self.assertEqual([(index1, ('name', ), 'data')],
2592.1.31 by Robert Collins
Build a combined graph index to use multiple indices at once.
976
            list(index.iter_all_entries()))
977
2624.2.9 by Robert Collins
Introduce multiple component keys, which is what is needed to combine multiple knit indices into one.
978
    def test_iter_key_prefix_2_key_element_refs(self):
979
        index1 = self.make_index('1', 1, key_elements=2, nodes=[
980
            (('name', 'fin1'), 'data', ([('ref', 'erence')], ))])
981
        index2 = self.make_index('2', 1, key_elements=2, nodes=[
982
            (('name', 'fin2'), 'beta', ([], )),
983
            (('ref', 'erence'), 'refdata', ([], ))])
984
        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.
985
        self.assertEqual(set([(index1, ('name', 'fin1'), 'data', ((('ref', 'erence'),),)),
986
            (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.
987
            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.
988
        self.assertEqual(set([(index1, ('name', 'fin1'), 'data', ((('ref', 'erence'),),)),
989
            (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.
990
            set(index.iter_entries_prefix([('name', None)])))
991
2592.1.31 by Robert Collins
Build a combined graph index to use multiple indices at once.
992
    def test_iter_nothing_empty(self):
993
        index = CombinedGraphIndex([])
994
        self.assertEqual([], list(index.iter_entries([])))
995
996
    def test_iter_nothing_children_empty(self):
997
        index1 = self.make_index('name')
998
        index = CombinedGraphIndex([index1])
999
        self.assertEqual([], list(index.iter_entries([])))
1000
1001
    def test_iter_all_keys(self):
1002
        index1 = self.make_index('1', 1, nodes=[
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1003
            (('name', ), 'data', ([('ref', )], ))])
2592.1.31 by Robert Collins
Build a combined graph index to use multiple indices at once.
1004
        index2 = self.make_index('2', 1, nodes=[
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1005
            (('ref', ), 'refdata', ((), ))])
2592.1.31 by Robert Collins
Build a combined graph index to use multiple indices at once.
1006
        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.
1007
        self.assertEqual(set([(index1, ('name', ), 'data', ((('ref', ), ), )),
1008
            (index2, ('ref', ), 'refdata', ((), ))]),
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1009
            set(index.iter_entries([('name', ), ('ref', )])))
2592.1.31 by Robert Collins
Build a combined graph index to use multiple indices at once.
1010
 
1011
    def test_iter_all_keys_dup_entry(self):
1012
        index1 = self.make_index('1', 1, nodes=[
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1013
            (('name', ), 'data', ([('ref', )], )),
1014
            (('ref', ), 'refdata', ([], ))])
2592.1.31 by Robert Collins
Build a combined graph index to use multiple indices at once.
1015
        index2 = self.make_index('2', 1, nodes=[
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1016
            (('ref', ), 'refdata', ([], ))])
2592.1.31 by Robert Collins
Build a combined graph index to use multiple indices at once.
1017
        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.
1018
        self.assertEqual(set([(index1, ('name', ), 'data', ((('ref',),),)),
1019
            (index1, ('ref', ), 'refdata', ((), ))]),
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1020
            set(index.iter_entries([('name', ), ('ref', )])))
2592.1.31 by Robert Collins
Build a combined graph index to use multiple indices at once.
1021
 
1022
    def test_iter_missing_entry_empty(self):
1023
        index = CombinedGraphIndex([])
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1024
        self.assertEqual([], list(index.iter_entries([('a', )])))
2592.1.31 by Robert Collins
Build a combined graph index to use multiple indices at once.
1025
1026
    def test_iter_missing_entry_one_index(self):
1027
        index1 = self.make_index('1')
1028
        index = CombinedGraphIndex([index1])
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1029
        self.assertEqual([], list(index.iter_entries([('a', )])))
2592.1.31 by Robert Collins
Build a combined graph index to use multiple indices at once.
1030
1031
    def test_iter_missing_entry_two_index(self):
1032
        index1 = self.make_index('1')
1033
        index2 = self.make_index('2')
1034
        index = CombinedGraphIndex([index1, index2])
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1035
        self.assertEqual([], list(index.iter_entries([('a', )])))
2592.1.31 by Robert Collins
Build a combined graph index to use multiple indices at once.
1036
 
1037
    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.
1038
        index1 = self.make_index('1', nodes=[(('key', ), '', ())])
2592.1.31 by Robert Collins
Build a combined graph index to use multiple indices at once.
1039
        index2 = self.make_index('2', nodes=[])
1040
        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.
1041
        self.assertEqual([(index1, ('key', ), '')],
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1042
            list(index.iter_entries([('key', )])))
2592.1.31 by Robert Collins
Build a combined graph index to use multiple indices at once.
1043
        # and in the other direction
1044
        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.
1045
        self.assertEqual([(index1, ('key', ), '')],
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1046
            list(index.iter_entries([('key', )])))
2592.1.31 by Robert Collins
Build a combined graph index to use multiple indices at once.
1047
2624.2.16 by Robert Collins
Add a key_count method to GraphIndex and friends, allowing optimisation of length calculations by the index.
1048
    def test_key_count_empty(self):
1049
        index1 = self.make_index('1', nodes=[])
1050
        index2 = self.make_index('2', nodes=[])
1051
        index = CombinedGraphIndex([index1, index2])
1052
        self.assertEqual(0, index.key_count())
1053
1054
    def test_key_count_sums_index_keys(self):
1055
        index1 = self.make_index('1', nodes=[
1056
            (('1',), '', ()),
1057
            (('2',), '', ())])
1058
        index2 = self.make_index('2', nodes=[(('1',), '', ())])
1059
        index = CombinedGraphIndex([index1, index2])
1060
        self.assertEqual(3, index.key_count())
1061
2592.1.31 by Robert Collins
Build a combined graph index to use multiple indices at once.
1062
    def test_validate_bad_child_index_errors(self):
1063
        trans = self.get_transport()
1064
        trans.put_bytes('name', "not an index\n")
2890.2.1 by Robert Collins
* ``bzrlib.index.GraphIndex`` now requires a size parameter to the
1065
        index1 = GraphIndex(trans, 'name', 13)
2592.1.31 by Robert Collins
Build a combined graph index to use multiple indices at once.
1066
        index = CombinedGraphIndex([index1])
1067
        self.assertRaises(errors.BadIndexFormatSignature, index.validate)
1068
1069
    def test_validate_empty(self):
1070
        index = CombinedGraphIndex([])
1071
        index.validate()
2592.1.38 by Robert Collins
Create an InMemoryGraphIndex for temporary indexing.
1072
1073
1074
class TestInMemoryGraphIndex(TestCaseWithMemoryTransport):
1075
2624.2.10 by Robert Collins
Also add iter_key_prefix support to InMemoryGraphIndex.
1076
    def make_index(self, ref_lists=0, key_elements=1, nodes=[]):
1077
        result = InMemoryGraphIndex(ref_lists, key_elements=key_elements)
2592.1.38 by Robert Collins
Create an InMemoryGraphIndex for temporary indexing.
1078
        result.add_nodes(nodes)
1079
        return result
1080
2624.2.1 by Robert Collins
InMemoryGraphIndex.add_nodes was inconsistent with other metods for non-node-reference indices.
1081
    def test_add_nodes_no_refs(self):
1082
        index = self.make_index(0)
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1083
        index.add_nodes([(('name', ), 'data')])
1084
        index.add_nodes([(('name2', ), ''), (('name3', ), '')])
2624.2.1 by Robert Collins
InMemoryGraphIndex.add_nodes was inconsistent with other metods for non-node-reference indices.
1085
        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.
1086
            (index, ('name', ), 'data'),
1087
            (index, ('name2', ), ''),
1088
            (index, ('name3', ), ''),
2624.2.1 by Robert Collins
InMemoryGraphIndex.add_nodes was inconsistent with other metods for non-node-reference indices.
1089
            ]), set(index.iter_all_entries()))
1090
2592.1.38 by Robert Collins
Create an InMemoryGraphIndex for temporary indexing.
1091
    def test_add_nodes(self):
1092
        index = self.make_index(1)
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1093
        index.add_nodes([(('name', ), 'data', ([],))])
1094
        index.add_nodes([(('name2', ), '', ([],)), (('name3', ), '', ([('r', )],))])
2592.1.38 by Robert Collins
Create an InMemoryGraphIndex for temporary indexing.
1095
        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.
1096
            (index, ('name', ), 'data', ((),)),
1097
            (index, ('name2', ), '', ((),)),
1098
            (index, ('name3', ), '', ((('r', ), ), )),
2592.1.38 by Robert Collins
Create an InMemoryGraphIndex for temporary indexing.
1099
            ]), set(index.iter_all_entries()))
1100
1101
    def test_iter_all_entries_empty(self):
1102
        index = self.make_index()
1103
        self.assertEqual([], list(index.iter_all_entries()))
1104
1105
    def test_iter_all_entries_simple(self):
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1106
        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.
1107
        self.assertEqual([(index, ('name', ), 'data')],
2592.1.38 by Robert Collins
Create an InMemoryGraphIndex for temporary indexing.
1108
            list(index.iter_all_entries()))
1109
1110
    def test_iter_all_entries_references(self):
1111
        index = self.make_index(1, nodes=[
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1112
            (('name', ), 'data', ([('ref', )], )),
1113
            (('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.
1114
        self.assertEqual(set([(index, ('name', ), 'data', ((('ref', ),),)),
1115
            (index, ('ref', ), 'refdata', ((), ))]),
2592.1.38 by Robert Collins
Create an InMemoryGraphIndex for temporary indexing.
1116
            set(index.iter_all_entries()))
1117
1118
    def test_iteration_absent_skipped(self):
1119
        index = self.make_index(1, nodes=[
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1120
            (('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.
1121
        self.assertEqual(set([(index, ('name', ), 'data', ((('ref',),),))]),
2592.1.38 by Robert Collins
Create an InMemoryGraphIndex for temporary indexing.
1122
            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.
1123
        self.assertEqual(set([(index, ('name', ), 'data', ((('ref',),),))]),
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1124
            set(index.iter_entries([('name', )])))
1125
        self.assertEqual([], list(index.iter_entries([('ref', )])))
2592.1.38 by Robert Collins
Create an InMemoryGraphIndex for temporary indexing.
1126
1127
    def test_iter_all_keys(self):
1128
        index = self.make_index(1, nodes=[
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1129
            (('name', ), 'data', ([('ref', )], )),
1130
            (('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.
1131
        self.assertEqual(set([(index, ('name', ), 'data', ((('ref',),),)),
1132
            (index, ('ref', ), 'refdata', ((), ))]),
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1133
            set(index.iter_entries([('name', ), ('ref', )])))
2592.1.38 by Robert Collins
Create an InMemoryGraphIndex for temporary indexing.
1134
2624.2.10 by Robert Collins
Also add iter_key_prefix support to InMemoryGraphIndex.
1135
    def test_iter_key_prefix_1_key_element_no_refs(self):
1136
        index = self.make_index( nodes=[
1137
            (('name', ), 'data'),
1138
            (('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.
1139
        self.assertEqual(set([(index, ('name', ), 'data'),
1140
            (index, ('ref', ), 'refdata')]),
2624.2.10 by Robert Collins
Also add iter_key_prefix support to InMemoryGraphIndex.
1141
            set(index.iter_entries_prefix([('name', ), ('ref', )])))
1142
1143
    def test_iter_key_prefix_1_key_element_refs(self):
1144
        index = self.make_index(1, nodes=[
1145
            (('name', ), 'data', ([('ref', )], )),
1146
            (('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.
1147
        self.assertEqual(set([(index, ('name', ), 'data', ((('ref',),),)),
1148
            (index, ('ref', ), 'refdata', ((), ))]),
2624.2.10 by Robert Collins
Also add iter_key_prefix support to InMemoryGraphIndex.
1149
            set(index.iter_entries_prefix([('name', ), ('ref', )])))
1150
1151
    def test_iter_key_prefix_2_key_element_no_refs(self):
1152
        index = self.make_index(key_elements=2, nodes=[
1153
            (('name', 'fin1'), 'data'),
1154
            (('name', 'fin2'), 'beta'),
1155
            (('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.
1156
        self.assertEqual(set([(index, ('name', 'fin1'), 'data'),
1157
            (index, ('ref', 'erence'), 'refdata')]),
2624.2.10 by Robert Collins
Also add iter_key_prefix support to InMemoryGraphIndex.
1158
            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.
1159
        self.assertEqual(set([(index, ('name', 'fin1'), 'data'),
1160
            (index, ('name', 'fin2'), 'beta')]),
2624.2.10 by Robert Collins
Also add iter_key_prefix support to InMemoryGraphIndex.
1161
            set(index.iter_entries_prefix([('name', None)])))
1162
1163
    def test_iter_key_prefix_2_key_element_refs(self):
1164
        index = self.make_index(1, key_elements=2, nodes=[
1165
            (('name', 'fin1'), 'data', ([('ref', 'erence')], )),
1166
            (('name', 'fin2'), 'beta', ([], )),
1167
            (('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.
1168
        self.assertEqual(set([(index, ('name', 'fin1'), 'data', ((('ref', 'erence'),),)),
1169
            (index, ('ref', 'erence'), 'refdata', ((), ))]),
2624.2.10 by Robert Collins
Also add iter_key_prefix support to InMemoryGraphIndex.
1170
            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.
1171
        self.assertEqual(set([(index, ('name', 'fin1'), 'data', ((('ref', 'erence'),),)),
1172
            (index, ('name', 'fin2'), 'beta', ((), ))]),
2624.2.10 by Robert Collins
Also add iter_key_prefix support to InMemoryGraphIndex.
1173
            set(index.iter_entries_prefix([('name', None)])))
1174
2592.1.38 by Robert Collins
Create an InMemoryGraphIndex for temporary indexing.
1175
    def test_iter_nothing_empty(self):
1176
        index = self.make_index()
1177
        self.assertEqual([], list(index.iter_entries([])))
1178
1179
    def test_iter_missing_entry_empty(self):
1180
        index = self.make_index()
1181
        self.assertEqual([], list(index.iter_entries(['a'])))
1182
2624.2.16 by Robert Collins
Add a key_count method to GraphIndex and friends, allowing optimisation of length calculations by the index.
1183
    def test_key_count_empty(self):
1184
        index = self.make_index()
1185
        self.assertEqual(0, index.key_count())
1186
1187
    def test_key_count_one(self):
1188
        index = self.make_index(nodes=[(('name', ), '')])
1189
        self.assertEqual(1, index.key_count())
1190
1191
    def test_key_count_two(self):
1192
        index = self.make_index(nodes=[(('name', ), ''), (('foo', ), '')])
1193
        self.assertEqual(2, index.key_count())
1194
2592.1.38 by Robert Collins
Create an InMemoryGraphIndex for temporary indexing.
1195
    def test_validate_empty(self):
1196
        index = self.make_index()
1197
        index.validate()
1198
1199
    def test_validate_no_refs_content(self):
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1200
        index = self.make_index(nodes=[(('key', ), 'value')])
2592.1.38 by Robert Collins
Create an InMemoryGraphIndex for temporary indexing.
1201
        index.validate()
1202
1203
2624.2.12 by Robert Collins
Create an adapter between indices with differing key lengths.
1204
class TestGraphIndexPrefixAdapter(TestCaseWithMemoryTransport):
1205
2624.2.13 by Robert Collins
Implement add_node/add_nodes to the GraphIndexPrefixAdapter.
1206
    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.
1207
        result = InMemoryGraphIndex(ref_lists, key_elements=key_elements)
1208
        result.add_nodes(nodes)
2624.2.13 by Robert Collins
Implement add_node/add_nodes to the GraphIndexPrefixAdapter.
1209
        if add_callback:
2624.2.17 by Robert Collins
Review feedback.
1210
            add_nodes_callback = result.add_nodes
2624.2.13 by Robert Collins
Implement add_node/add_nodes to the GraphIndexPrefixAdapter.
1211
        else:
2624.2.17 by Robert Collins
Review feedback.
1212
            add_nodes_callback = None
2624.2.13 by Robert Collins
Implement add_node/add_nodes to the GraphIndexPrefixAdapter.
1213
        adapter = GraphIndexPrefixAdapter(result, ('prefix', ), key_elements - 1,
1214
            add_nodes_callback=add_nodes_callback)
2624.2.12 by Robert Collins
Create an adapter between indices with differing key lengths.
1215
        return result, adapter
1216
2624.2.13 by Robert Collins
Implement add_node/add_nodes to the GraphIndexPrefixAdapter.
1217
    def test_add_node(self):
1218
        index, adapter = self.make_index(add_callback=True)
1219
        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.
1220
        self.assertEqual(set([(index, ('prefix', 'key'), 'value', ((('prefix', 'ref'),),))]),
2624.2.13 by Robert Collins
Implement add_node/add_nodes to the GraphIndexPrefixAdapter.
1221
            set(index.iter_all_entries()))
1222
1223
    def test_add_nodes(self):
1224
        index, adapter = self.make_index(add_callback=True)
1225
        adapter.add_nodes((
1226
            (('key',), 'value', ((('ref',),),)),
1227
            (('key2',), 'value2', ((),)),
1228
            ))
1229
        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.
1230
            (index, ('prefix', 'key2'), 'value2', ((),)),
1231
            (index, ('prefix', 'key'), 'value', ((('prefix', 'ref'),),))
2624.2.13 by Robert Collins
Implement add_node/add_nodes to the GraphIndexPrefixAdapter.
1232
            ]),
1233
            set(index.iter_all_entries()))
1234
2624.2.12 by Robert Collins
Create an adapter between indices with differing key lengths.
1235
    def test_construct(self):
1236
        index = InMemoryGraphIndex()
1237
        adapter = GraphIndexPrefixAdapter(index, ('prefix', ), 1)
1238
1239
    def test_construct_with_callback(self):
1240
        index = InMemoryGraphIndex()
1241
        adapter = GraphIndexPrefixAdapter(index, ('prefix', ), 1, index.add_nodes)
1242
1243
    def test_iter_all_entries_cross_prefix_map_errors(self):
1244
        index, adapter = self.make_index(nodes=[
1245
            (('prefix', 'key1'), 'data1', ((('prefixaltered', 'key2'),),))])
1246
        self.assertRaises(errors.BadIndexData, list, adapter.iter_all_entries())
1247
1248
    def test_iter_all_entries(self):
1249
        index, adapter = self.make_index(nodes=[
1250
            (('notprefix', 'key1'), 'data', ((), )),
1251
            (('prefix', 'key1'), 'data1', ((), )),
1252
            (('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.
1253
        self.assertEqual(set([(index, ('key1', ), 'data1', ((),)),
1254
            (index, ('key2', ), 'data2', ((('key1',),),))]),
2624.2.12 by Robert Collins
Create an adapter between indices with differing key lengths.
1255
            set(adapter.iter_all_entries()))
1256
1257
    def test_iter_entries(self):
1258
        index, adapter = self.make_index(nodes=[
1259
            (('notprefix', 'key1'), 'data', ((), )),
1260
            (('prefix', 'key1'), 'data1', ((), )),
1261
            (('prefix', 'key2'), 'data2', ((('prefix', 'key1'),),))])
1262
        # 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.
1263
        self.assertEqual(set([(index, ('key1', ), 'data1', ((),)),
1264
            (index, ('key2', ), 'data2', ((('key1', ),),))]),
2624.2.12 by Robert Collins
Create an adapter between indices with differing key lengths.
1265
            set(adapter.iter_entries([('key1', ), ('key2', )])))
1266
        # 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.
1267
        self.assertEqual(set([(index, ('key1', ), 'data1', ((),))]),
2624.2.12 by Robert Collins
Create an adapter between indices with differing key lengths.
1268
            set(adapter.iter_entries([('key1', )])))
1269
        # ask for missing, get none
1270
        self.assertEqual(set(),
1271
            set(adapter.iter_entries([('key3', )])))
1272
1273
    def test_iter_entries_prefix(self):
1274
        index, adapter = self.make_index(key_elements=3, nodes=[
1275
            (('notprefix', 'foo', 'key1'), 'data', ((), )),
1276
            (('prefix', 'prefix2', 'key1'), 'data1', ((), )),
1277
            (('prefix', 'prefix2', 'key2'), 'data2', ((('prefix', 'prefix2', 'key1'),),))])
1278
        # 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.
1279
        self.assertEqual(set([(index, ('prefix2', 'key1', ), 'data1', ((),)),
1280
            (index, ('prefix2', 'key2', ), 'data2', ((('prefix2', 'key1', ),),))]),
2624.2.12 by Robert Collins
Create an adapter between indices with differing key lengths.
1281
            set(adapter.iter_entries_prefix([('prefix2', None)])))
1282
2624.2.16 by Robert Collins
Add a key_count method to GraphIndex and friends, allowing optimisation of length calculations by the index.
1283
    def test_key_count_no_matching_keys(self):
1284
        index, adapter = self.make_index(nodes=[
1285
            (('notprefix', 'key1'), 'data', ((), ))])
1286
        self.assertEqual(0, adapter.key_count())
1287
1288
    def test_key_count_some_keys(self):
1289
        index, adapter = self.make_index(nodes=[
1290
            (('notprefix', 'key1'), 'data', ((), )),
1291
            (('prefix', 'key1'), 'data1', ((), )),
1292
            (('prefix', 'key2'), 'data2', ((('prefix', 'key1'),),))])
1293
        self.assertEqual(2, adapter.key_count())
1294
2624.2.12 by Robert Collins
Create an adapter between indices with differing key lengths.
1295
    def test_validate(self):
1296
        index, adapter = self.make_index()
1297
        calls = []
1298
        def validate():
1299
            calls.append('called')
1300
        index.validate = validate
1301
        adapter.validate()
1302
        self.assertEqual(['called'], calls)