~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_knit.py

Merge from bzr.dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
26
26
    errors,
27
27
    generate_ids,
28
28
    knit,
 
29
    pack,
29
30
    )
30
31
from bzrlib.errors import (
31
32
    RevisionAlreadyPresent,
40
41
    KnitVersionedFile,
41
42
    KnitPlainFactory,
42
43
    KnitAnnotateFactory,
 
44
    _KnitAccess,
43
45
    _KnitData,
44
46
    _KnitIndex,
 
47
    _PackAccess,
45
48
    WeaveToKnit,
46
49
    KnitSequenceMatcher,
47
50
    )
48
51
from bzrlib.osutils import split_lines
49
 
from bzrlib.tests import TestCase, TestCaseWithTransport, Feature
 
52
from bzrlib.tests import (
 
53
    Feature,
 
54
    TestCase,
 
55
    TestCaseWithMemoryTransport,
 
56
    TestCaseWithTransport,
 
57
    )
50
58
from bzrlib.transport import TransportLogger, get_transport
51
59
from bzrlib.transport.memory import MemoryTransport
52
60
from bzrlib.weave import Weave
145
153
        return queue_call
146
154
 
147
155
 
 
156
class KnitRecordAccessTestsMixin(object):
 
157
    """Tests for getting and putting knit records."""
 
158
 
 
159
    def assertAccessExists(self, access):
 
160
        """Ensure the data area for access has been initialised/exists."""
 
161
        raise NotImplementedError(self.assertAccessExists)
 
162
 
 
163
    def test_add_raw_records(self):
 
164
        """Add_raw_records adds records retrievable later."""
 
165
        access = self.get_access()
 
166
        memos = access.add_raw_records([10], '1234567890')
 
167
        self.assertEqual(['1234567890'], list(access.get_raw_records(memos)))
 
168
 
 
169
    def test_add_several_raw_records(self):
 
170
        """add_raw_records with many records and read some back."""
 
171
        access = self.get_access()
 
172
        memos = access.add_raw_records([10, 2, 5], '12345678901234567')
 
173
        self.assertEqual(['1234567890', '12', '34567'],
 
174
            list(access.get_raw_records(memos)))
 
175
        self.assertEqual(['1234567890'],
 
176
            list(access.get_raw_records(memos[0:1])))
 
177
        self.assertEqual(['12'],
 
178
            list(access.get_raw_records(memos[1:2])))
 
179
        self.assertEqual(['34567'],
 
180
            list(access.get_raw_records(memos[2:3])))
 
181
        self.assertEqual(['1234567890', '34567'],
 
182
            list(access.get_raw_records(memos[0:1] + memos[2:3])))
 
183
 
 
184
    def test_create(self):
 
185
        """create() should make a file on disk."""
 
186
        access = self.get_access()
 
187
        access.create()
 
188
        self.assertAccessExists(access)
 
189
 
 
190
    def test_open_file(self):
 
191
        """open_file never errors."""
 
192
        access = self.get_access()
 
193
        access.open_file()
 
194
 
 
195
 
 
196
class TestKnitKnitAccess(TestCaseWithMemoryTransport, KnitRecordAccessTestsMixin):
 
197
    """Tests for the .kndx implementation."""
 
198
 
 
199
    def assertAccessExists(self, access):
 
200
        self.assertNotEqual(None, access.open_file())
 
201
 
 
202
    def get_access(self):
 
203
        """Get a .knit style access instance."""
 
204
        access = _KnitAccess(self.get_transport(), "foo.knit", None, None,
 
205
            False, False)
 
206
        return access
 
207
    
 
208
 
 
209
class TestPackKnitAccess(TestCaseWithMemoryTransport, KnitRecordAccessTestsMixin):
 
210
    """Tests for the pack based access."""
 
211
 
 
212
    def assertAccessExists(self, access):
 
213
        # as pack based access has no backing unless an index maps data, this
 
214
        # is a no-op.
 
215
        pass
 
216
 
 
217
    def get_access(self):
 
218
        return self._get_access()[0]
 
219
 
 
220
    def _get_access(self, packname='packfile', index='FOO'):
 
221
        transport = self.get_transport()
 
222
        def write_data(bytes):
 
223
            transport.append_bytes(packname, bytes)
 
224
        writer = pack.ContainerWriter(write_data)
 
225
        writer.begin()
 
226
        indices = {index:(transport, packname)}
 
227
        access = _PackAccess(indices, writer=(writer, index))
 
228
        return access, writer
 
229
 
 
230
    def test_read_from_several_packs(self):
 
231
        access, writer = self._get_access()
 
232
        memos = []
 
233
        memos.extend(access.add_raw_records([10], '1234567890'))
 
234
        writer.end()
 
235
        access, writer = self._get_access('pack2', 'FOOBAR')
 
236
        memos.extend(access.add_raw_records([5], '12345'))
 
237
        writer.end()
 
238
        access, writer = self._get_access('pack3', 'BAZ')
 
239
        memos.extend(access.add_raw_records([5], 'alpha'))
 
240
        writer.end()
 
241
        transport = self.get_transport()
 
242
        access = _PackAccess({"FOO":(transport, 'packfile'),
 
243
            "FOOBAR":(transport, 'pack2'),
 
244
            "BAZ":(transport, 'pack3')})
 
