~bzr-pqm/bzr/bzr.dev

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