/brz/remove-bazaar

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