245
        self.assertEqual(['1234567890', '12345', 'alpha'],
 
246
            list(access.get_raw_records(memos)))
 
247
        self.assertEqual(['1234567890'],
 
248
            list(access.get_raw_records(memos[0:1])))
 
249
        self.assertEqual(['12345'],
 
250
            list(access.get_raw_records(memos[1:2])))
 
251
        self.assertEqual(['alpha'],
 
252
            list(access.get_raw_records(memos[2:3])))
 
253
        self.assertEqual(['1234567890', 'alpha'],
 
254
            list(access.get_raw_records(memos[0:1] + memos[2:3])))
 
255
 
 
256
    def test_set_writer(self):
 
257
        """The writer should be settable post construction."""
 
258
        access = _PackAccess({})
 
259
        transport = self.get_transport()
 
260
        packname = 'packfile'
 
261
        index = 'foo'
 
262
        def write_data(bytes):
 
263
            transport.append_bytes(packname, bytes)
 
264
        writer = pack.ContainerWriter(write_data)
 
265
        writer.begin()
 
266
        access.set_writer(writer, index, (transport, packname))
 
267
        memos = access.add_raw_records([10], '1234567890')
 
268
        writer.end()
 
269
        self.assertEqual(['1234567890'], list(access.get_raw_records(memos)))
 
270
 
 
271
 
148
272
class LowLevelKnitDataTests(TestCase):
149
273
 
150
274
    def create_gz_content(self, text):
162
286
                                        'end rev-id-1\n'
163
287
                                        % (sha1sum,))
164
288
        transport = MockTransport([gz_txt])
165
 
        data = _KnitData(transport, 'filename', mode='r')
166
 
        records = [('rev-id-1', 0, len(gz_txt))]
 
289
        access = _KnitAccess(transport, 'filename', None, None, False, False)
 
290
        data = _KnitData(access=access)
 
291
        records = [('rev-id-1', (None, 0, len(gz_txt)))]
167
292
 
168
293
        contents = data.read_records(records)
169
294
        self.assertEqual({'rev-id-1':(['foo\n', 'bar\n'], sha1sum)}, contents)
179
304
                                        'end rev-id-1\n'
180
305
                                        % (sha1sum,))
181
306
        transport = MockTransport([gz_txt])
182
 
        data = _KnitData(transport, 'filename', mode='r')
183
 
        records = [('rev-id-1', 0, len(gz_txt))]
 
307
        access = _KnitAccess(transport, 'filename', None, None, False, False)
 
308
        data = _KnitData(access=access)
 
309
        records = [('rev-id-1', (None, 0, len(gz_txt)))]
184
310
        self.assertRaises(errors.KnitCorrupt, data.read_records, records)
185
311
 
186
312
        # read_records_iter_raw won't detect that sort of mismatch/corruption
196
322
                                        'end rev-id-1\n'
197
323
                                        % (sha1sum,))
198
324
        transport = MockTransport([gz_txt])
199
 
        data = _KnitData(transport, 'filename', mode='r')
200
 
        records = [('rev-id-1', 0, len(gz_txt))]
 
325
        access = _KnitAccess(transport, 'filename', None, None, False, False)
 
326
        data = _KnitData(access=access)
 
327
        records = [('rev-id-1', (None, 0, len(gz_txt)))]
201
328
        self.assertRaises(errors.KnitCorrupt, data.read_records, records)
202
329
 
203
330
        # read_records_iter_raw won't detect that sort of mismatch/corruption
212
339
                                        'end rev-id-1\n'
213
340
                                        % (sha1sum,))
214
341
        transport = MockTransport([gz_txt])
215
 
        data = _KnitData(transport, 'filename', mode='r')
 
342
        access = _KnitAccess(transport, 'filename', None, None, False, False)
 
343
        data = _KnitData(access=access)
216
344
        # We are asking for rev-id-2, but the data is rev-id-1
217
 
        records = [('rev-id-2', 0, len(gz_txt))]
 
345
        records = [('rev-id-2', (None, 0, len(gz_txt)))]
218
346
        self.assertRaises(errors.KnitCorrupt, data.read_records, records)
219
347
 
220
348
        # read_records_iter_raw will notice if we request the wrong version.
229
357
               'end rev-id-1\n'
230
358
               % (sha1sum,))
231
359
        transport = MockTransport([txt])
232
 
        data = _KnitData(transport, 'filename', mode='r')
233
 
        records = [('rev-id-1', 0, len(txt))]
 
360
        access = _KnitAccess(transport, 'filename', None, None, False, False)
 
361
        data = _KnitData(access=access)
 
362
        records = [('rev-id-1', (None, 0, len(txt)))]
234
363
 
235
364
        # We don't have valid gzip data ==> corrupt
236
365
        self.assertRaises(errors.KnitCorrupt, data.read_records, records)
249
378
        # Change 2 bytes in the middle to \xff
250
379
        gz_txt = gz_txt[:10] + '\xff\xff' + gz_txt[12:]
251
380
        transport = MockTransport([gz_txt])
252
 
        data = _KnitData(transport, 'filename', mode='r')
253
 
        records = [('rev-id-1', 0, len(gz_txt))]
 
381
        access = _KnitAccess(transport, 'filename', None, None, False, False)
 
382
        data = _KnitData(access=access)
 
383
        records = [('rev-id-1', (None, 0, len(gz_txt)))]
