~bzr-pqm/bzr/bzr.dev

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