~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_index.py

  • Committer: Robert Collins
  • Date: 2007-08-06 23:49:18 UTC
  • mto: (2592.3.81 repository)
  • mto: This revision was merged to the branch mainline in revision 2933.
  • Revision ID: robertc@robertcollins.net-20070806234918-xc9w5f86tgjphf9u
Prevent the duplicate additions of names to FileNames collections.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2007, 2009 Canonical Ltd
 
1
# Copyright (C) 2007 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
17
"""Tests for indices."""
18
18
 
19
19
from bzrlib import errors
20
20
from bzrlib.index import *
21
21
from bzrlib.tests import TestCaseWithMemoryTransport
22
 
from bzrlib.transport import get_transport
23
22
 
24
23
 
25
24
class TestGraphIndexBuilder(TestCaseWithMemoryTransport):
28
27
        builder = GraphIndexBuilder()
29
28
        stream = builder.finish()
30
29
        contents = stream.read()
31
 
        self.assertEqual(
32
 
            "Bazaar Graph Index 1\nnode_ref_lists=0\nkey_elements=1\nlen=0\n\n",
33
 
            contents)
 
30
        self.assertEqual("Bazaar Graph Index 1\nnode_ref_lists=0\nkey_elements=1\n\n", contents)
34
31
 
35
32
    def test_build_index_empty_two_element_keys(self):
36
33
        builder = GraphIndexBuilder(key_elements=2)
37
34
        stream = builder.finish()
38
35
        contents = stream.read()
39
 
        self.assertEqual(
40
 
            "Bazaar Graph Index 1\nnode_ref_lists=0\nkey_elements=2\nlen=0\n\n",
41
 
            contents)
 
36
        self.assertEqual("Bazaar Graph Index 1\nnode_ref_lists=0\nkey_elements=2\n\n", contents)
42
37
 
43
38
    def test_build_index_one_reference_list_empty(self):
44
39
        builder = GraphIndexBuilder(reference_lists=1)
45
40
        stream = builder.finish()
46
41
        contents = stream.read()
47
 
        self.assertEqual(
48
 
            "Bazaar Graph Index 1\nnode_ref_lists=1\nkey_elements=1\nlen=0\n\n",
49
 
            contents)
 
42
        self.assertEqual("Bazaar Graph Index 1\nnode_ref_lists=1\nkey_elements=1\n\n", contents)
50
43
 
51
44
    def test_build_index_two_reference_list_empty(self):
52
45
        builder = GraphIndexBuilder(reference_lists=2)
53
46
        stream = builder.finish()
54
47
        contents = stream.read()
55
 
        self.assertEqual(
56
 
            "Bazaar Graph Index 1\nnode_ref_lists=2\nkey_elements=1\nlen=0\n\n",
57
 
            contents)
 
48
        self.assertEqual("Bazaar Graph Index 1\nnode_ref_lists=2\nkey_elements=1\n\n", contents)
58
49
 
59
50
    def test_build_index_one_node_no_refs(self):
60
51
        builder = GraphIndexBuilder()
61
52
        builder.add_node(('akey', ), 'data')
62
53
        stream = builder.finish()
63
54
        contents = stream.read()