254
384
 
255
385
        self.assertRaises(errors.KnitCorrupt, data.read_records, records)
256
386
 
362
492
        # check that the index used is the first one written. (Specific
363
493
        # to KnitIndex style indices.
364
494
        self.assertEqual("1", index._version_list_to_index(["version"]))
365
 
        self.assertEqual((3, 4), index.get_position("version"))
 
495
        self.assertEqual((None, 3, 4), index.get_position("version"))
366
496
        self.assertEqual(["options3"], index.get_options("version"))
367
497
        self.assertEqual(["parent", "other"],
368
498
            index.get_parents_with_ghosts("version"))
385
515
            _KnitIndex.HEADER
386
516
            ])
387
517
        index = self.get_knit_index(transport, "filename", "r")
388
 
        index.add_version(utf8_revision_id, ["option"], 0, 1, [])
 
518
        index.add_version(utf8_revision_id, ["option"], (None, 0, 1), [])
389
519
        self.assertEqual(("append_bytes", ("filename",
390
520
            "\n%s option 0 1  :" % (utf8_revision_id,)),
391
521
            {}),
398
528
            _KnitIndex.HEADER
399
529
            ])
400
530
        index = self.get_knit_index(transport, "filename", "r")
401
 
        index.add_version("version", ["option"], 0, 1, [utf8_revision_id])
 
531
        index.add_version("version", ["option"], (None, 0, 1), [utf8_revision_id])
402
532
        self.assertEqual(("append_bytes", ("filename",
403
533
            "\nversion option 0 1 .%s :" % (utf8_revision_id,)),
404
534
            {}),
409
539
        index = self.get_knit_index(transport, "filename", "w", create=True)
410
540
        self.assertEqual([], index.get_graph())
411
541
 
412
 
        index.add_version("a", ["option"], 0, 1, ["b"])
 
542
        index.add_version("a", ["option"], (None, 0, 1), ["b"])
413
543
        self.assertEqual([("a", ["b"])], index.get_graph())
414
544
 
415
 
        index.add_version("c", ["option"], 0, 1, ["d"])
 
545
        index.add_version("c", ["option"], (None, 0, 1), ["d"])
416
546
        self.assertEqual([("a", ["b"]), ("c", ["d"])],
417
547
            sorted(index.get_graph()))
418
548
 
469
599
        transport = MockTransport()
470
600
        index = self.get_knit_index(transport, "filename", "w", create=True)
471
601
        # no parents
472
 
        index.add_version('r0', ['option'], 0, 1, [])
 
602
        index.add_version('r0', ['option'], (None, 0, 1), [])
473
603
        # 1 parent
474
 
        index.add_version('r1', ['option'], 0, 1, ['r0'])
 
604
        index.add_version('r1', ['option'], (None, 0, 1), ['r0'])
475
605
        # 2 parents
476
 
        index.add_version('r2', ['option'], 0, 1, ['r1', 'r0'])
 
606
        index.add_version('r2', ['option'], (None, 0, 1), ['r1', 'r0'])
477
607
        # XXX TODO a ghost
478
608
        # cases: each sample data individually:
479
609
        self.assertEqual(set([('r0', ())]),
504
634
        self.assertEqual(0, index.num_versions())
505
635
        self.assertEqual(0, len(index))
506
636
 
507
 
        index.add_version("a", ["option"], 0, 1, [])
508
 
        self.assertEqual(1, index.num_versions())
509
 
        self.assertEqual(1, len(index))
510
 
 
511
 
        index.add_version("a", ["option2"], 1, 2, [])
512
 
        self.assertEqual(1, index.num_versions())
513
 
        self.assertEqual(1, len(index))
514
 
 
515
 
        index.add_version("b", ["option"], 0, 1, [])
 
637
        index.add_version("a", ["option"], (None, 0, 1), [])
 
638
        self.assertEqual(1, index.num_versions())
 
639
        self.assertEqual(1, len(index))
 
640
 
 
641
        index.add_version("a", ["option2"], (None, 1, 2), [])
 
642
        self.assertEqual(1, index.num_versions())
 
643
        self.assertEqual(1, len(index))
 
644
 
 
645
        index.add_version("b", ["option"], (None, 0, 1), [])
516
646
        self.assertEqual(2, index.num_versions())
517
647
        self.assertEqual(2, len(index))
518
648
 
524
654
 
525
655
        self.assertEqual([], index.get_versions())
526
656
 
527
 
        index.add_version("a", ["option"], 0, 1, [])
528
 
        self.assertEqual(["a"], index.get_versions())
529
 
 
530
 
        index.add_version("a", ["option"], 0, 1, [])
531
 
        self.assertEqual(["a"], index.get_versions())
532
 
 
533
 
        index.add_version("b", ["option"], 0, 1, [])
 
657
        index.add_version("a", ["option"], (None, 0, 1), [])
 
658
        self.assertEqual(["a"], index.get_versions())
 
659
 
 
660
        index.add_version("a", ["option"], (None, 0, 1), [])
 
661
        self.assertEqual(["a"], index.get_versions())
 
662
 
 
663
        index.add_version("b", ["option"], (None, 0, 1), [])
534
664
        self.assertEqual(["a", "b"], index.get_versions())
535
665
 
536
666
    def test_add_version(self):
539
669
            ])
