/brz/remove-bazaar

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