64
 
        self.assertEqual(
65
 
            "Bazaar Graph Index 1\nnode_ref_lists=0\nkey_elements=1\nlen=1\n"
 
55
        self.assertEqual("Bazaar Graph Index 1\nnode_ref_lists=0\nkey_elements=1\n"
66
56
            "akey\x00\x00\x00data\n\n", contents)
67
57
 
68
58
    def test_build_index_one_node_no_refs_accepts_empty_reflist(self):
70
60
        builder.add_node(('akey', ), 'data', ())
71
61
        stream = builder.finish()
72
62
        contents = stream.read()
73
 
        self.assertEqual(
74
 
            "Bazaar Graph Index 1\nnode_ref_lists=0\nkey_elements=1\nlen=1\n"
 
63
        self.assertEqual("Bazaar Graph Index 1\nnode_ref_lists=0\nkey_elements=1\n"
75
64
            "akey\x00\x00\x00data\n\n", contents)
76
65
 
77
66
    def test_build_index_one_node_2_element_keys(self):
82
71
        builder.add_node(('akey', 'secondpart'), 'data')
83
72
        stream = builder.finish()
84
73
        contents = stream.read()
85
 
        self.assertEqual(
86
 
            "Bazaar Graph Index 1\nnode_ref_lists=0\nkey_elements=2\nlen=1\n"
 
74
        self.assertEqual("Bazaar Graph Index 1\nnode_ref_lists=0\nkey_elements=2\n"
87
75
            "akey\x00secondpart\x00\x00\x00data\n\n", contents)
88
76
 
89
77
    def test_add_node_empty_value(self):
91
79
        builder.add_node(('akey', ), '')
92
80
        stream = builder.finish()
93
81
        contents = stream.read()
94
 
        self.assertEqual(
95
 
            "Bazaar Graph Index 1\nnode_ref_lists=0\nkey_elements=1\nlen=1\n"
 
82
        self.assertEqual("Bazaar Graph Index 1\nnode_ref_lists=0\nkey_elements=1\n"
96
83
            "akey\x00\x00\x00\n\n", contents)
97
84
 
98
85
    def test_build_index_nodes_sorted(self):
106
93
        builder.add_node(('2001', ), 'data')
107
94
        stream = builder.finish()
108
95
        contents = stream.read()
109
 
        self.assertEqual(
110
 
            "Bazaar Graph Index 1\nnode_ref_lists=0\nkey_elements=1\nlen=3\n"
 
96
        self.assertEqual("Bazaar Graph Index 1\nnode_ref_lists=0\nkey_elements=1\n"
111
97
            "2000\x00\x00\x00data\n"
112
98
            "2001\x00\x00\x00data\n"
113
99
            "2002\x00\x00\x00data\n"
130
116
        builder.add_node(('2001', '2001'), 'data')
131
117
        stream = builder.finish()
132
118
        contents = stream.read()
133
 
        self.assertEqual(
134
 
            "Bazaar Graph Index 1\nnode_ref_lists=0\nkey_elements=2\nlen=9\n"
 
119
        self.assertEqual("Bazaar Graph Index 1\nnode_ref_lists=0\nkey_elements=2\n"
135
120
            "2000\x002000\x00\x00\x00data\n"
136
121
            "2000\x002001\x00\x00\x00data\n"
137
122
            "2000\x002002\x00\x00\x00data\n"
148
133
        builder.add_node(('key', ), 'data', ([], ))
149
134
        stream = builder.finish()
150
135
        contents = stream.read()
151
 
        self.assertEqual(
152
 
            "Bazaar Graph Index 1\nnode_ref_lists=1\nkey_elements=1\nlen=1\n"
 
136
        self.assertEqual("Bazaar Graph Index 1\nnode_ref_lists=1\nkey_elements=1\n"
153
137
            "key\x00\x00\x00data\n"
154
138
            "\n", contents)
155
139
 
158
142
        builder.add_node(('key', 'key2'), 'data', ([], ))
159
143
        stream = builder.finish()
160
144
        contents = stream.read()
161
 
        self.assertEqual(
162
 
            "Bazaar Graph Index 1\nnode_ref_lists=1\nkey_elements=2\nlen=1\n"
 
145
        self.assertEqual("Bazaar Graph Index 1\nnode_ref_lists=1\nkey_elements=2\n"
163
146
            "key\x00key2\x00\x00\x00data\n"
164
147
            "\n", contents)
165
148
 
168
151
        builder.add_node(('key', ), 'data', ([], []))
169
152
        stream = builder.finish()
170
153
        contents = stream.read()
171
 
        self.assertEqual(
172
 
            "Bazaar Graph Index 1\nnode_ref_lists=2\nkey_elements=1\nlen=1\n"
 
154
        self.assertEqual("Bazaar Graph Index 1\nnode_ref_lists=2\nkey_elements=1\n"
173
155
            "key\x00\x00\t\x00data\n"
174
156
            "\n", contents)
175
157
 
176
 
    def test_clear_cache(self):
177
 
        builder = GraphIndexBuilder(reference_lists=2)
178
 
        # This is a no-op, but the api should exist
179
 
        builder.clear_cache()
180
 
 
181
158
    def test_node_references_are_byte_offsets(self):
182
159
        builder = GraphIndexBuilder(reference_lists=1)
183
160
        builder.add_node(('reference', ), 'data', ([], ))
184
161
        builder.add_node(('key', ), 'data', ([('reference', )], ))
185
162
        stream = builder.finish()
186
163
        contents = stream.read()
187
 
        self.assertEqual(
188
 
            "Bazaar Graph Index 1\nnode_ref_lists=1\nkey_elements=1\nlen=2\n"
189
 
            "key\x00\x0072\x00data\n"
 
164
        self.assertEqual("Bazaar Graph Index 1\nnode_ref_lists=1\nkey_elements=1\n"
 
165
            "key\x00\x0066\x00data\n"
190
166
            "reference\x00\x00\x00data\n"
191
167
            "\n", contents)
192
168
 
197
173
        builder.add_node(('key', ), 'data', ([('reference', ), ('reference2', )], ))
198
174
        stream = builder.finish()
199
175
        contents = stream.read()
200
 
        self.assertEqual(
201
 
            "Bazaar Graph Index 1\nnode_ref_lists=1\nkey_elements=1\nlen=3\n"
202
 
            "key\x00\x00077\r094\x00data\n"
 
176
        self.assertEqual("Bazaar Graph Index 1\nnode_ref_lists=1\nkey_elements=1\n"
 
177
            "key\x00\x00071\r088\x00data\n"
203
178
            "reference\x00\x00\x00data\n"
204
179
            "reference2\x00\x00\x00data\n"
205
180
            "\n", contents)
210
185
        builder.add_node(('rey', ), 'data', ([('keference', )], [('keference', )]))
211
186
        stream = builder.finish()
212
187
        contents = stream.read()
213
 
        self.assertEqual(
214
 
            "Bazaar Graph Index 1\nnode_ref_lists=2\nkey_elements=1\nlen=2\n"
 
188
        self.assertEqual("Bazaar Graph Index 1\nnode_ref_lists=2\nkey_elements=1\n"
215
189
            "keference\x00\x00\t\x00data\n"
216
 
            "rey\x00\x0059\t59\x00data\n"
 
190
            "rey\x00\x0053\t53\x00data\n"
217
191
            "\n", contents)
218
192
 
219
193
    def test_add_node_referencing_missing_key_makes_absent(self):
221
195
        builder.add_node(('rey', ), 'data', ([('beference', ), ('aeference2', )], ))
222
196
        stream = builder.finish()
223
197
        contents = stream.read()
224
 
        self.assertEqual(
225
 
            "Bazaar Graph Index 1\nnode_ref_lists=1\nkey_elements=1\nlen=1\n"
 
198
        self.assertEqual("Bazaar Graph Index 1\nnode_ref_lists=1\nkey_elements=1\n"
226
199
            "aeference2\x00a\x00\x00\n"
227
200
            "beference\x00a\x00\x00\n"
228
 
            "rey\x00\x00074\r059\x00data\n"
 
201
            "rey\x00\x0068\r53\x00data\n"
229
202
            "\n", contents)
230
203
 
231
204
    def test_node_references_three_digits(self):
235
208
        builder.add_node(('2-key', ), '', (references, ))
236
209
        stream = builder.finish()
237
210
        contents = stream.read()
238
 
        self.assertEqualDiff(
239
 
            "Bazaar Graph Index 1\nnode_ref_lists=1\nkey_elements=1\nlen=1\n"
 
211
        self.assertEqual("Bazaar Graph Index 1\nnode_ref_lists=1\nkey_elements=1\n"
240
212
            "0\x00a\x00\x00\n"
241
213
            "1\x00a\x00\x00\n"
242
214
            "2\x00a\x00\x00\n"
243
 
            "2-key\x00\x00151\r145\r139\r133\r127\r121\r071\r065\r059\x00\n"
 
215
            "2-key\x00\x00145\r139\r133\r127\r121\r115\r065\r059\r053\x00\n"
244
216
            "3\x00a\x00\x00\n"
245
217
            "4\x00a\x00\x00\n"
246
218
            "5\x00a\x00\x00\n"
256
228
        builder.add_node(('parent', ), '', ([('aail', ), ('zther', )], []))
257
229
        stream = builder.finish()
258
230
        contents = stream.read()
259
 
        self.assertEqual(
260
 
            "Bazaar Graph Index 1\nnode_ref_lists=2\nkey_elements=1\nlen=1\n"
 
231
        self.assertEqual("Bazaar Graph Index 1\nnode_ref_lists=2\nkey_elements=1\n"
261
232
            "aail\x00a\x00\x00\n"
262
 
            "parent\x00\x0059\r84\t\x00\n"
 
233
            "parent\x00\x0053\r78\t\x00\n"
263
234
            "zther\x00a\x00\x00\n"
264
235
            "\n", contents)
265
236
 
355
326
        builder.add_node(('k', 'ey'), 'data', ([('reference', 'tokey')], ))
356
327
        builder.add_node(('reference', 'tokey'), 'data', ([],))
357
328
 
358
 
    def test_set_optimize(self):
359
 
        builder = GraphIndexBuilder(reference_lists=1, key_elements=2)
360
 
        builder.set_optimize(for_size=True)
361
 
        self.assertTrue(builder._optimize_for_size)
362
 
        builder.set_optimize(for_size=False)
363
 
        self.assertFalse(builder._optimize_for_size)
364
 
 
365
329
 
366
330
class TestGraphIndex(TestCaseWithMemoryTransport):
367
331
 
368
 
    def make_key(self, number):
369
 
        return (str(number) + 'X'*100,)
370
 
 
371
 
    def make_value(self, number):
372
 
            return str(number) + 'Y'*100
373
 
 
374
 
    def make_nodes(self, count=64):
375
 
        # generate a big enough index that we only read some of it on a typical
376
 
        # bisection lookup.
377
 
        nodes = []
378
 
        for counter in range(count):
379
 
            nodes.append((self.make_key(counter), self.make_value(counter), ()))
380
 
        return nodes
381
 
 
382
332
    def make_index(self, ref_lists=0, key_elements=1, nodes=[]):
383
333
        builder = GraphIndexBuilder(ref_lists, key_elements=key_elements)
384
 
        for key, value, references in nodes:
385
 
            builder.add_node(key, value, references)
 
334
        for node, value, references in nodes:
 
335
            builder.add_node(node, value, references)
386
336
        stream = builder.finish()
387
 
        trans = get_transport('trace+' + self.get_url())
388
 
        size = trans.put_file('index', stream)
389
 
        return GraphIndex(trans, 'index', size)
390
 
 
391
 
    def test_clear_cache(self):
392
 
        index = self.make_index()
393
 
        # For now, we just want to make sure the api is available. As this is
394
 
        # old code, we don't really worry if it *does* anything.
395
 
        index.clear_cache()
 
337
        trans = self.get_transport()
 
338
        trans.put_file('index', stream)
 
339
        return GraphIndex(trans, 'index')
396
340
 
397
341
    def test_open_bad_index_no_error(self):
398
342
        trans = self.get_transport()
399
343
        trans.put_bytes('name', "not an index\n")
400
 
        index = GraphIndex(trans, 'name', 13)
401
 
 
402
 
    def test_open_sets_parsed_map_empty(self):
403
 
        index = self.make_index()
404
 
        self.assertEqual([], index._parsed_byte_map)
405
 
        self.assertEqual([], index._parsed_key_map)
406
 
 
407
 
    def test_key_count_buffers(self):
408
 
        index = self.make_index(nodes=self.make_nodes(2))
409
 
        # reset the transport log
410
 
        del index._transport._activity[:]
411
 
        self.assertEqual(2, index.key_count())
412
 
        # We should have requested reading the header bytes
413
 
        self.assertEqual([
414
 
            ('readv', 'index', [(0, 200)], True, index._size),
415
 
            ],
416
 
            index._transport._activity)
417
 
        # And that should have been enough to trigger reading the whole index
418
 
        # with buffering
419
 
        self.assertIsNot(None, index._nodes)
420
 
 
421
 
    def test_lookup_key_via_location_buffers(self):
422
 
        index = self.make_index()
423
 
        # reset the transport log
424
 
        del index._transport._activity[:]
425
 
        # do a _lookup_keys_via_location call for the middle of the file, which
426
 
        # is what bisection uses.
427
 
        result = index._lookup_keys_via_location(
428
 
            [(index._size // 2, ('missing', ))])
429
 
        # this should have asked for a readv request, with adjust_for_latency,
430
 
        # and two regions: the header, and half-way into the file.
431
 
        self.assertEqual([
432
 
            ('readv', 'index', [(30, 30), (0, 200)], True, 60),
433
 
            ],
434
 
            index._transport._activity)
435
 
        # and the result should be that the key cannot be present, because this
436
 
        # is a trivial index.
437
 
        self.assertEqual([((index._size // 2, ('missing', )), False)],
438
 
            result)
439
 
        # And this should have caused the file to be fully buffered
440
 
        self.assertIsNot(None, index._nodes)
441
 
        self.assertEqual([], index._parsed_byte_map)
442
 
 
443
 
    def test_first_lookup_key_via_location(self):
444
 
        # We need enough data so that the _HEADER_READV doesn't consume the
445
 
        # whole file. We always read 800 bytes for every key, and the local
446
 
        # transport natural expansion is 4096 bytes. So we have to have >8192
447
 
        # bytes or we will trigger "buffer_all".
448
 
        # We also want the 'missing' key to fall within the range that *did*
449
 
        # read
450
 
        nodes = []
451
 
        index = self.make_index(nodes=self.make_nodes(64))
452
 
        # reset the transport log
453
 
        del index._transport._activity[:]
454
 
        # do a _lookup_keys_via_location call for the middle of the file, which
455
 
        # is what bisection uses.
456
 
        start_lookup = index._size // 2
457
 
        result = index._lookup_keys_via_location(
458
 
            [(start_lookup, ('40missing', ))])
459
 
        # this should have asked for a readv request, with adjust_for_latency,
460
 
        # and two regions: the header, and half-way into the file.
461
 
        self.assertEqual([
462
 
            ('readv', 'index',
463
 
             [(start_lookup, 800), (0, 200)], True, index._size),
464
 
            ],
465
 
            index._transport._activity)
466
 
        # and the result should be that the key cannot be present, because this
467
 
        # is a trivial index.
468
 
        self.assertEqual([((start_lookup, ('40missing', )), False)],
469
 
            result)
470
 
        # And this should not have caused the file to be fully buffered
471
 
        self.assertIs(None, index._nodes)
472
 
        # And the regions of the file that have been parsed should be in the
473
 
        # parsed_byte_map and the parsed_key_map
474
 
        self.assertEqual([(0, 4008), (5046, 8996)], index._parsed_byte_map)
475
 
        self.assertEqual([(None, self.make_key(26)),
476
 
                          (self.make_key(31), self.make_key(48))],
477
 
                         index._parsed_key_map)
478
 
 
479
 
    def test_parsing_non_adjacent_data_trims(self):
480
 
        index = self.make_index(nodes=self.make_nodes(64))
481
 
        result = index._lookup_keys_via_location(
482
 
            [(index._size // 2, ('40', ))])
483
 
        # and the result should be that the key cannot be present, because key is
484
 
        # in the middle of the observed data from a 4K read - the smallest transport
485
 
        # will do today with this api.
486
 
        self.assertEqual([((index._size // 2, ('40', )), False)],
487
 
            result)
488
 
        # and we should have a parse map that includes the header and the
489
 
        # region that was parsed after trimming.
490
 
        self.assertEqual([(0, 4008), (5046, 8996)], index._parsed_byte_map)
491
 
        self.assertEqual([(None, self.make_key(26)),
492
 
                          (self.make_key(31), self.make_key(48))],
493
 
            index._parsed_key_map)
494
 
 
495
 
    def test_parsing_data_handles_parsed_contained_regions(self):
496
 
        # the following patten creates a parsed region that is wholly within a
497
 
        # single result from the readv layer:
498
 
        # .... single-read (readv-minimum-size) ...
499
 
        # which then trims the start and end so the parsed size is < readv
500
 
        # miniumum.
501
 
        # then a dual lookup (or a reference lookup for that matter) which
502
 
        # abuts or overlaps the parsed region on both sides will need to
503
 
        # discard the data in the middle, but parse the end as well.
504
 
        #
505
 
        # we test this by doing a single lookup to seed the data, then
506
 
        # a lookup for two keys that are present, and adjacent -
507
 
        # we except both to be found, and the parsed byte map to include the
508
 
        # locations of both keys.
509
 
        index = self.make_index(nodes=self.make_nodes(128))
510
 
        result = index._lookup_keys_via_location(
511
 
            [(index._size // 2, ('40', ))])
512
 
        # and we should have a parse map that includes the header and the
513
 
        # region that was parsed after trimming.
514
 
        self.assertEqual([(0, 4045), (11759, 15707)], index._parsed_byte_map)
515
 
        self.assertEqual([(None, self.make_key(116)),
516
 
                          (self.make_key(35), self.make_key(51))],
517
 
            index._parsed_key_map)
518
 
        # now ask for two keys, right before and after the parsed region
519
 
        result = index._lookup_keys_via_location(
520
 
            [(11450, self.make_key(34)), (15707, self.make_key(52))])
521
 
        self.assertEqual([
522
 
            ((11450, self.make_key(34)),
523
 
             (index, self.make_key(34), self.make_value(34))),
524
 
            ((15707, self.make_key(52)),
525
 
             (index, self.make_key(52), self.make_value(52))),
526
 
            ],
527
 
            result)
528
 
        self.assertEqual([(0, 4045), (9889, 17993)], index._parsed_byte_map)
529
 
 
530
 
    def test_lookup_missing_key_answers_without_io_when_map_permits(self):
531
 
        # generate a big enough index that we only read some of it on a typical
532
 
        # bisection lookup.
533
 
        index = self.make_index(nodes=self.make_nodes(64))
534
 
        # lookup the keys in the middle of the file
535
 
        result =index._lookup_keys_via_location(
536
 
            [(index._size // 2, ('40', ))])
537
 
        # check the parse map, this determines the test validity
538
 
        self.assertEqual([(0, 4008), (5046, 8996)], index._parsed_byte_map)
539
 
        self.assertEqual([(None, self.make_key(26)),
540
 
                          (self.make_key(31), self.make_key(48))],
541
 
            index._parsed_key_map)
542
 
        # reset the transport log
543
 
        del index._transport._activity[:]
544
 
        # now looking up a key in the portion of the file already parsed should
545
 
        # not create a new transport request, and should return False (cannot
546
 
        # be in the index) - even when the byte location we ask for is outside
547
 
        # the parsed region
548
 
        result = index._lookup_keys_via_location(
549
 
            [(4000, ('40', ))])
550
 
        self.assertEqual([((4000, ('40', )), False)],
551
 
            result)
552
 
        self.assertEqual([], index._transport._activity)
553
 
 
554
 
    def test_lookup_present_key_answers_without_io_when_map_permits(self):
555
 
        # generate a big enough index that we only read some of it on a typical
556
 
        # bisection lookup.
557
 
        index = self.make_index(nodes=self.make_nodes(64))
558
 
        # lookup the keys in the middle of the file
559
 
        result =index._lookup_keys_via_location(
560
 
            [(index._size // 2, ('40', ))])
561
 
        # check the parse map, this determines the test validity
562
 
        self.assertEqual([(0, 4008), (5046, 8996)], index._parsed_byte_map)
563
 
        self.assertEqual([(None, self.make_key(26)),
564
 
                          (self.make_key(31), self.make_key(48))],
565
 
            index._parsed_key_map)
566
 
        # reset the transport log
567
 
        del index._transport._activity[:]
568
 
        # now looking up a key in the portion of the file already parsed should
569
 
        # not create a new transport request, and should return False (cannot
570
 
        # be in the index) - even when the byte location we ask for is outside
571
 
        # the parsed region
572
 
        #
573
 
        result = index._lookup_keys_via_location([(4000, self.make_key(40))])
574
 
        self.assertEqual(
575
 
            [((4000, self.make_key(40)),
576
 
              (index, self.make_key(40), self.make_value(40)))],
577
 
            result)
578
 
        self.assertEqual([], index._transport._activity)
579
 
 
580
 
    def test_lookup_key_below_probed_area(self):
581
 
        # generate a big enough index that we only read some of it on a typical
582
 
        # bisection lookup.
583
 
        index = self.make_index(nodes=self.make_nodes(64))
584
 
        # ask for the key in the middle, but a key that is located in the
585
 
        # unparsed region before the middle.
586
 
        result =index._lookup_keys_via_location(
587
 
            [(index._size // 2, ('30', ))])
588
 
        # check the parse map, this determines the test validity
589
 
        self.assertEqual([(0, 4008), (5046, 8996)], index._parsed_byte_map)
590
 
        self.assertEqual([(None, self.make_key(26)),
591
 
                          (self.make_key(31), self.make_key(48))],
592
 
            index._parsed_key_map)
593
 
        self.assertEqual([((index._size // 2, ('30', )), -1)],
594
 
            result)
595
 
 
596
 
    def test_lookup_key_above_probed_area(self):
597
 
        # generate a big enough index that we only read some of it on a typical
598
 
        # bisection lookup.
599
 
        index = self.make_index(nodes=self.make_nodes(64))
600
 
        # ask for the key in the middle, but a key that is located in the
601
 
        # unparsed region after the middle.
602
 
        result =index._lookup_keys_via_location(
603
 
            [(index._size // 2, ('50', ))])
604
 
        # check the parse map, this determines the test validity
605
 
        self.assertEqual([(0, 4008), (5046, 8996)], index._parsed_byte_map)
606
 
        self.assertEqual([(None, self.make_key(26)),
607
 
                          (self.make_key(31), self.make_key(48))],
608
 
            index._parsed_key_map)
609
 
        self.assertEqual([((index._size // 2, ('50', )), +1)],
610
 
            result)
611
 
 
612
 
    def test_lookup_key_resolves_references(self):
613
 
        # generate a big enough index that we only read some of it on a typical
614
 
        # bisection lookup.
615
 
        nodes = []
616
 
        for counter in range(99):
617
 
            nodes.append((self.make_key(counter), self.make_value(counter),
618
 
                ((self.make_key(counter + 20),),)  ))
619
 
        index = self.make_index(ref_lists=1, nodes=nodes)
620
 
        # lookup a key in the middle that does not exist, so that when we can
621
 
        # check that the referred-to-keys are not accessed automatically.
622
 
        index_size = index._size
623
 
        index_center = index_size // 2
624
 
        result = index._lookup_keys_via_location(
625
 
            [(index_center, ('40', ))])
626
 
        # check the parse map - only the start and middle should have been
627
 
        # parsed.
628
 
        self.assertEqual([(0, 4027), (10198, 14028)], index._parsed_byte_map)
629
 
        self.assertEqual([(None, self.make_key(17)),
630
 
                          (self.make_key(44), self.make_key(5))],
631
 
            index._parsed_key_map)
632
 
        # and check the transport activity likewise.
633
 
        self.assertEqual(
634
 
            [('readv', 'index', [(index_center, 800), (0, 200)], True,
635
 
                                  index_size)],
636
 
            index._transport._activity)
637
 
        # reset the transport log for testing the reference lookup
638
 
        del index._transport._activity[:]
639
 
        # now looking up a key in the portion of the file already parsed should
640
 
        # only perform IO to resolve its key references.
641
 
        result = index._lookup_keys_via_location([(11000, self.make_key(45))])
642
 
        self.assertEqual(
643
 
            [((11000, self.make_key(45)),
644
 
              (index, self.make_key(45), self.make_value(45),
645
 
               ((self.make_key(65),),)))],
646
 
            result)
647
 
        self.assertEqual([('readv', 'index', [(15093, 800)], True, index_size)],
648
 
            index._transport._activity)
649
 
 
650
 
    def test_lookup_key_can_buffer_all(self):
651
 
        nodes = []
652
 
        for counter in range(64):
653
 
            nodes.append((self.make_key(counter), self.make_value(counter),
654
 
                ((self.make_key(counter + 20),),)  ))
655
 
        index = self.make_index(ref_lists=1, nodes=nodes)
656
 
        # lookup a key in the middle that does not exist, so that when we can
657
 
        # check that the referred-to-keys are not accessed automatically.
658
 
        index_size = index._size
659
 
        index_center = index_size // 2
660
 
        result = index._lookup_keys_via_location([(index_center, ('40', ))])
661
 
        # check the parse map - only the start and middle should have been
662
 
        # parsed.
663
 
        self.assertEqual([(0, 3890), (6444, 10274)], index._parsed_byte_map)
664
 
        self.assertEqual([(None, self.make_key(25)),
665
 
                          (self.make_key(37), self.make_key(52))],
666
 
            index._parsed_key_map)
667
 
        # and check the transport activity likewise.
668
 
        self.assertEqual(
669
 
            [('readv', 'index', [(index_center, 800), (0, 200)], True,
670
 
                                  index_size)],
671
 
            index._transport._activity)
672
 
        # reset the transport log for testing the reference lookup
673
 
        del index._transport._activity[:]
674
 
        # now looking up a key in the portion of the file already parsed should
675
 
        # only perform IO to resolve its key references.
676
 
        result = index._lookup_keys_via_location([(7000, self.make_key(40))])
677
 
        self.assertEqual(
678
 
            [((7000, self.make_key(40)),
679
 
              (index, self.make_key(40), self.make_value(40),
680
 
               ((self.make_key(60),),)))],
681
 
            result)
682
 
        # Resolving the references would have required more data read, and we
683
 
        # are already above the 50% threshold, so it triggered a _buffer_all
684
 
        self.assertEqual([('get', 'index')], index._transport._activity)
 
344
        index = GraphIndex(trans, 'name')
685
345
 
686
346
    def test_iter_all_entries_empty(self):
687
347
        index = self.make_index()
689
349
 
690
350
    def test_iter_all_entries_simple(self):
691
351
        index = self.make_index(nodes=[(('name', ), 'data', ())])
692
 
        self.assertEqual([(index, ('name', ), 'data')],
 
352
        self.assertEqual([(('name', ), 'data')],
693
353
            list(index.iter_all_entries()))
694
354
 
695
355
    def test_iter_all_entries_simple_2_elements(self):
696
356
        index = self.make_index(key_elements=2,
697
357
            nodes=[(('name', 'surname'), 'data', ())])
698
 
        self.assertEqual([(index, ('name', 'surname'), 'data')],
 
358
        self.assertEqual([(('name', 'surname'), 'data')],
699
359
            list(index.iter_all_entries()))
700
360
 
701
361
    def test_iter_all_entries_references_resolved(self):
702
362
        index = self.make_index(1, nodes=[
703
363
            (('name', ), 'data', ([('ref', )], )),
704
364
            (('ref', ), 'refdata', ([], ))])
705
 
        self.assertEqual(set([(index, ('name', ), 'data', ((('ref',),),)),
706
 
            (index, ('ref', ), 'refdata', ((), ))]),
 
365
        self.assertEqual(set([(('name', ), 'data', ((('ref',),),)),
 
366
            (('ref', ), 'refdata', ((), ))]),
707
367
            set(index.iter_all_entries()))
708
368
 
709
 
    def test_iter_entries_buffers_once(self):
710
 
        index = self.make_index(nodes=self.make_nodes(2))
711
 
        # reset the transport log
712
 
        del index._transport._activity[:]
713
 
        self.assertEqual(set([(index, self.make_key(1), self.make_value(1))]),
714
 
                         set(index.iter_entries([self.make_key(1)])))
715
 
        # We should have requested reading the header bytes
716
 
        # But not needed any more than that because it would have triggered a
717
 
        # buffer all
718
 
        self.assertEqual([
719
 
            ('readv', 'index', [(0, 200)], True, index._size),
720
 
            ],
721
 
            index._transport._activity)
722
 
        # And that should have been enough to trigger reading the whole index
723
 
        # with buffering
724
 
        self.assertIsNot(None, index._nodes)
725
 
 
726
 
    def test_iter_entries_buffers_by_bytes_read(self):
727
 
        index = self.make_index(nodes=self.make_nodes(64))
728
 
        list(index.iter_entries([self.make_key(10)]))
729
 
        # The first time through isn't enough to trigger a buffer all
730
 
        self.assertIs(None, index._nodes)
731
 
        self.assertEqual(4096, index._bytes_read)
732
 
        # Grabbing a key in that same page won't trigger a buffer all, as we
733
 
        # still haven't read 50% of the file
734
 
        list(index.iter_entries([self.make_key(11)]))
735
 
        self.assertIs(None, index._nodes)
736
 
        self.assertEqual(4096, index._bytes_read)
737
 
        # We haven't read more data, so reading outside the range won't trigger
738
 
        # a buffer all right away
739
 
        list(index.iter_entries([self.make_key(40)]))
740
 
        self.assertIs(None, index._nodes)
741
 
        self.assertEqual(8192, index._bytes_read)
742
 
        # On the next pass, we will not trigger buffer all if the key is
743
 
        # available without reading more
744
 
        list(index.iter_entries([self.make_key(32)]))
745
 
        self.assertIs(None, index._nodes)
746
 
        # But if we *would* need to read more to resolve it, then we will
747
 
        # buffer all.
748
 
        list(index.iter_entries([self.make_key(60)]))
749
 
        self.assertIsNot(None, index._nodes)
750
 
 
751
 
    def test_iter_entries_references_resolved(self):
752
 
        index = self.make_index(1, nodes=[
753
 
            (('name', ), 'data', ([('ref', ), ('ref', )], )),
754
 
            (('ref', ), 'refdata', ([], ))])
755
 
        self.assertEqual(set([(index, ('name', ), 'data', ((('ref',),('ref',)),)),
756
 
            (index, ('ref', ), 'refdata', ((), ))]),
757
 
            set(index.iter_entries([('name',), ('ref',)])))
758
 
 
759
 
    def test_iter_entries_references_2_refs_resolved(self):
760
 
        index = self.make_index(2, nodes=[
761
 
            (('name', ), 'data', ([('ref', )], [('ref', )])),
762
 
            (('ref', ), 'refdata', ([], []))])
763
 
        self.assertEqual(set([(index, ('name', ), 'data', ((('ref',),), (('ref',),))),
764
 
            (index, ('ref', ), 'refdata', ((), ()))]),
765
 
            set(index.iter_entries([('name',), ('ref',)])))
766
 
 
767
369
    def test_iteration_absent_skipped(self):
768
370
        index = self.make_index(1, nodes=[
769
371
            (('name', ), 'data', ([('ref', )], ))])
770
 
        self.assertEqual(set([(index, ('name', ), 'data', ((('ref',),),))]),
 
372
        self.assertEqual(set([(('name', ), 'data', ((('ref',),),))]),
771
373
            set(index.iter_all_entries()))
772
 
        self.assertEqual(set([(index, ('name', ), 'data', ((('ref',),),))]),
 
374
        self.assertEqual(set([(('name', ), 'data', ((('ref',),),))]),
773
375
            set(index.iter_entries([('name', )])))
774
376
        self.assertEqual([], list(index.iter_entries([('ref', )])))
775
377
 
776
378
    def test_iteration_absent_skipped_2_element_keys(self):
777
379
        index = self.make_index(1, key_elements=2, nodes=[
778
380
            (('name', 'fin'), 'data', ([('ref', 'erence')], ))])
779
 
        self.assertEqual(set([(index, ('name', 'fin'), 'data', ((('ref', 'erence'),),))]),
 
381
        self.assertEqual(set([(('name', 'fin'), 'data', ((('ref', 'erence'),),))]),
780
382
            set(index.iter_all_entries()))
781
 
        self.assertEqual(set([(index, ('name', 'fin'), 'data', ((('ref', 'erence'),),))]),
 
383
        self.assertEqual(set([(('name', 'fin'), 'data', ((('ref', 'erence'),),))]),
782
384
            set(index.iter_entries([('name', 'fin')])))
783
385
        self.assertEqual([], list(index.iter_entries([('ref', 'erence')])))
784
386
 
786
388
        index = self.make_index(1, nodes=[
787
389
            (('name', ), 'data', ([('ref', )], )),
788
390
            (('ref', ), 'refdata', ([], ))])
789
 
        self.assertEqual(set([(index, ('name', ), 'data', ((('ref',),),)),
790
 
            (index, ('ref', ), 'refdata', ((), ))]),
 
391
        self.assertEqual(set([(('name', ), 'data', ((('ref',),),)),
 
392
            (('ref', ), 'refdata', ((), ))]),
791
393
            set(index.iter_entries([('name', ), ('ref', )])))
792
394
 
793
395
    def test_iter_nothing_empty(self):
798
400
        index = self.make_index()
799
401
        self.assertEqual([], list(index.iter_entries([('a', )])))
800
402
 
801
 
    def test_iter_missing_entry_empty_no_size(self):
802
 
        index = self.make_index()
803
 
        index = GraphIndex(index._transport, 'index', None)
804
 
        self.assertEqual([], list(index.iter_entries([('a', )])))
805
 
 
806
403
    def test_iter_key_prefix_1_element_key_None(self):
807
404
        index = self.make_index()
808
405
        self.assertRaises(errors.BadIndexKey, list,
822
419
        index = self.make_index( nodes=[
823
420
            (('name', ), 'data', ()),
824
421
            (('ref', ), 'refdata', ())])
825
 
        self.assertEqual(set([(index, ('name', ), 'data'),
826
 
            (index, ('ref', ), 'refdata')]),
 
422
        self.assertEqual(set([(('name', ), 'data'),
 
423
            (('ref', ), 'refdata')]),
827
424
            set(index.iter_entries_prefix([('name', ), ('ref', )])))
828
425
 
829
426
    def test_iter_key_prefix_1_key_element_refs(self):
830
427
        index = self.make_index(1, nodes=[
831
428
            (('name', ), 'data', ([('ref', )], )),
832
429
            (('ref', ), 'refdata', ([], ))])
833
 
        self.assertEqual(set([(index, ('name', ), 'data', ((('ref',),),)),
834
 
            (index, ('ref', ), 'refdata', ((), ))]),
 
430
        self.assertEqual(set([(('name', ), 'data', ((('ref',),),)),
 
431
            (('ref', ), 'refdata', ((), ))]),
835
432
            set(index.iter_entries_prefix([('name', ), ('ref', )])))
836
433
 
837
434
    def test_iter_key_prefix_2_key_element_no_refs(self):
839
436
            (('name', 'fin1'), 'data', ()),
840
437
            (('name', 'fin2'), 'beta', ()),
841
438
            (('ref', 'erence'), 'refdata', ())])
842
 
        self.assertEqual(set([(index, ('name', 'fin1'), 'data'),
843
 
            (index, ('ref', 'erence'), 'refdata')]),
 
439
        self.assertEqual(set([(('name', 'fin1'), 'data'),
 
440
            (('ref', 'erence'), 'refdata')]),
844
441
            set(index.iter_entries_prefix([('name', 'fin1'), ('ref', 'erence')])))
845
 
        self.assertEqual(set([(index, ('name', 'fin1'), 'data'),
846
 
            (index, ('name', 'fin2'), 'beta')]),
 
442
        self.assertEqual(set([(('name', 'fin1'), 'data'),
 
443
            (('name', 'fin2'), 'beta')]),
847
444
            set(index.iter_entries_prefix([('name', None)])))
848
445
 
849
446
    def test_iter_key_prefix_2_key_element_refs(self):
851
448
            (('name', 'fin1'), 'data', ([('ref', 'erence')], )),
852
449
            (('name', 'fin2'), 'beta', ([], )),
853
450
            (('ref', 'erence'), 'refdata', ([], ))])
854
 
        self.assertEqual(set([(index, ('name', 'fin1'), 'data', ((('ref', 'erence'),),)),
855
 
            (index, ('ref', 'erence'), 'refdata', ((), ))]),
 
451
        self.assertEqual(set([(('name', 'fin1'), 'data', ((('ref', 'erence'),),)),
 
452
            (('ref', 'erence'), 'refdata', ((), ))]),
856
453
            set(index.iter_entries_prefix([('name', 'fin1'), ('ref', 'erence')])))
857
 
        self.assertEqual(set([(index, ('name', 'fin1'), 'data', ((('ref', 'erence'),),)),
858
 
            (index, ('name', 'fin2'), 'beta', ((), ))]),
 
454
        self.assertEqual(set([(('name', 'fin1'), 'data', ((('ref', 'erence'),),)),
 
455
            (('name', 'fin2'), 'beta', ((), ))]),
859
456
            set(index.iter_entries_prefix([('name', None)])))
860
457
 
861
 
    def test_key_count_empty(self):
862
 
        index = self.make_index()
863
 
        self.assertEqual(0, index.key_count())
864
 
 
865
 
    def test_key_count_one(self):
866
 
        index = self.make_index(nodes=[(('name', ), '', ())])
867
 
        self.assertEqual(1, index.key_count())
868
 
 
869
 
    def test_key_count_two(self):
870
 
        index = self.make_index(nodes=[
871
 
            (('name', ), '', ()), (('foo', ), '', ())])
872
 
        self.assertEqual(2, index.key_count())
873
 
 
874
 
    def test_read_and_parse_tracks_real_read_value(self):
875
 
        index = self.make_index(nodes=self.make_nodes(10))
876
 
        del index._transport._activity[:]
877
 
        index._read_and_parse([(0, 200)])
878
 
        self.assertEqual([
879
 
            ('readv', 'index', [(0, 200)], True, index._size),
880
 
            ],
881
 
            index._transport._activity)
882
 
        # The readv expansion code will expand the initial request to 4096
883
 
        # bytes, which is more than enough to read the entire index, and we
884
 
        # will track the fact that we read that many bytes.
885
 
        self.assertEqual(index._size, index._bytes_read)
886
 
 
887
 
    def test_read_and_parse_triggers_buffer_all(self):
888
 
        index = self.make_index(key_elements=2, nodes=[
889
 
            (('name', 'fin1'), 'data', ()),
890
 
            (('name', 'fin2'), 'beta', ()),
891
 
            (('ref', 'erence'), 'refdata', ())])
892
 
        self.assertTrue(index._size > 0)
893
 
        self.assertIs(None, index._nodes)
894
 
        index._read_and_parse([(0, index._size)])
895
 
        self.assertIsNot(None, index._nodes)
896
 
 
897
458
    def test_validate_bad_index_errors(self):
898
459
        trans = self.get_transport()
899
460
        trans.put_bytes('name', "not an index\n")
900
 
        index = GraphIndex(trans, 'name', 13)
 
461
        index = GraphIndex(trans, 'name')
901
462
        self.assertRaises(errors.BadIndexFormatSignature, index.validate)
902
463
 
903
464
    def test_validate_bad_node_refs(self):
933
494
        index = self.make_index(nodes=[(('key', ), 'value', ())])
934
495
        index.validate()
935
496
 
936
 
    # XXX: external_references tests are duplicated in test_btree_index.  We
937
 
    # probably should have per_graph_index tests...
938
 
    def test_external_references_no_refs(self):
939
 
        index = self.make_index(ref_lists=0, nodes=[])
940
 
        self.assertRaises(ValueError, index.external_references, 0)
941
 
 
942
 
    def test_external_references_no_results(self):
943
 
        index = self.make_index(ref_lists=1, nodes=[
944
 
            (('key',), 'value', ([],))])
945
 
        self.assertEqual(set(), index.external_references(0))
946
 
 
947
 
    def test_external_references_missing_ref(self):
948
 
        missing_key = ('missing',)
949
 
        index = self.make_index(ref_lists=1, nodes=[
950
 
            (('key',), 'value', ([missing_key],))])
951
 
        self.assertEqual(set([missing_key]), index.external_references(0))
952
 
 
953
 
    def test_external_references_multiple_ref_lists(self):
954
 
        missing_key = ('missing',)
955
 
        index = self.make_index(ref_lists=2, nodes=[
956
 
            (('key',), 'value', ([], [missing_key]))])
957
 
        self.assertEqual(set([]), index.external_references(0))
958
 
        self.assertEqual(set([missing_key]), index.external_references(1))
959
 
 
960
 
    def test_external_references_two_records(self):
961
 
        index = self.make_index(ref_lists=1, nodes=[
962
 
            (('key-1',), 'value', ([('key-2',)],)),
963
 
            (('key-2',), 'value', ([],)),
964
 
            ])
965
 
        self.assertEqual(set([]), index.external_references(0))
966
 
 
967
 
    def test__find_ancestors(self):
968
 
        key1 = ('key-1',)
969
 
        key2 = ('key-2',)
970
 
        index = self.make_index(ref_lists=1, key_elements=1, nodes=[
971
 
            (key1, 'value', ([key2],)),
972
 
            (key2, 'value', ([],)),
973
 
            ])
974
 
        parent_map = {}
975
 
        missing_keys = set()
976
 
        search_keys = index._find_ancestors([key1], 0, parent_map, missing_keys)
977
 
        self.assertEqual({key1: (key2,)}, parent_map)
978
 
        self.assertEqual(set(), missing_keys)
979
 
        self.assertEqual(set([key2]), search_keys)
980
 
        search_keys = index._find_ancestors(search_keys, 0, parent_map,
981
 
                                            missing_keys)
982
 
        self.assertEqual({key1: (key2,), key2: ()}, parent_map)
983
 
        self.assertEqual(set(), missing_keys)
984
 
        self.assertEqual(set(), search_keys)
985
 
 
986
 
    def test__find_ancestors_w_missing(self):
987
 
        key1 = ('key-1',)
988
 
        key2 = ('key-2',)
989
 
        key3 = ('key-3',)
990
 
        index = self.make_index(ref_lists=1, key_elements=1, nodes=[
991
 
            (key1, 'value', ([key2],)),
992
 
            (key2, 'value', ([],)),
993
 
            ])
994
 
        parent_map = {}
995
 
        missing_keys = set()
996
 
        search_keys = index._find_ancestors([key2, key3], 0, parent_map,
997
 
                                            missing_keys)
998
 
        self.assertEqual({key2: ()}, parent_map)
999
 
        self.assertEqual(set([key3]), missing_keys)
1000
 
        self.assertEqual(set(), search_keys)
1001
 
 
1002
 
    def test__find_ancestors_dont_search_known(self):
1003
 
        key1 = ('key-1',)
1004
 
        key2 = ('key-2',)
1005
 
        key3 = ('key-3',)
1006
 
        index = self.make_index(ref_lists=1, key_elements=1, nodes=[
1007
 
            (key1, 'value', ([key2],)),
1008
 
            (key2, 'value', ([key3],)),
1009
 
            (key3, 'value', ([],)),
1010
 
            ])
1011
 
        # We already know about key2, so we won't try to search for key3
1012
 
        parent_map = {key2: (key3,)}
1013
 
        missing_keys = set()
1014
 
        search_keys = index._find_ancestors([key1], 0, parent_map,
1015
 
                                            missing_keys)
1016
 
        self.assertEqual({key1: (key2,), key2: (key3,)}, parent_map)
1017
 
        self.assertEqual(set(), missing_keys)
1018
 
        self.assertEqual(set(), search_keys)
1019
 
 
1020
 
    def test_supports_unlimited_cache(self):
1021
 
        builder = GraphIndexBuilder(0, key_elements=1)
1022
 
        stream = builder.finish()
1023
 
        trans = get_transport(self.get_url())
1024
 
        size = trans.put_file('index', stream)
1025
 
        # It doesn't matter what unlimited_cache does here, just that it can be
1026
 
        # passed
1027
 
        index = GraphIndex(trans, 'index', size, unlimited_cache=True)
1028
 
 
1029
497
 
1030
498
class TestCombinedGraphIndex(TestCaseWithMemoryTransport):
1031
499
 
1032
500
    def make_index(self, name, ref_lists=0, key_elements=1, nodes=[]):
1033
501
        builder = GraphIndexBuilder(ref_lists, key_elements=key_elements)
1034
 
        for key, value, references in nodes:
1035
 
            builder.add_node(key, value, references)
 
502
        for node, value, references in nodes:
 
503
            builder.add_node(node, value, references)
1036
504
        stream = builder.finish()
1037
505
        trans = self.get_transport()
1038
 
        size = trans.put_file(name, stream)
1039
 
        return GraphIndex(trans, name, size)
1040
 
 
1041
 
    def make_combined_index_with_missing(self, missing=['1', '2']):
1042
 
        """Create a CombinedGraphIndex which will have missing indexes.
1043
 
 
1044
 
        This creates a CGI which thinks it has 2 indexes, however they have
1045
 
        been deleted. If CGI._reload_func() is called, then it will repopulate
1046
 
        with a new index.
1047
 
 
1048
 
        :param missing: The underlying indexes to delete
1049
 
        :return: (CombinedGraphIndex, reload_counter)
1050
 
        """
1051
 
        index1 = self.make_index('1', nodes=[(('1',), '', ())])
1052
 
        index2 = self.make_index('2', nodes=[(('2',), '', ())])
1053
 
        index3 = self.make_index('3', nodes=[
1054
 
            (('1',), '', ()),
1055
 
            (('2',), '', ())])
1056
 
 
1057
 
        # total_reloads, num_changed, num_unchanged
1058
 
        reload_counter = [0, 0, 0]
1059
 
        def reload():
1060
 
            reload_counter[0] += 1
1061
 
            new_indices = [index3]
1062
 
            if index._indices == new_indices:
1063
 
                reload_counter[2] += 1
1064
 
                return False
1065
 
            reload_counter[1] += 1
1066
 
            index._indices[:] = new_indices
1067
 
            return True
1068
 
        index = CombinedGraphIndex([index1, index2], reload_func=reload)
1069
 
        trans = self.get_transport()
1070
 
        for fname in missing:
1071
 
            trans.delete(fname)
1072
 
        return index, reload_counter
 
506
        trans.put_file(name, stream)
 
507
        return GraphIndex(trans, name)
1073
508
 
1074
509
    def test_open_missing_index_no_error(self):
1075
510
        trans = self.get_transport()
1076
 
        index1 = GraphIndex(trans, 'missing', 100)
 
511
        index1 = GraphIndex(trans, 'missing')
1077
512
        index = CombinedGraphIndex([index1])
1078
513
 
1079
514
    def test_add_index(self):
1080
515
        index = CombinedGraphIndex([])
1081
516
        index1 = self.make_index('name', 0, nodes=[(('key', ), '', ())])
1082
517
        index.insert_index(0, index1)
1083
 
        self.assertEqual([(index1, ('key', ), '')], list(index.iter_all_entries()))
1084
 
 
1085
 
    def test_clear_cache(self):
1086
 
        log = []
1087
 
 
1088
 
        class ClearCacheProxy(object):
1089
 
 
1090
 
            def __init__(self, index):
1091
 
                self._index = index
1092
 
 
1093
 
            def __getattr__(self, name):
1094
 
                return getattr(self._index)
1095
 
 
1096
 
            def clear_cache(self):
1097
 
                log.append(self._index)
1098
 
                return self._index.clear_cache()
1099
 
 
1100
 
        index = CombinedGraphIndex([])
1101
 
        index1 = self.make_index('name', 0, nodes=[(('key', ), '', ())])
1102
 
        index.insert_index(0, ClearCacheProxy(index1))
1103
 
        index2 = self.make_index('name', 0, nodes=[(('key', ), '', ())])
1104
 
        index.insert_index(1, ClearCacheProxy(index2))
1105
 
        # CombinedGraphIndex should call 'clear_cache()' on all children
1106
 
        index.clear_cache()
1107
 
        self.assertEqual(sorted([index1, index2]), sorted(log))
 
518
        self.assertEqual([(('key', ), '')], list(index.iter_all_entries()))
1108
519
 
1109
520
    def test_iter_all_entries_empty(self):
1110
521
        index = CombinedGraphIndex([])
1118
529
    def test_iter_all_entries_simple(self):
1119
530
        index1 = self.make_index('name', nodes=[(('name', ), 'data', ())])
1120
531
        index = CombinedGraphIndex([index1])
1121
 
        self.assertEqual([(index1, ('name', ), 'data')],
 
532
        self.assertEqual([(('name', ), 'data')],
1122
533
            list(index.iter_all_entries()))
1123
534
 
1124
535
    def test_iter_all_entries_two_indices(self):
1125
536
        index1 = self.make_index('name1', nodes=[(('name', ), 'data', ())])
1126
537
        index2 = self.make_index('name2', nodes=[(('2', ), '', ())])
1127
538
        index = CombinedGraphIndex([index1, index2])
1128
 
        self.assertEqual([(index1, ('name', ), 'data'),
1129
 
            (index2, ('2', ), '')],
 
539
        self.assertEqual([(('name', ), 'data'),
 
540
            (('2', ), '')],
1130
541
            list(index.iter_all_entries()))
1131
542
 
1132
543
    def test_iter_entries_two_indices_dup_key(self):
1133
544
        index1 = self.make_index('name1', nodes=[(('name', ), 'data', ())])
1134
545
        index2 = self.make_index('name2', nodes=[(('name', ), 'data', ())])
1135
546
        index = CombinedGraphIndex([index1, index2])
1136
 
        self.assertEqual([(index1, ('name', ), 'data')],
 
547
        self.assertEqual([(('name', ), 'data')],
1137
548
            list(index.iter_entries([('name', )])))
1138
549
 
1139
550
    def test_iter_all_entries_two_indices_dup_key(self):
1140
551
        index1 = self.make_index('name1', nodes=[(('name', ), 'data', ())])
1141
552
        index2 = self.make_index('name2', nodes=[(('name', ), 'data', ())])
1142
553
        index = CombinedGraphIndex([index1, index2])
1143
 
        self.assertEqual([(index1, ('name', ), 'data')],
 
554
        self.assertEqual([(('name', ), 'data')],
1144
555
            list(index.iter_all_entries()))
1145
556
 
1146
557
    def test_iter_key_prefix_2_key_element_refs(self):
1150
561
            (('name', 'fin2'), 'beta', ([], )),
1151
562
            (('ref', 'erence'), 'refdata', ([], ))])
1152
563
        index = CombinedGraphIndex([index1, index2])
1153
 
        self.assertEqual(set([(index1, ('name', 'fin1'), 'data', ((('ref', 'erence'),),)),
1154
 
            (index2, ('ref', 'erence'), 'refdata', ((), ))]),
 
564
        self.assertEqual(set([(('name', 'fin1'), 'data', ((('ref', 'erence'),),)),
 
565
            (('ref', 'erence'), 'refdata', ((), ))]),
1155
566
            set(index.iter_entries_prefix([('name', 'fin1'), ('ref', 'erence')])))
1156
 
        self.assertEqual(set([(index1, ('name', 'fin1'), 'data', ((('ref', 'erence'),),)),
1157
 
            (index2, ('name', 'fin2'), 'beta', ((), ))]),
 
567
        self.assertEqual(set([(('name', 'fin1'), 'data', ((('ref', 'erence'),),)),
 
568
            (('name', 'fin2'), 'beta', ((), ))]),
1158
569
            set(index.iter_entries_prefix([('name', None)])))
1159
570
 
1160
571
    def test_iter_nothing_empty(self):
1172
583
        index2 = self.make_index('2', 1, nodes=[
1173
584
            (('ref', ), 'refdata', ((), ))])
1174
585
        index = CombinedGraphIndex([index1, index2])
1175
 
        self.assertEqual(set([(index1, ('name', ), 'data', ((('ref', ), ), )),
1176
 
            (index2, ('ref', ), 'refdata', ((), ))]),
 
586
        self.assertEqual(set([(('name', ), 'data', ((('ref', ), ), )),
 
587
            (('ref', ), 'refdata', ((), ))]),
1177
588
            set(index.iter_entries([('name', ), ('ref', )])))
1178
 
 
 
589
 
1179
590
    def test_iter_all_keys_dup_entry(self):
1180
591
        index1 = self.make_index('1', 1, nodes=[
1181
592
            (('name', ), 'data', ([('ref', )], )),
1183
594
        index2 = self.make_index('2', 1, nodes=[
1184
595
            (('ref', ), 'refdata', ([], ))])
1185
596
        index = CombinedGraphIndex([index1, index2])
1186
 
        self.assertEqual(set([(index1, ('name', ), 'data', ((('ref',),),)),
1187
 
            (index1, ('ref', ), 'refdata', ((), ))]),
 
597
        self.assertEqual(set([(('name', ), 'data', ((('ref',),),)),
 
598
            (('ref', ), 'refdata', ((), ))]),
1188
599
            set(index.iter_entries([('name', ), ('ref', )])))
1189
 
 
 
600
 
1190
601
    def test_iter_missing_entry_empty(self):
1191
602
        index = CombinedGraphIndex([])
1192
603
        self.assertEqual([], list(index.iter_entries([('a', )])))
1201
612
        index2 = self.make_index('2')
1202
613
        index = CombinedGraphIndex([index1, index2])
1203
614
        self.assertEqual([], list(index.iter_entries([('a', )])))
1204
 
 
 
615
 
1205
616
    def test_iter_entry_present_one_index_only(self):
1206
617
        index1 = self.make_index('1', nodes=[(('key', ), '', ())])
1207
618
        index2 = self.make_index('2', nodes=[])
1208
619
        index = CombinedGraphIndex([index1, index2])
1209
 
        self.assertEqual([(index1, ('key', ), '')],
 
620
        self.assertEqual([(('key', ), '')],
1210
621
            list(index.iter_entries([('key', )])))
1211
622
        # and in the other direction
1212
623
        index = CombinedGraphIndex([index2, index1])
1213
 
        self.assertEqual([(index1, ('key', ), '')],
 
624
        self.assertEqual([(('key', ), '')],
1214
625
            list(index.iter_entries([('key', )])))
1215
626
 
1216
 
    def test_key_count_empty(self):
1217
 
        index1 = self.make_index('1', nodes=[])
1218
 
        index2 = self.make_index('2', nodes=[])
1219
 
        index = CombinedGraphIndex([index1, index2])
1220
 
        self.assertEqual(0, index.key_count())
1221
 
 
1222
 
    def test_key_count_sums_index_keys(self):
1223
 
        index1 = self.make_index('1', nodes=[
1224
 
            (('1',), '', ()),
1225
 
            (('2',), '', ())])
1226
 
        index2 = self.make_index('2', nodes=[(('1',), '', ())])
1227
 
        index = CombinedGraphIndex([index1, index2])
1228
 
        self.assertEqual(3, index.key_count())
1229
 
 
1230
627
    def test_validate_bad_child_index_errors(self):
1231
628
        trans = self.get_transport()
1232
629
        trans.put_bytes('name', "not an index\n")
1233
 
        index1 = GraphIndex(trans, 'name', 13)
 
630
        index1 = GraphIndex(trans, 'name')
1234
631
        index = CombinedGraphIndex([index1])
1235
632
        self.assertRaises(errors.BadIndexFormatSignature, index.validate)
1236
633
 
1238
635
        index = CombinedGraphIndex([])
1239
636
        index.validate()
1240
637
 
1241
 
    def test_key_count_reloads(self):
1242
 
        index, reload_counter = self.make_combined_index_with_missing()
1243
 
        self.assertEqual(2, index.key_count())
1244
 
        self.assertEqual([1, 1, 0], reload_counter)
1245
 
 
1246
 
    def test_key_count_no_reload(self):
1247
 
        index, reload_counter = self.make_combined_index_with_missing()
1248
 
        index._reload_func = None
1249
 
        # Without a _reload_func we just raise the exception
1250
 
        self.assertRaises(errors.NoSuchFile, index.key_count)
1251
 
 
1252
 
    def test_key_count_reloads_and_fails(self):
1253
 
        # We have deleted all underlying indexes, so we will try to reload, but
1254
 
        # still fail. This is mostly to test we don't get stuck in an infinite
1255
 
        # loop trying to reload
1256
 
        index, reload_counter = self.make_combined_index_with_missing(
1257
 
                                    ['1', '2', '3'])
1258
 
        self.assertRaises(errors.NoSuchFile, index.key_count)
1259
 
        self.assertEqual([2, 1, 1], reload_counter)
1260
 
 
1261
 
    def test_iter_entries_reloads(self):
1262
 
        index, reload_counter = self.make_combined_index_with_missing()
1263
 
        result = list(index.iter_entries([('1',), ('2',), ('3',)]))
1264
 
        index3 = index._indices[0]
1265
 
        self.assertEqual([(index3, ('1',), ''), (index3, ('2',), '')],
1266
 
                         result)
1267
 
        self.assertEqual([1, 1, 0], reload_counter)
1268
 
 
1269
 
    def test_iter_entries_reloads_midway(self):
1270
 
        # The first index still looks present, so we get interrupted mid-way
1271
 
        # through
1272
 
        index, reload_counter = self.make_combined_index_with_missing(['2'])
1273
 
        index1, index2 = index._indices
1274
 
        result = list(index.iter_entries([('1',), ('2',), ('3',)]))
1275
 
        index3 = index._indices[0]
1276
 
        # We had already yielded '1', so we just go on to the next, we should
1277
 
        # not yield '1' twice.
1278
 
        self.assertEqual([(index1, ('1',), ''), (index3, ('2',), '')],
1279
 
                         result)
1280
 
        self.assertEqual([1, 1, 0], reload_counter)
1281
 
 
1282
 
    def test_iter_entries_no_reload(self):
1283
 
        index, reload_counter = self.make_combined_index_with_missing()
1284
 
        index._reload_func = None
1285
 
        # Without a _reload_func we just raise the exception
1286
 
        self.assertListRaises(errors.NoSuchFile, index.iter_entries, [('3',)])
1287
 
 
1288
 
    def test_iter_entries_reloads_and_fails(self):
1289
 
        index, reload_counter = self.make_combined_index_with_missing(
1290
 
                                    ['1', '2', '3'])
1291
 
        self.assertListRaises(errors.NoSuchFile, index.iter_entries, [('3',)])
1292
 
        self.assertEqual([2, 1, 1], reload_counter)
1293
 
 
1294
 
    def test_iter_all_entries_reloads(self):
1295
 
        index, reload_counter = self.make_combined_index_with_missing()
1296
 
        result = list(index.iter_all_entries())
1297
 
        index3 = index._indices[0]
1298
 
        self.assertEqual([(index3, ('1',), ''), (index3, ('2',), '')],
1299
 
                         result)
1300
 
        self.assertEqual([1, 1, 0], reload_counter)
1301
 
 
1302
 
    def test_iter_all_entries_reloads_midway(self):
1303
 
        index, reload_counter = self.make_combined_index_with_missing(['2'])
1304
 
        index1, index2 = index._indices
1305
 
        result = list(index.iter_all_entries())
1306
 
        index3 = index._indices[0]
1307
 
        # We had already yielded '1', so we just go on to the next, we should
1308
 
        # not yield '1' twice.
1309
 
        self.assertEqual([(index1, ('1',), ''), (index3, ('2',), '')],
1310
 
                         result)
1311
 
        self.assertEqual([1, 1, 0], reload_counter)
1312
 
 
1313
 
    def test_iter_all_entries_no_reload(self):
1314
 
        index, reload_counter = self.make_combined_index_with_missing()
1315
 
        index._reload_func = None
1316
 
        self.assertListRaises(errors.NoSuchFile, index.iter_all_entries)
1317
 
 
1318
 
    def test_iter_all_entries_reloads_and_fails(self):
1319
 
        index, reload_counter = self.make_combined_index_with_missing(
1320
 
                                    ['1', '2', '3'])
1321
 
        self.assertListRaises(errors.NoSuchFile, index.iter_all_entries)
1322
 
 
1323
 
    def test_iter_entries_prefix_reloads(self):
1324
 
        index, reload_counter = self.make_combined_index_with_missing()
1325
 
        result = list(index.iter_entries_prefix([('1',)]))
1326
 
        index3 = index._indices[0]
1327
 
        self.assertEqual([(index3, ('1',), '')], result)
1328
 
        self.assertEqual([1, 1, 0], reload_counter)
1329
 
 
1330
 
    def test_iter_entries_prefix_reloads_midway(self):
1331
 
        index, reload_counter = self.make_combined_index_with_missing(['2'])
1332
 
        index1, index2 = index._indices
1333
 
        result = list(index.iter_entries_prefix([('1',)]))
1334
 
        index3 = index._indices[0]
1335
 
        # We had already yielded '1', so we just go on to the next, we should
1336
 
        # not yield '1' twice.
1337
 
        self.assertEqual([(index1, ('1',), '')], result)
1338
 
        self.assertEqual([1, 1, 0], reload_counter)
1339
 
 
1340
 
    def test_iter_entries_prefix_no_reload(self):
1341
 
        index, reload_counter = self.make_combined_index_with_missing()
1342
 
        index._reload_func = None
1343
 
        self.assertListRaises(errors.NoSuchFile, index.iter_entries_prefix,
1344
 
                                                 [('1',)])
1345
 
 
1346
 
    def test_iter_entries_prefix_reloads_and_fails(self):
1347
 
        index, reload_counter = self.make_combined_index_with_missing(
1348
 
                                    ['1', '2', '3'])
1349
 
        self.assertListRaises(errors.NoSuchFile, index.iter_entries_prefix,
1350
 
                                                 [('1',)])
1351
 
 
1352
 
    def test_validate_reloads(self):
1353
 
        index, reload_counter = self.make_combined_index_with_missing()
1354
 
        index.validate()
1355
 
        self.assertEqual([1, 1, 0], reload_counter)
1356
 
 
1357
 
    def test_validate_reloads_midway(self):
1358
 
        index, reload_counter = self.make_combined_index_with_missing(['2'])
1359
 
        index.validate()
1360
 
 
1361
 
    def test_validate_no_reload(self):
1362
 
        index, reload_counter = self.make_combined_index_with_missing()
1363
 
        index._reload_func = None
1364
 
        self.assertRaises(errors.NoSuchFile, index.validate)
1365
 
 
1366
 
    def test_validate_reloads_and_fails(self):
1367
 
        index, reload_counter = self.make_combined_index_with_missing(
1368
 
                                    ['1', '2', '3'])
1369
 
        self.assertRaises(errors.NoSuchFile, index.validate)
1370
 
 
1371
 
    def test_find_ancestors_across_indexes(self):
1372
 
        key1 = ('key-1',)
1373
 
        key2 = ('key-2',)
1374
 
        key3 = ('key-3',)
1375
 
        key4 = ('key-4',)
1376
 
        index1 = self.make_index('12', ref_lists=1, nodes=[
1377
 
            (key1, 'value', ([],)),
1378
 
            (key2, 'value', ([key1],)),
1379
 
            ])
1380
 
        index2 = self.make_index('34', ref_lists=1, nodes=[
1381
 
            (key3, 'value', ([key2],)),
1382
 
            (key4, 'value', ([key3],)),
1383
 
            ])
1384
 
        c_index = CombinedGraphIndex([index1, index2])
1385
 
        parent_map, missing_keys = c_index.find_ancestry([key1], 0)
1386
 
        self.assertEqual({key1: ()}, parent_map)
1387
 
        self.assertEqual(set(), missing_keys)
1388
 
        # Now look for a key from index2 which requires us to find the key in
1389
 
        # the second index, and then continue searching for parents in the
1390
 
        # first index
1391
 
        parent_map, missing_keys = c_index.find_ancestry([key3], 0)
1392
 
        self.assertEqual({key1: (), key2: (key1,), key3: (key2,)}, parent_map)
1393
 
        self.assertEqual(set(), missing_keys)
1394
 
 
1395
 
    def test_find_ancestors_missing_keys(self):
1396
 
        key1 = ('key-1',)
1397
 
        key2 = ('key-2',)
1398
 
        key3 = ('key-3',)
1399
 
        key4 = ('key-4',)
1400
 
        index1 = self.make_index('12', ref_lists=1, nodes=[
1401
 
            (key1, 'value', ([],)),
1402
 
            (key2, 'value', ([key1],)),
1403
 
            ])
1404
 
        index2 = self.make_index('34', ref_lists=1, nodes=[
1405
 
            (key3, 'value', ([key2],)),
1406
 
            ])
1407
 
        c_index = CombinedGraphIndex([index1, index2])
1408
 
        # Searching for a key which is actually not present at all should
1409
 
        # eventually converge
1410
 
        parent_map, missing_keys = c_index.find_ancestry([key4], 0)
1411
 
        self.assertEqual({}, parent_map)
1412
 
        self.assertEqual(set([key4]), missing_keys)
1413
 
 
1414
 
    def test_find_ancestors_no_indexes(self):
1415
 
        c_index = CombinedGraphIndex([])
1416
 
        key1 = ('key-1',)
1417
 
        parent_map, missing_keys = c_index.find_ancestry([key1], 0)
1418
 
        self.assertEqual({}, parent_map)
1419
 
        self.assertEqual(set([key1]), missing_keys)
1420
 
 
1421
 
    def test_find_ancestors_ghost_parent(self):
1422
 
        key1 = ('key-1',)
1423
 
        key2 = ('key-2',)
1424
 
        key3 = ('key-3',)
1425
 
        key4 = ('key-4',)
1426
 
        index1 = self.make_index('12', ref_lists=1, nodes=[
1427
 
            (key1, 'value', ([],)),
1428
 
            (key2, 'value', ([key1],)),
1429
 
            ])
1430
 
        index2 = self.make_index('34', ref_lists=1, nodes=[
1431
 
            (key4, 'value', ([key2, key3],)),
1432
 
            ])
1433
 
        c_index = CombinedGraphIndex([index1, index2])
1434
 
        # Searching for a key which is actually not present at all should
1435
 
        # eventually converge
1436
 
        parent_map, missing_keys = c_index.find_ancestry([key4], 0)
1437
 
        self.assertEqual({key4: (key2, key3), key2: (key1,), key1: ()},
1438
 
                         parent_map)
1439
 
        self.assertEqual(set([key3]), missing_keys)
1440
 
 
1441
 
    def test__find_ancestors_empty_index(self):
1442
 
        index = self.make_index('test', ref_lists=1, key_elements=1, nodes=[])
1443
 
        parent_map = {}
1444
 
        missing_keys = set()
1445
 
        search_keys = index._find_ancestors([('one',), ('two',)], 0, parent_map,
1446
 
                                            missing_keys)
1447
 
        self.assertEqual(set(), search_keys)
1448
 
        self.assertEqual({}, parent_map)
1449
 
        self.assertEqual(set([('one',), ('two',)]), missing_keys)
1450
 
 
1451
638
 
1452
639
class TestInMemoryGraphIndex(TestCaseWithMemoryTransport):
1453
640
 
1461
648
        index.add_nodes([(('name', ), 'data')])
1462
649
        index.add_nodes([(('name2', ), ''), (('name3', ), '')])
1463
650
        self.assertEqual(set([
1464
 
            (index, ('name', ), 'data'),
1465
 
            (index, ('name2', ), ''),
1466
 
            (index, ('name3', ), ''),
 
651
            (('name', ), 'data'),
 
652
            (('name2', ), ''),
 
653
            (('name3', ), ''),
1467
654
            ]), set(index.iter_all_entries()))
1468
655
 
1469
656
    def test_add_nodes(self):
1471
658
        index.add_nodes([(('name', ), 'data', ([],))])
1472
659
        index.add_nodes([(('name2', ), '', ([],)), (('name3', ), '', ([('r', )],))])
1473
660
        self.assertEqual(set([
1474
 
            (index, ('name', ), 'data', ((),)),
1475
 
            (index, ('name2', ), '', ((),)),
1476
 
            (index, ('name3', ), '', ((('r', ), ), )),
 
661
            (('name', ), 'data', ((),)),
 
662
            (('name2', ), '', ((),)),
 
663
            (('name3', ), '', ((('r', ), ), )),
1477
664
            ]), set(index.iter_all_entries()))
1478
665
 
1479
666
    def test_iter_all_entries_empty(self):
1482
669
 
1483
670
    def test_iter_all_entries_simple(self):
1484
671
        index = self.make_index(nodes=[(('name', ), 'data')])
1485
 
        self.assertEqual([(index, ('name', ), 'data')],
 
672
        self.assertEqual([(('name', ), 'data')],
1486
673
            list(index.iter_all_entries()))
1487
674
 
1488
675
    def test_iter_all_entries_references(self):
1489
676
        index = self.make_index(1, nodes=[
1490
677
            (('name', ), 'data', ([('ref', )], )),
1491
678
            (('ref', ), 'refdata', ([], ))])
1492
 
        self.assertEqual(set([(index, ('name', ), 'data', ((('ref', ),),)),
1493
 
            (index, ('ref', ), 'refdata', ((), ))]),
 
679
        self.assertEqual(set([(('name', ), 'data', ((('ref', ),),)),
 
680
            (('ref', ), 'refdata', ((), ))]),
1494
681
            set(index.iter_all_entries()))
1495
682
 
1496
683
    def test_iteration_absent_skipped(self):
1497
684
        index = self.make_index(1, nodes=[
1498
685
            (('name', ), 'data', ([('ref', )], ))])
1499
 
        self.assertEqual(set([(index, ('name', ), 'data', ((('ref',),),))]),
 
686
        self.assertEqual(set([(('name', ), 'data', ((('ref',),),))]),
1500
687
            set(index.iter_all_entries()))
1501
 
        self.assertEqual(set([(index, ('name', ), 'data', ((('ref',),),))]),
 
688
        self.assertEqual(set([(('name', ), 'data', ((('ref',),),))]),
1502
689
            set(index.iter_entries([('name', )])))
1503
690
        self.assertEqual([], list(index.iter_entries([('ref', )])))
1504
691
 
1506
693
        index = self.make_index(1, nodes=[
1507
694
            (('name', ), 'data', ([('ref', )], )),
1508
695
            (('ref', ), 'refdata', ([], ))])
1509
 
        self.assertEqual(set([(index, ('name', ), 'data', ((('ref',),),)),
1510
 
            (index, ('ref', ), 'refdata', ((), ))]),
 
696
        self.assertEqual(set([(('name', ), 'data', ((('ref',),),)),
 
697
            (('ref', ), 'refdata', ((), ))]),
1511
698
            set(index.iter_entries([('name', ), ('ref', )])))
1512
699
 
1513
700
    def test_iter_key_prefix_1_key_element_no_refs(self):
1514
701
        index = self.make_index( nodes=[
1515
702
            (('name', ), 'data'),
1516
703
            (('ref', ), 'refdata')])
1517
 
        self.assertEqual(set([(index, ('name', ), 'data'),
1518
 
            (index, ('ref', ), 'refdata')]),
 
704
        self.assertEqual(set([(('name', ), 'data'),
 
705
            (('ref', ), 'refdata')]),
1519
706
            set(index.iter_entries_prefix([('name', ), ('ref', )])))
1520
707
 
1521
708
    def test_iter_key_prefix_1_key_element_refs(self):
1522
709
        index = self.make_index(1, nodes=[
1523
710
            (('name', ), 'data', ([('ref', )], )),
1524
711
            (('ref', ), 'refdata', ([], ))])
1525
 
        self.assertEqual(set([(index, ('name', ), 'data', ((('ref',),),)),
1526
 
            (index, ('ref', ), 'refdata', ((), ))]),
 
712
        self.assertEqual(set([(('name', ), 'data', ((('ref',),),)),
 
713
            (('ref', ), 'refdata', ((), ))]),
1527
714
            set(index.iter_entries_prefix([('name', ), ('ref', )])))
1528
715
 
1529
716
    def test_iter_key_prefix_2_key_element_no_refs(self):
1531
718
            (('name', 'fin1'), 'data'),
1532
719
            (('name', 'fin2'), 'beta'),
1533
720
            (('ref', 'erence'), 'refdata')])
1534
 
        self.assertEqual(set([(index, ('name', 'fin1'), 'data'),
1535
 
            (index, ('ref', 'erence'), 'refdata')]),
 
721
        self.assertEqual(set([(('name', 'fin1'), 'data'),
 
722
            (('ref', 'erence'), 'refdata')]),
1536
723
            set(index.iter_entries_prefix([('name', 'fin1'), ('ref', 'erence')])))
1537
 
        self.assertEqual(set([(index, ('name', 'fin1'), 'data'),
1538
 
            (index, ('name', 'fin2'), 'beta')]),
 
724
        self.assertEqual(set([(('name', 'fin1'), 'data'),
 
725
            (('name', 'fin2'), 'beta')]),
1539
726
            set(index.iter_entries_prefix([('name', None)])))
1540
727
 
1541
728
    def test_iter_key_prefix_2_key_element_refs(self):
1543
730
            (('name', 'fin1'), 'data', ([('ref', 'erence')], )),
1544
731
            (('name', 'fin2'), 'beta', ([], )),
1545
732
            (('ref', 'erence'), 'refdata', ([], ))])
1546
 
        self.assertEqual(set([(index, ('name', 'fin1'), 'data', ((('ref', 'erence'),),)),
1547
 
            (index, ('ref', 'erence'), 'refdata', ((), ))]),
 
733
        self.assertEqual(set([(('name', 'fin1'), 'data', ((('ref', 'erence'),),)),
 
734
            (('ref', 'erence'), 'refdata', ((), ))]),
1548
735
            set(index.iter_entries_prefix([('name', 'fin1'), ('ref', 'erence')])))
1549
 
        self.assertEqual(set([(index, ('name', 'fin1'), 'data', ((('ref', 'erence'),),)),
1550
 
            (index, ('name', 'fin2'), 'beta', ((), ))]),
 
736
        self.assertEqual(set([(('name', 'fin1'), 'data', ((('ref', 'erence'),),)),
 
737
            (('name', 'fin2'), 'beta', ((), ))]),
1551
738
            set(index.iter_entries_prefix([('name', None)])))
1552
739
 
1553
740
    def test_iter_nothing_empty(self):
1558
745
        index = self.make_index()
1559
746
        self.assertEqual([], list(index.iter_entries(['a'])))
1560
747
 
1561
 
    def test_key_count_empty(self):
1562
 
        index = self.make_index()
1563
 
        self.assertEqual(0, index.key_count())
1564
 
 
1565
 
    def test_key_count_one(self):
1566
 
        index = self.make_index(nodes=[(('name', ), '')])
1567
 
        self.assertEqual(1, index.key_count())
1568
 
 
1569
 
    def test_key_count_two(self):
1570
 
        index = self.make_index(nodes=[(('name', ), ''), (('foo', ), '')])
1571
 
        self.assertEqual(2, index.key_count())
1572
 
 
1573
748
    def test_validate_empty(self):
1574
749
        index = self.make_index()
1575
750
        index.validate()
1579
754
        index.validate()
1580
755
 
1581
756
 
1582
 
class TestGraphIndexPrefixAdapter(TestCaseWithMemoryTransport):
1583
 
 
1584
 
    def make_index(self, ref_lists=1, key_elements=2, nodes=[], add_callback=False):
1585
 
        result = InMemoryGraphIndex(ref_lists, key_elements=key_elements)
1586
 
        result.add_nodes(nodes)
1587
 
        if add_callback:
1588
 
            add_nodes_callback = result.add_nodes
1589
 
        else:
1590
 
            add_nodes_callback = None
1591
 
        adapter = GraphIndexPrefixAdapter(result, ('prefix', ), key_elements - 1,
1592
 
            add_nodes_callback=add_nodes_callback)
1593
 
        return result, adapter
1594
 
 
1595
 
    def test_add_node(self):
1596
 
        index, adapter = self.make_index(add_callback=True)
1597
 
        adapter.add_node(('key',), 'value', ((('ref',),),))
1598
 
        self.assertEqual(set([(index, ('prefix', 'key'), 'value', ((('prefix', 'ref'),),))]),
1599
 
            set(index.iter_all_entries()))
1600
 
 
1601
 
    def test_add_nodes(self):
1602
 
        index, adapter = self.make_index(add_callback=True)
1603
 
        adapter.add_nodes((
1604
 
            (('key',), 'value', ((('ref',),),)),
1605
 
            (('key2',), 'value2', ((),)),
1606
 
            ))
1607
 
        self.assertEqual(set([
1608
 
            (index, ('prefix', 'key2'), 'value2', ((),)),
1609
 
            (index, ('prefix', 'key'), 'value', ((('prefix', 'ref'),),))
1610
 
            ]),
1611
 
            set(index.iter_all_entries()))
1612
 
 
1613
 
    def test_construct(self):
1614
 
        index = InMemoryGraphIndex()
1615
 
        adapter = GraphIndexPrefixAdapter(index, ('prefix', ), 1)
1616
 
 
1617
 
    def test_construct_with_callback(self):
1618
 
        index = InMemoryGraphIndex()
1619
 
        adapter = GraphIndexPrefixAdapter(index, ('prefix', ), 1, index.add_nodes)
1620
 
 
1621
 
    def test_iter_all_entries_cross_prefix_map_errors(self):
1622
 
        index, adapter = self.make_index(nodes=[
1623
 
            (('prefix', 'key1'), 'data1', ((('prefixaltered', 'key2'),),))])
1624
 
        self.assertRaises(errors.BadIndexData, list, adapter.iter_all_entries())
1625
 
 
1626
 
    def test_iter_all_entries(self):
1627
 
        index, adapter = self.make_index(nodes=[
1628
 
            (('notprefix', 'key1'), 'data', ((), )),
1629
 
            (('prefix', 'key1'), 'data1', ((), )),
1630
 
            (('prefix', 'key2'), 'data2', ((('prefix', 'key1'),),))])
1631
 
        self.assertEqual(set([(index, ('key1', ), 'data1', ((),)),
1632
 
            (index, ('key2', ), 'data2', ((('key1',),),))]),
1633
 
            set(adapter.iter_all_entries()))
1634
 
 
1635
 
    def test_iter_entries(self):
1636
 
        index, adapter = self.make_index(nodes=[
1637
 
            (('notprefix', 'key1'), 'data', ((), )),
1638
 
            (('prefix', 'key1'), 'data1', ((), )),
1639
 
            (('prefix', 'key2'), 'data2', ((('prefix', 'key1'),),))])
1640
 
        # ask for many - get all
1641
 
        self.assertEqual(set([(index, ('key1', ), 'data1', ((),)),
1642
 
            (index, ('key2', ), 'data2', ((('key1', ),),))]),
1643
 
            set(adapter.iter_entries([('key1', ), ('key2', )])))
1644
 
        # ask for one, get one
1645
 
        self.assertEqual(set([(index, ('key1', ), 'data1', ((),))]),
1646
 
            set(adapter.iter_entries([('key1', )])))
1647
 
        # ask for missing, get none
1648
 
        self.assertEqual(set(),
1649
 
            set(adapter.iter_entries([('key3', )])))
1650
 
 
1651
 
    def test_iter_entries_prefix(self):
1652
 
        index, adapter = self.make_index(key_elements=3, nodes=[
1653
 
            (('notprefix', 'foo', 'key1'), 'data', ((), )),
1654
 
            (('prefix', 'prefix2', 'key1'), 'data1', ((), )),
1655
 
            (('prefix', 'prefix2', 'key2'), 'data2', ((('prefix', 'prefix2', 'key1'),),))])
1656
 
        # ask for a prefix, get the results for just that prefix, adjusted.
1657
 
        self.assertEqual(set([(index, ('prefix2', 'key1', ), 'data1', ((),)),
1658
 
            (index, ('prefix2', 'key2', ), 'data2', ((('prefix2', 'key1', ),),))]),
1659
 
            set(adapter.iter_entries_prefix([('prefix2', None)])))
1660
 
 
1661
 
    def test_key_count_no_matching_keys(self):
1662
 
        index, adapter = self.make_index(nodes=[
1663
 
            (('notprefix', 'key1'), 'data', ((), ))])
1664
 
        self.assertEqual(0, adapter.key_count())
1665
 
 
1666
 
    def test_key_count_some_keys(self):
1667
 
        index, adapter = self.make_index(nodes=[
1668
 
            (('notprefix', 'key1'), 'data', ((), )),
1669
 
            (('prefix', 'key1'), 'data1', ((), )),
1670
 
            (('prefix', 'key2'), 'data2', ((('prefix', 'key1'),),))])
1671
 
        self.assertEqual(2, adapter.key_count())
1672
 
 
1673
 
    def test_validate(self):
1674
 
        index, adapter = self.make_index()
1675
 
        calls = []
1676
 
        def validate():
1677
 
            calls.append('called')
1678
 
        index.validate = validate
1679
 
        adapter.validate()
1680
 
        self.assertEqual(['called'], calls)