540
670
        index = self.get_knit_index(transport, "filename", "r")
541
671
 
542
 
        index.add_version("a", ["option"], 0, 1, ["b"])
 
672
        index.add_version("a", ["option"], (None, 0, 1), ["b"])
543
673
        self.assertEqual(("append_bytes",
544
674
            ("filename", "\na option 0 1 .b :"),
545
675
            {}), transport.calls.pop(0))
546
676
        self.assertTrue(index.has_version("a"))
547
677
        self.assertEqual(1, index.num_versions())
548
 
        self.assertEqual((0, 1), index.get_position("a"))
 
678
        self.assertEqual((None, 0, 1), index.get_position("a"))
549
679
        self.assertEqual(["option"], index.get_options("a"))
550
680
        self.assertEqual(["b"], index.get_parents_with_ghosts("a"))
551
681
 
552
 
        index.add_version("a", ["opt"], 1, 2, ["c"])
 
682
        index.add_version("a", ["opt"], (None, 1, 2), ["c"])
553
683
        self.assertEqual(("append_bytes",
554
684
            ("filename", "\na opt 1 2 .c :"),
555
685
            {}), transport.calls.pop(0))
556
686
        self.assertTrue(index.has_version("a"))
557
687
        self.assertEqual(1, index.num_versions())
558
 
        self.assertEqual((1, 2), index.get_position("a"))
 
688
        self.assertEqual((None, 1, 2), index.get_position("a"))
559
689
        self.assertEqual(["opt"], index.get_options("a"))
560
690
        self.assertEqual(["c"], index.get_parents_with_ghosts("a"))
561
691
 
562
 
        index.add_version("b", ["option"], 2, 3, ["a"])
 
692
        index.add_version("b", ["option"], (None, 2, 3), ["a"])
563
693
        self.assertEqual(("append_bytes",
564
694
            ("filename", "\nb option 2 3 0 :"),
565
695
            {}), transport.calls.pop(0))
566
696
        self.assertTrue(index.has_version("b"))
567
697
        self.assertEqual(2, index.num_versions())
568
 
        self.assertEqual((2, 3), index.get_position("b"))
 
698
        self.assertEqual((None, 2, 3), index.get_position("b"))
569
699
        self.assertEqual(["option"], index.get_options("b"))
570
700
        self.assertEqual(["a"], index.get_parents_with_ghosts("b"))
571
701
 
576
706
        index = self.get_knit_index(transport, "filename", "r")
577
707
 
578
708
        index.add_versions([
579
 
            ("a", ["option"], 0, 1, ["b"]),
580
 
            ("a", ["opt"], 1, 2, ["c"]),
581
 
            ("b", ["option"], 2, 3, ["a"])
 
709
            ("a", ["option"], (None, 0, 1), ["b"]),
 
710
            ("a", ["opt"], (None, 1, 2), ["c"]),
 
711
            ("b", ["option"], (None, 2, 3), ["a"])
582
712
            ])
583
713
        self.assertEqual(("append_bytes", ("filename",
584
714
            "\na option 0 1 .b :"
588
718
        self.assertTrue(index.has_version("a"))
589
719
        self.assertTrue(index.has_version("b"))
590
720
        self.assertEqual(2, index.num_versions())
591
 
        self.assertEqual((1, 2), index.get_position("a"))
592
 
        self.assertEqual((2, 3), index.get_position("b"))
 
721
        self.assertEqual((None, 1, 2), index.get_position("a"))
 
722
        self.assertEqual((None, 2, 3), index.get_position("b"))
593
723
        self.assertEqual(["opt"], index.get_options("a"))
594
724
        self.assertEqual(["option"], index.get_options("b"))
595
725
        self.assertEqual(["c"], index.get_parents_with_ghosts("a"))
604
734
        self.assertEqual([], transport.calls)
605
735
 
606
736
        index.add_versions([
607
 
            ("a", ["option"], 0, 1, ["b"]),
608
 
            ("a", ["opt"], 1, 2, ["c"]),
609
 
            ("b", ["option"], 2, 3, ["a"])
 
737
            ("a", ["option"], (None, 0, 1), ["b"]),
 
738
            ("a", ["opt"], (None, 1, 2), ["c"]),
 
739
            ("b", ["option"], (None, 2, 3), ["a"])
610
740
            ])
611
741
        name, (filename, f), kwargs = transport.calls.pop(0)
612
742
        self.assertEqual("put_file_non_atomic", name)
639
769
            ])
640
770
        index = self.get_knit_index(transport, "filename", "r")
641
771
 
642
 
        self.assertEqual((0, 1), index.get_position("a"))
643
 
        self.assertEqual((1, 2), index.get_position("b"))
 
772
        self.assertEqual((None, 0, 1), index.get_position("a"))
 
773
        self.assertEqual((None, 1, 2), index.get_position("b"))
644
774
 
645
775
    def test_get_method(self):
646
776
        transport = MockTransport([
1017
1147
        self.assertEqualDiff(''.join(k.get_lines('text-1a')), TEXT_1A)
1018
1148
        # check the index had the right data added.
1019
1149
        self.assertEqual(set([
1020
 
            (('text-1', ), ' 0 127', ((), ())),
1021
 
            (('text-1a', ), ' 127 140', ((('text-1', ),), (('text-1', ),))),
 
1150
            (index, ('text-1', ), ' 0 127', ((), ())),
 
1151
            (index, ('text-1a', ), ' 127 140', ((('text-1', ),), (('text-1', ),))),
1022
1152
            ]), set(index.iter_all_entries()))
1023
1153
        # we should not have a .kndx file
1024
1154
        self.assertFalse(get_transport('.').has('test.kndx'))
1461
1591
 
1462
1592
        def read_one_raw(version):
1463
1593
            pos_map = k._get_components_positions([version])
1464
 
            method, pos, size, next = pos_map[version]
1465
 
            lst = list(k._data.read_records_iter_raw([(version, pos, size)]))
 
1594
            method, index_memo, next = pos_map[version]
 
1595
            lst = list(k._data.read_records_iter_raw([(version, index_memo)]))
1466
1596
            self.assertEqual(1, len(lst))
1467
1597
            return lst[0]
1468
1598
 
1482
1612
 
1483
1613
        def read_one(version):
1484
1614
            pos_map = k._get_components_positions([version])
1485
 
            method, pos, size, next = pos_map[version]
1486
 
            lst = list(k._data.read_records_iter([(version, pos, size)]))
 
1615
            method, index_memo, next = pos_map[version]
 
1616
            lst = list(k._data.read_records_iter([(version, index_memo)]))
1487
1617
            self.assertEqual(1, len(lst))
1488
1618
            return lst[0]
1489
1619
 
1527
1657
        """Adding versions to the index should update the lookup dict"""
1528
1658
        knit = self.make_test_knit()
1529
1659
        idx = knit._index
1530
 
        idx.add_version('a-1', ['fulltext'], 0, 0, [])
 
1660
        idx.add_version('a-1', ['fulltext'], (None, 0, 0), [])
1531
1661
        self.check_file_contents('test.kndx',
1532
1662
            '# bzr knit index 8\n'
1533
1663
            '\n'
1534
1664
            'a-1 fulltext 0 0  :'
1535
1665
            )
1536
 
        idx.add_versions([('a-2', ['fulltext'], 0, 0, ['a-1']),
1537
 
                          ('a-3', ['fulltext'], 0, 0, ['a-2']),
 
1666
        idx.add_versions([('a-2', ['fulltext'], (None, 0, 0), ['a-1']),
 
1667
                          ('a-3', ['fulltext'], (None, 0, 0), ['a-2']),
1538
1668
                         ])
1539
1669
        self.check_file_contents('test.kndx',
1540
1670
            '# bzr knit index 8\n'
1563
1693
 
1564
1694
        knit = self.make_test_knit()
1565
1695
        idx = knit._index
1566
 
        idx.add_version('a-1', ['fulltext'], 0, 0, [])
 
1696
        idx.add_version('a-1', ['fulltext'], (None, 0, 0), [])
1567
1697
 
1568
1698
        class StopEarly(Exception):
1569
1699
            pass
1570
1700
 
1571
1701
        def generate_failure():
1572
1702
            """Add some entries and then raise an exception"""
1573
 
            yield ('a-2', ['fulltext'], 0, 0, ['a-1'])
1574
 
            yield ('a-3', ['fulltext'], 0, 0, ['a-2'])
 
1703
            yield ('a-2', ['fulltext'], (None, 0, 0), ['a-1'])
 
1704
            yield ('a-3', ['fulltext'], (None, 0, 0), ['a-2'])
1575
1705
            raise StopEarly()
1576
1706
 
1577
1707
        # Assert the pre-condition
1719
1849
 
1720
1850
    def test_get_position(self):
1721
1851
        index = self.two_graph_index()
1722
 
        self.assertEqual((0, 100), index.get_position('tip'))
1723
 
        self.assertEqual((100, 78), index.get_position('parent'))
 
1852
        self.assertEqual((index._graph_index._indices[0], 0, 100), index.get_position('tip'))
 
1853
        self.assertEqual((index._graph_index._indices[1], 100, 78), index.get_position('parent'))
1724
1854
 
1725
1855
    def test_get_method_deltas(self):
1726
1856
        index = self.two_graph_index(deltas=True)
1774
1904
    def test_add_no_callback_errors(self):
1775
1905
        index = self.two_graph_index()
1776
1906
        self.assertRaises(errors.ReadOnlyError, index.add_version,
1777
 
            'new', 'fulltext,no-eol', 50, 60, ['separate'])
 
1907
            'new', 'fulltext,no-eol', (None, 50, 60), ['separate'])
1778
1908
 
1779
1909
    def test_add_version_smoke(self):
1780
1910
        index = self.two_graph_index(catch_adds=True)
1781
 
        index.add_version('new', 'fulltext,no-eol', 50, 60, ['separate'])
 
1911
        index.add_version('new', 'fulltext,no-eol', (None, 50, 60), ['separate'])
1782
1912
        self.assertEqual([[(('new', ), 'N50 60', ((('separate',),),))]],
1783
1913
            self.caught_entries)
1784
1914
 
1785
1915
    def test_add_version_delta_not_delta_index(self):
1786
1916
        index = self.two_graph_index(catch_adds=True)
1787
1917
        self.assertRaises(errors.KnitCorrupt, index.add_version,
1788
 
            'new', 'no-eol,line-delta', 0, 100, ['parent'])
 
1918
            'new', 'no-eol,line-delta', (None, 0, 100), ['parent'])
1789
1919
        self.assertEqual([], self.caught_entries)
1790
1920
 
1791
1921
    def test_add_version_same_dup(self):
1792
1922
        index = self.two_graph_index(catch_adds=True)
1793
1923
        # options can be spelt two different ways
1794
 
        index.add_version('tip', 'fulltext,no-eol', 0, 100, ['parent'])
1795
 
        index.add_version('tip', 'no-eol,fulltext', 0, 100, ['parent'])
 
1924
        index.add_version('tip', 'fulltext,no-eol', (None, 0, 100), ['parent'])
 
1925
        index.add_version('tip', 'no-eol,fulltext', (None, 0, 100), ['parent'])
1796
1926
        # but neither should have added data.
1797
1927
        self.assertEqual([[], []], self.caught_entries)
1798
1928
        
1800
1930
        index = self.two_graph_index(deltas=True, catch_adds=True)
1801
1931
        # change options
1802
1932
        self.assertRaises(errors.KnitCorrupt, index.add_version,
1803
 
            'tip', 'no-eol,line-delta', 0, 100, ['parent'])
1804
 
        self.assertRaises(errors.KnitCorrupt, index.add_version,
1805
 
            'tip', 'line-delta,no-eol', 0, 100, ['parent'])
1806
 
        self.assertRaises(errors.KnitCorrupt, index.add_version,
1807
 
            'tip', 'fulltext', 0, 100, ['parent'])
 
1933
            'tip', 'no-eol,line-delta', (None, 0, 100), ['parent'])
 
1934
        self.assertRaises(errors.KnitCorrupt, index.add_version,
 
1935
            'tip', 'line-delta,no-eol', (None, 0, 100), ['parent'])
 
1936
        self.assertRaises(errors.KnitCorrupt, index.add_version,
 
1937
            'tip', 'fulltext', (None, 0, 100), ['parent'])
1808
1938
        # position/length
1809
1939
        self.assertRaises(errors.KnitCorrupt, index.add_version,
1810
 
            'tip', 'fulltext,no-eol', 50, 100, ['parent'])
 
1940
            'tip', 'fulltext,no-eol', (None, 50, 100), ['parent'])
1811
1941
        self.assertRaises(errors.KnitCorrupt, index.add_version,
1812
 
            'tip', 'fulltext,no-eol', 0, 1000, ['parent'])
 
1942
            'tip', 'fulltext,no-eol', (None, 0, 1000), ['parent'])
1813
1943
        # parents
1814
1944
        self.assertRaises(errors.KnitCorrupt, index.add_version,
1815
 
            'tip', 'fulltext,no-eol', 0, 100, [])
 
1945
            'tip', 'fulltext,no-eol', (None, 0, 100), [])
1816
1946
        self.assertEqual([], self.caught_entries)
1817
1947
        
1818
1948
    def test_add_versions_nodeltas(self):
1819
1949
        index = self.two_graph_index(catch_adds=True)
1820
1950
        index.add_versions([
1821
 
                ('new', 'fulltext,no-eol', 50, 60, ['separate']),
1822
 
                ('new2', 'fulltext', 0, 6, ['new']),
 
1951
                ('new', 'fulltext,no-eol', (None, 50, 60), ['separate']),
 
1952
                ('new2', 'fulltext', (None, 0, 6), ['new']),
1823
1953
                ])
1824
1954
        self.assertEqual([(('new', ), 'N50 60', ((('separate',),),)),
1825
1955
            (('new2', ), ' 0 6', ((('new',),),))],
1829
1959
    def test_add_versions_deltas(self):
1830
1960
        index = self.two_graph_index(deltas=True, catch_adds=True)
1831
1961
        index.add_versions([
1832
 
                ('new', 'fulltext,no-eol', 50, 60, ['separate']),
1833
 
                ('new2', 'line-delta', 0, 6, ['new']),
 
1962
                ('new', 'fulltext,no-eol', (None, 50, 60), ['separate']),
 
1963
                ('new2', 'line-delta', (None, 0, 6), ['new']),
1834
1964
                ])
1835
1965
        self.assertEqual([(('new', ), 'N50 60', ((('separate',),), ())),
1836
1966
            (('new2', ), ' 0 6', ((('new',),), (('new',),), ))],
1840
1970
    def test_add_versions_delta_not_delta_index(self):
1841
1971
        index = self.two_graph_index(catch_adds=True)
1842
1972
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
1843
 
            [('new', 'no-eol,line-delta', 0, 100, ['parent'])])
 
1973
            [('new', 'no-eol,line-delta', (None, 0, 100), ['parent'])])
1844
1974
        self.assertEqual([], self.caught_entries)
1845
1975
 
1846
1976
    def test_add_versions_same_dup(self):
1847
1977
        index = self.two_graph_index(catch_adds=True)
1848
1978
        # options can be spelt two different ways
1849
 
        index.add_versions([('tip', 'fulltext,no-eol', 0, 100, ['parent'])])
1850
 
        index.add_versions([('tip', 'no-eol,fulltext', 0, 100, ['parent'])])
 
1979
        index.add_versions([('tip', 'fulltext,no-eol', (None, 0, 100), ['parent'])])
 
1980
        index.add_versions([('tip', 'no-eol,fulltext', (None, 0, 100), ['parent'])])
1851
1981
        # but neither should have added data.
1852
1982
        self.assertEqual([[], []], self.caught_entries)
1853
1983
        
1855
1985
        index = self.two_graph_index(deltas=True, catch_adds=True)
1856
1986
        # change options
1857
1987
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
1858
 
            [('tip', 'no-eol,line-delta', 0, 100, ['parent'])])
1859
 
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
1860
 
            [('tip', 'line-delta,no-eol', 0, 100, ['parent'])])
1861
 
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
1862
 
            [('tip', 'fulltext', 0, 100, ['parent'])])
 
1988
            [('tip', 'no-eol,line-delta', (None, 0, 100), ['parent'])])
 
1989
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
 
1990
            [('tip', 'line-delta,no-eol', (None, 0, 100), ['parent'])])
 
1991
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
 
1992
            [('tip', 'fulltext', (None, 0, 100), ['parent'])])
1863
1993
        # position/length
1864
1994
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
1865
 
            [('tip', 'fulltext,no-eol', 50, 100, ['parent'])])
 
1995
            [('tip', 'fulltext,no-eol', (None, 50, 100), ['parent'])])
1866
1996
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
1867
 
            [('tip', 'fulltext,no-eol', 0, 1000, ['parent'])])
 
1997
            [('tip', 'fulltext,no-eol', (None, 0, 1000), ['parent'])])
1868
1998
        # parents
1869
1999
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
1870
 
            [('tip', 'fulltext,no-eol', 0, 100, [])])
 
2000
            [('tip', 'fulltext,no-eol', (None, 0, 100), [])])
1871
2001
        # change options in the second record
1872
2002
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
1873
 
            [('tip', 'fulltext,no-eol', 0, 100, ['parent']),
1874
 
             ('tip', 'no-eol,line-delta', 0, 100, ['parent'])])
 
2003
            [('tip', 'fulltext,no-eol', (None, 0, 100), ['parent']),
 
2004
             ('tip', 'no-eol,line-delta', (None, 0, 100), ['parent'])])
1875
2005
        self.assertEqual([], self.caught_entries)
1876
2006
 
1877
2007
    def test_iter_parents(self):
2002
2132
 
2003
2133
    def test_get_position(self):
2004
2134
        index = self.two_graph_index()
2005
 
        self.assertEqual((0, 100), index.get_position('tip'))
2006
 
        self.assertEqual((100, 78), index.get_position('parent'))
 
2135
        self.assertEqual((index._graph_index._indices[0], 0, 100), index.get_position('tip'))
 
2136
        self.assertEqual((index._graph_index._indices[1], 100, 78), index.get_position('parent'))
2007
2137
 
2008
2138
    def test_get_method(self):
2009
2139
        index = self.two_graph_index()
2043
2173
    def test_add_no_callback_errors(self):
2044
2174
        index = self.two_graph_index()
2045
2175
        self.assertRaises(errors.ReadOnlyError, index.add_version,
2046
 
            'new', 'fulltext,no-eol', 50, 60, ['separate'])
 
2176
            'new', 'fulltext,no-eol', (None, 50, 60), ['separate'])
2047
2177
 
2048
2178
    def test_add_version_smoke(self):
2049
2179
        index = self.two_graph_index(catch_adds=True)
2050
 
        index.add_version('new', 'fulltext,no-eol', 50, 60, [])
 
2180
        index.add_version('new', 'fulltext,no-eol', (None, 50, 60), [])
2051
2181
        self.assertEqual([[(('new', ), 'N50 60')]],
2052
2182
            self.caught_entries)
2053
2183
 
2054
2184
    def test_add_version_delta_not_delta_index(self):
2055
2185
        index = self.two_graph_index(catch_adds=True)
2056
2186
        self.assertRaises(errors.KnitCorrupt, index.add_version,
2057
 
            'new', 'no-eol,line-delta', 0, 100, [])
 
2187
            'new', 'no-eol,line-delta', (None, 0, 100), [])
2058
2188
        self.assertEqual([], self.caught_entries)
2059
2189
 
2060
2190
    def test_add_version_same_dup(self):
2061
2191
        index = self.two_graph_index(catch_adds=True)
2062
2192
        # options can be spelt two different ways
2063
 
        index.add_version('tip', 'fulltext,no-eol', 0, 100, [])
2064
 
        index.add_version('tip', 'no-eol,fulltext', 0, 100, [])
 
2193
        index.add_version('tip', 'fulltext,no-eol', (None, 0, 100), [])
 
2194
        index.add_version('tip', 'no-eol,fulltext', (None, 0, 100), [])
2065
2195
        # but neither should have added data.
2066
2196
        self.assertEqual([[], []], self.caught_entries)
2067
2197
        
2069
2199
        index = self.two_graph_index(catch_adds=True)
2070
2200
        # change options
2071
2201
        self.assertRaises(errors.KnitCorrupt, index.add_version,
2072
 
            'tip', 'no-eol,line-delta', 0, 100, [])
2073
 
        self.assertRaises(errors.KnitCorrupt, index.add_version,
2074
 
            'tip', 'line-delta,no-eol', 0, 100, [])
2075
 
        self.assertRaises(errors.KnitCorrupt, index.add_version,
2076
 
            'tip', 'fulltext', 0, 100, [])
 
2202
            'tip', 'no-eol,line-delta', (None, 0, 100), [])
 
2203
        self.assertRaises(errors.KnitCorrupt, index.add_version,
 
2204
            'tip', 'line-delta,no-eol', (None, 0, 100), [])
 
2205
        self.assertRaises(errors.KnitCorrupt, index.add_version,
 
2206
            'tip', 'fulltext', (None, 0, 100), [])
2077
2207
        # position/length
2078
2208
        self.assertRaises(errors.KnitCorrupt, index.add_version,
2079
 
            'tip', 'fulltext,no-eol', 50, 100, [])
 
2209
            'tip', 'fulltext,no-eol', (None, 50, 100), [])
2080
2210
        self.assertRaises(errors.KnitCorrupt, index.add_version,
2081
 
            'tip', 'fulltext,no-eol', 0, 1000, [])
 
2211
            'tip', 'fulltext,no-eol', (None, 0, 1000), [])
2082
2212
        # parents
2083
2213
        self.assertRaises(errors.KnitCorrupt, index.add_version,
2084
 
            'tip', 'fulltext,no-eol', 0, 100, ['parent'])
 
2214
            'tip', 'fulltext,no-eol', (None, 0, 100), ['parent'])
2085
2215
        self.assertEqual([], self.caught_entries)
2086
2216
        
2087
2217
    def test_add_versions(self):
2088
2218
        index = self.two_graph_index(catch_adds=True)
2089
2219
        index.add_versions([
2090
 
                ('new', 'fulltext,no-eol', 50, 60, []),
2091
 
                ('new2', 'fulltext', 0, 6, []),
 
2220
                ('new', 'fulltext,no-eol', (None, 50, 60), []),
 
2221
                ('new2', 'fulltext', (None, 0, 6), []),
2092
2222
                ])
2093
2223
        self.assertEqual([(('new', ), 'N50 60'), (('new2', ), ' 0 6')],
2094
2224
            sorted(self.caught_entries[0]))
2097
2227
    def test_add_versions_delta_not_delta_index(self):
2098
2228
        index = self.two_graph_index(catch_adds=True)
2099
2229
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
2100
 
            [('new', 'no-eol,line-delta', 0, 100, ['parent'])])
 
2230
            [('new', 'no-eol,line-delta', (None, 0, 100), ['parent'])])
2101
2231
        self.assertEqual([], self.caught_entries)
2102
2232
 
2103
2233
    def test_add_versions_parents_not_parents_index(self):
2104
2234
        index = self.two_graph_index(catch_adds=True)
2105
2235
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
2106
 
            [('new', 'no-eol,fulltext', 0, 100, ['parent'])])
 
2236
            [('new', 'no-eol,fulltext', (None, 0, 100), ['parent'])])
2107
2237
        self.assertEqual([], self.caught_entries)
2108
2238
 
2109
2239
    def test_add_versions_same_dup(self):
2110
2240
        index = self.two_graph_index(catch_adds=True)
2111
2241
        # options can be spelt two different ways
2112
 
        index.add_versions([('tip', 'fulltext,no-eol', 0, 100, [])])
2113
 
        index.add_versions([('tip', 'no-eol,fulltext', 0, 100, [])])
 
2242
        index.add_versions([('tip', 'fulltext,no-eol', (None, 0, 100), [])])
 
2243
        index.add_versions([('tip', 'no-eol,fulltext', (None, 0, 100), [])])
2114
2244
        # but neither should have added data.
2115
2245
        self.assertEqual([[], []], self.caught_entries)
2116
2246
        
2118
2248
        index = self.two_graph_index(catch_adds=True)
2119
2249
        # change options
2120
2250
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
2121
 
            [('tip', 'no-eol,line-delta', 0, 100, [])])
2122
 
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
2123
 
            [('tip', 'line-delta,no-eol', 0, 100, [])])
2124
 
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
2125
 
            [('tip', 'fulltext', 0, 100, [])])
 
2251
            [('tip', 'no-eol,line-delta', (None, 0, 100), [])])
 
2252
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
 
2253
            [('tip', 'line-delta,no-eol', (None, 0, 100), [])])
 
2254
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
 
2255
            [('tip', 'fulltext', (None, 0, 100), [])])
2126
2256
        # position/length
2127
2257
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
2128
 
            [('tip', 'fulltext,no-eol', 50, 100, [])])
 
2258
            [('tip', 'fulltext,no-eol', (None, 50, 100), [])])
2129
2259
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
2130
 
            [('tip', 'fulltext,no-eol', 0, 1000, [])])
 
2260
            [('tip', 'fulltext,no-eol', (None, 0, 1000), [])])
2131
2261
        # parents
2132
2262
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
2133
 
            [('tip', 'fulltext,no-eol', 0, 100, ['parent'])])
 
2263
            [('tip', 'fulltext,no-eol', (None, 0, 100), ['parent'])])
2134
2264
        # change options in the second record
2135
2265
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
2136
 
            [('tip', 'fulltext,no-eol', 0, 100, []),
2137
 
             ('tip', 'no-eol,line-delta', 0, 100, [])])
 
2266
            [('tip', 'fulltext,no-eol', (None, 0, 100), []),
 
2267
             ('tip', 'no-eol,line-delta', (None, 0, 100), [])])
2138
2268
        self.assertEqual([], self.caught_entries)
2139
2269
 
2140
2270
    def test_iter_parents(self):