/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/tests/test_knit.py

  • Committer: Jelmer Vernooij
  • Date: 2019-03-04 00:16:27 UTC
  • mfrom: (7293 work)
  • mto: This revision was merged to the branch mainline in revision 7318.
  • Revision ID: jelmer@jelmer.uk-20190304001627-v6u7o6pf97tukhek
Merge trunk.

Show diffs side-by-side

added added

removed removed

Lines of Context:
111
111
        content = self._make_content([])
112
112
        self.assertEqual(content.text(), [])
113
113
 
114
 
        content = self._make_content([(b"origin1", b"text1"), (b"origin2", b"text2")])
 
114
        content = self._make_content(
 
115
            [(b"origin1", b"text1"), (b"origin2", b"text2")])
115
116
        self.assertEqual(content.text(), [b"text1", b"text2"])
116
117
 
117
118
    def test_copy(self):
118
 
        content = self._make_content([(b"origin1", b"text1"), (b"origin2", b"text2")])
 
119
        content = self._make_content(
 
120
            [(b"origin1", b"text1"), (b"origin2", b"text2")])
119
121
        copy = content.copy()
120
122
        self.assertIsInstance(copy, content.__class__)
121
123
        self.assertEqual(copy.annotate(), content.annotate())
124
126
        """Assert that the derived matching blocks match real output"""
125
127
        source_lines = source.splitlines(True)
126
128
        target_lines = target.splitlines(True)
 
129
 
127
130
        def nl(line):
128
131
            if noeol and not line.endswith('\n'):
129
132
                return line + '\n'
130
133
            else:
131
134
                return line
132
 
        source_content = self._make_content([(None, nl(l)) for l in source_lines])
133
 
        target_content = self._make_content([(None, nl(l)) for l in target_lines])
 
135
        source_content = self._make_content(
 
136
            [(None, nl(l)) for l in source_lines])
 
137
        target_content = self._make_content(
 
138
            [(None, nl(l)) for l in target_lines])
134
139
        line_delta = source_content.line_delta(target_content)
135
140
        delta_blocks = list(KnitContent.get_line_delta_blocks(line_delta,
136
 
            source_lines, target_lines))
 
141
                                                              source_lines, target_lines))
137
142
        matcher = PatienceSequenceMatcher(None, source_lines, target_lines)
138
143
        matcher_blocks = list(matcher.get_matching_blocks())
139
144
        self.assertEqual(matcher_blocks, delta_blocks)
220
225
        content = self._make_content([])
221
226
        self.assertEqual(content.annotate(), [])
222
227
 
223
 
        content = self._make_content([("origin1", "text1"), ("origin2", "text2")])
 
228
        content = self._make_content(
 
229
            [("origin1", "text1"), ("origin2", "text2")])
224
230
        self.assertEqual(content.annotate(),
225
 
            [("bogus", "text1"), ("bogus", "text2")])
 
231
                         [("bogus", "text1"), ("bogus", "text2")])
226
232
 
227
233
    def test_line_delta(self):
228
234
        content1 = self._make_content([("", "a"), ("", "b")])
229
235
        content2 = self._make_content([("", "a"), ("", "a"), ("", "c")])
230
236
        self.assertEqual(content1.line_delta(content2),
231
 
            [(1, 2, 2, ["a", "c"])])
 
237
                         [(1, 2, 2, ["a", "c"])])
232
238
 
233
239
    def test_line_delta_iter(self):
234
240
        content1 = self._make_content([("", "a"), ("", "b")])
247
253
        content = self._make_content([])
248
254
        self.assertEqual(content.annotate(), [])
249
255
 
250
 
        content = self._make_content([(b"origin1", b"text1"), (b"origin2", b"text2")])
 
256
        content = self._make_content(
 
257
            [(b"origin1", b"text1"), (b"origin2", b"text2")])
251
258
        self.assertEqual(content.annotate(),
252
 
            [(b"origin1", b"text1"), (b"origin2", b"text2")])
 
259
                         [(b"origin1", b"text1"), (b"origin2", b"text2")])
253
260
 
254
261
    def test_line_delta(self):
255
262
        content1 = self._make_content([("", "a"), ("", "b")])
256
263
        content2 = self._make_content([("", "a"), ("", "a"), ("", "c")])
257
264
        self.assertEqual(content1.line_delta(content2),
258
 
            [(1, 2, 2, [("", "a"), ("", "c")])])
 
265
                         [(1, 2, 2, [("", "a"), ("", "c")])])
259
266
 
260
267
    def test_line_delta_iter(self):
261
268
        content1 = self._make_content([("", "a"), ("", "b")])
322
329
        """add_raw_records with many records and read some back."""
323
330
        access = self.get_access()
324
331
        memos = access.add_raw_records([(b'key', 10), (b'key2', 2), (b'key3', 5)],
325
 
            b'12345678901234567')
 
332
                                       b'12345678901234567')
326
333
        self.assertEqual([b'1234567890', b'12', b'34567'],
327
 
            list(access.get_raw_records(memos)))
 
334
                         list(access.get_raw_records(memos)))
328
335
        self.assertEqual([b'1234567890'],
329
 
            list(access.get_raw_records(memos[0:1])))
 
336
                         list(access.get_raw_records(memos[0:1])))
330
337
        self.assertEqual([b'12'],
331
 
            list(access.get_raw_records(memos[1:2])))
 
338
                         list(access.get_raw_records(memos[1:2])))
332
339
        self.assertEqual([b'34567'],
333
 
            list(access.get_raw_records(memos[2:3])))
 
340
                         list(access.get_raw_records(memos[2:3])))
334
341
        self.assertEqual([b'1234567890', b'34567'],
335
 
            list(access.get_raw_records(memos[0:1] + memos[2:3])))
 
342
                         list(access.get_raw_records(memos[0:1] + memos[2:3])))
336
343
 
337
344
 
338
345
class TestKnitKnitAccess(TestCaseWithMemoryTransport, KnitRecordAccessTestsMixin):
357
364
 
358
365
    def _get_access(self, packname='packfile', index='FOO'):
359
366
        transport = self.get_transport()
 
367
 
360
368
        def write_data(bytes):
361
369
            transport.append_bytes(packname, bytes)
362
370
        writer = pack.ContainerWriter(write_data)
443
451
        new_index = new_pack.revision_index
444
452
        access_tuple = new_pack.access_tuple()
445
453
        reload_counter = [0, 0, 0]
 
454
 
446
455
        def reload():
447
456
            reload_counter[0] += 1
448
457
            if reload_counter[1] > 0:
465
474
 
466
475
    def make_reload_func(self, return_val=True):
467
476
        reload_called = [0]
 
477
 
468
478
        def reload():
469
479
            reload_called[0] += 1
470
480
            return return_val
493
503
        memos.extend(access.add_raw_records([(b'key', 5)], b'alpha'))
494
504
        writer.end()
495
505
        transport = self.get_transport()
496
 
        access = pack_repo._DirectPackAccess({"FOO":(transport, 'packfile'),
497
 
            "FOOBAR":(transport, 'pack2'),
498
 
            "BAZ":(transport, 'pack3')})
 
506
        access = pack_repo._DirectPackAccess({"FOO": (transport, 'packfile'),
 
507
                                              "FOOBAR": (transport, 'pack2'),
 
508
                                              "BAZ": (transport, 'pack3')})
499
509
        self.assertEqual([b'1234567890', b'12345', b'alpha'],
500
 
            list(access.get_raw_records(memos)))
 
510
                         list(access.get_raw_records(memos)))
501
511
        self.assertEqual([b'1234567890'],
502
 
            list(access.get_raw_records(memos[0:1])))
 
512
                         list(access.get_raw_records(memos[0:1])))
503
513
        self.assertEqual([b'12345'],
504
 
            list(access.get_raw_records(memos[1:2])))
 
514
                         list(access.get_raw_records(memos[1:2])))
505
515
        self.assertEqual([b'alpha'],
506
 
            list(access.get_raw_records(memos[2:3])))
 
516
                         list(access.get_raw_records(memos[2:3])))
507
517
        self.assertEqual([b'1234567890', b'alpha'],
508
 
            list(access.get_raw_records(memos[0:1] + memos[2:3])))
 
518
                         list(access.get_raw_records(memos[0:1] + memos[2:3])))
509
519
 
510
520
    def test_set_writer(self):
511
521
        """The writer should be settable post construction."""
513
523
        transport = self.get_transport()
514
524
        packname = 'packfile'
515
525
        index = 'foo'
 
526
 
516
527
        def write_data(bytes):
517
528
            transport.append_bytes(packname, bytes)
518
529
        writer = pack.ContainerWriter(write_data)
527
538
        transport = self.get_transport()
528
539
        reload_called, reload_func = self.make_reload_func()
529
540
        # Note that the index key has changed from 'foo' to 'bar'
530
 
        access = pack_repo._DirectPackAccess({'bar':(transport, 'packname')},
531
 
                                   reload_func=reload_func)
 
541
        access = pack_repo._DirectPackAccess({'bar': (transport, 'packname')},
 
542
                                             reload_func=reload_func)
532
543
        e = self.assertListRaises(errors.RetryWithNewPacks,
533
544
                                  access.get_raw_records, memos)
534
545
        # Because a key was passed in which does not match our index list, we
542
553
        memos = self.make_pack_file()
543
554
        transport = self.get_transport()
544
555
        # Note that the index key has changed from 'foo' to 'bar'
545
 
        access = pack_repo._DirectPackAccess({'bar':(transport, 'packname')})
 
556
        access = pack_repo._DirectPackAccess({'bar': (transport, 'packname')})
546
557
        e = self.assertListRaises(KeyError, access.get_raw_records, memos)
547
558
 
548
559
    def test_missing_file_raises_retry(self):
551
562
        reload_called, reload_func = self.make_reload_func()
552
563
        # Note that the 'filename' has been changed to 'different-packname'
553
564
        access = pack_repo._DirectPackAccess(
554
 
            {'foo':(transport, 'different-packname')},
 
565
            {'foo': (transport, 'different-packname')},
555
566
            reload_func=reload_func)
556
567
        e = self.assertListRaises(errors.RetryWithNewPacks,
557
568
                                  access.get_raw_records, memos)
575
586
        memos = self.make_pack_file()
576
587
        transport = self.get_transport()
577
588
        failing_transport = MockReadvFailingTransport(
578
 
                                [transport.get_bytes('packname')])
 
589
            [transport.get_bytes('packname')])
579
590
        reload_called, reload_func = self.make_reload_func()
580
591
        access = pack_repo._DirectPackAccess(
581
592
            {'foo': (failing_transport, 'packname')},
582
593
            reload_func=reload_func)
583
594
        # Asking for a single record will not trigger the Mock failure
584
595
        self.assertEqual([b'1234567890'],
585
 
            list(access.get_raw_records(memos[:1])))
 
596
                         list(access.get_raw_records(memos[:1])))
586
597
        self.assertEqual([b'12345'],
587
 
            list(access.get_raw_records(memos[1:2])))
 
598
                         list(access.get_raw_records(memos[1:2])))
588
599
        # A multiple offset readv() will fail mid-way through
589
600
        e = self.assertListRaises(errors.RetryWithNewPacks,
590
601
                                  access.get_raw_records, memos)
599
610
        memos = self.make_pack_file()
600
611
        transport = self.get_transport()
601
612
        failing_transport = MockReadvFailingTransport(
602
 
                                [transport.get_bytes('packname')])
 
613
            [transport.get_bytes('packname')])
603
614
        reload_called, reload_func = self.make_reload_func()
604
615
        access = pack_repo._DirectPackAccess(
605
 
            {'foo':(failing_transport, 'packname')})
 
616
            {'foo': (failing_transport, 'packname')})
606
617
        # Asking for a single record will not trigger the Mock failure
607
618
        self.assertEqual([b'1234567890'],
608
 
            list(access.get_raw_records(memos[:1])))
 
619
                         list(access.get_raw_records(memos[:1])))
609
620
        self.assertEqual([b'12345'],
610
 
            list(access.get_raw_records(memos[1:2])))
 
621
                         list(access.get_raw_records(memos[1:2])))
611
622
        # A multiple offset readv() will fail mid-way through
612
623
        e = self.assertListRaises(errors.NoSuchFile,
613
624
                                  access.get_raw_records, memos)
624
635
        retry_exc = self.make_retry_exception()
625
636
        access.reload_or_raise(retry_exc)
626
637
        self.assertEqual([1], reload_called)
627
 
        retry_exc.reload_occurred=True
 
638
        retry_exc.reload_occurred = True
628
639
        access.reload_or_raise(retry_exc)
629
640
        self.assertEqual([2], reload_called)
630
641
 
636
647
        # reload_func() return False (no changes).
637
648
        self.assertRaises(_TestException, access.reload_or_raise, retry_exc)
638
649
        self.assertEqual([1], reload_called)
639
 
        retry_exc.reload_occurred=True
 
650
        retry_exc.reload_occurred = True
640
651
        # If reload_occurred is True, then we assume nothing changed because
641
652
        # it had changed earlier, but didn't change again
642
653
        access.reload_or_raise(retry_exc)
650
661
        reload_lines = vf.annotate(key)
651
662
        self.assertEqual([1, 1, 0], reload_counter)
652
663
        plain_lines = vf.annotate(key)
653
 
        self.assertEqual([1, 1, 0], reload_counter) # No extra reloading
 
664
        self.assertEqual([1, 1, 0], reload_counter)  # No extra reloading
654
665
        if reload_lines != plain_lines:
655
666
            self.fail('Annotation was not identical with reloading.')
656
667
        # Now delete the packs-in-use, which should trigger another reload, but
690
701
        for trans, name in vf._access._indices.values():
691
702
            trans.delete(name)
692
703
        self.assertListRaises(errors.NoSuchFile,
693
 
            vf.get_record_stream, keys, 'topological', False)
 
704
                              vf.get_record_stream, keys, 'topological', False)
694
705
 
695
706
    def test_iter_lines_added_or_present_in_keys_retries(self):
696
707
        vf, reload_counter = self.make_vf_for_retrying()
707
718
        self.assertEqual([1, 1, 0], reload_counter)
708
719
        # Now do it again, to make sure the result is equivalent
709
720
        plain_lines = sorted(vf.iter_lines_added_or_present_in_keys(keys))
710
 
        self.assertEqual([1, 1, 0], reload_counter) # No extra reloading
 
721
        self.assertEqual([1, 1, 0], reload_counter)  # No extra reloading
711
722
        self.assertEqual(plain_lines, reload_lines)
712
723
        self.assertEqual(21, len(plain_lines))
713
724
        # Now delete all pack files, and see that we raise the right error
714
725
        for trans, name in vf._access._indices.values():
715
726
            trans.delete(name)
716
727
        self.assertListRaises(errors.NoSuchFile,
717
 
            vf.iter_lines_added_or_present_in_keys, keys)
 
728
                              vf.iter_lines_added_or_present_in_keys, keys)
718
729
        self.assertEqual([2, 1, 1], reload_counter)
719
730
 
720
731
    def test_get_record_stream_yields_disk_sorted_order(self):
798
809
 
799
810
        contents = list(knit._read_records_iter(records))
800
811
        self.assertEqual([((b'rev-id-1',), [b'foo\n', b'bar\n'],
801
 
            b'4e48e2c9a3d2ca8a708cb0cc545700544efb5021')], contents)
 
812
                           b'4e48e2c9a3d2ca8a708cb0cc545700544efb5021')], contents)
802
813
 
803
814
        raw_contents = list(knit._read_records_iter_raw(records))
804
815
        self.assertEqual([((b'rev-id-1',), gz_txt, sha1sum)], raw_contents)
833
844
        knit = KnitVersionedFiles(None, access)
834
845
        records = [((b'rev-id-1',), ((b'rev-id-1',), 0, len(gz_txt)))]
835
846
        self.assertRaises(KnitCorrupt, list,
836
 
            knit._read_records_iter(records))
 
847
                          knit._read_records_iter(records))
837
848
 
838
849
        # read_records_iter_raw won't detect that sort of mismatch/corruption
839
850
        raw_contents = list(knit._read_records_iter_raw(records))
840
 
        self.assertEqual([((b'rev-id-1',),  gz_txt, sha1sum)], raw_contents)
 
851
        self.assertEqual([((b'rev-id-1',), gz_txt, sha1sum)], raw_contents)
841
852
 
842
853
    def test_too_many_lines(self):
843
854
        sha1sum = osutils.sha_string(b'foo\nbar\n')
852
863
        knit = KnitVersionedFiles(None, access)
853
864
        records = [((b'rev-id-1',), ((b'rev-id-1',), 0, len(gz_txt)))]
854
865
        self.assertRaises(KnitCorrupt, list,
855
 
            knit._read_records_iter(records))
 
866
                          knit._read_records_iter(records))
856
867
 
857
868
        # read_records_iter_raw won't detect that sort of mismatch/corruption
858
869
        raw_contents = list(knit._read_records_iter_raw(records))
871
882
        # We are asking for rev-id-2, but the data is rev-id-1
872
883
        records = [((b'rev-id-2',), ((b'rev-id-2',), 0, len(gz_txt)))]
873
884
        self.assertRaises(KnitCorrupt, list,
874
 
            knit._read_records_iter(records))
 
885
                          knit._read_records_iter(records))
875
886
 
876
887
        # read_records_iter_raw detects mismatches in the header
877
888
        self.assertRaises(KnitCorrupt, list,
878
 
            knit._read_records_iter_raw(records))
 
889
                          knit._read_records_iter_raw(records))
879
890
 
880
891
    def test_uncompressed_data(self):
881
892
        sha1sum = osutils.sha_string(b'foo\nbar\n')
891
902
 
892
903
        # We don't have valid gzip data ==> corrupt
893
904
        self.assertRaises(KnitCorrupt, list,
894
 
            knit._read_records_iter(records))
 
905
                          knit._read_records_iter(records))
895
906
 
896
907
        # read_records_iter_raw will notice the bad data
897
908
        self.assertRaises(KnitCorrupt, list,
898
 
            knit._read_records_iter_raw(records))
 
909
                          knit._read_records_iter_raw(records))
899
910
 
900
911
    def test_corrupted_data(self):
901
912
        sha1sum = osutils.sha_string(b'foo\nbar\n')
911
922
        knit = KnitVersionedFiles(None, access)
912
923
        records = [((b'rev-id-1',), ((b'rev-id-1',), 0, len(gz_txt)))]
913
924
        self.assertRaises(KnitCorrupt, list,
914
 
            knit._read_records_iter(records))
 
925
                          knit._read_records_iter(records))
915
926
        # read_records_iter_raw will barf on bad gz data
916
927
        self.assertRaises(KnitCorrupt, list,
917
 
            knit._read_records_iter_raw(records))
 
928
                          knit._read_records_iter_raw(records))
918
929
 
919
930
 
920
931
class LowLevelKnitIndexTests(TestCase):
927
938
    def get_knit_index(self, transport, name, mode):
928
939
        mapper = ConstantMapper(name)
929
940
        self.overrideAttr(knit, '_load_data', self._load_data)
930
 
        allow_writes = lambda: 'w' in mode
 
941
 
 
942
        def allow_writes():
 
943
            return 'w' in mode
931
944
        return _KndxIndex(transport, mapper, lambda: None, allow_writes, lambda: True)
932
945
 
933
946
    def test_create_file(self):
940
953
        self.assertEqual('filename.kndx', call[1][0])
941
954
        # With no history, _KndxIndex writes a new index:
942
955
        self.assertEqual(_KndxIndex.HEADER,
943
 
            call[1][1].getvalue())
 
956
                         call[1][1].getvalue())
944
957
        self.assertEqual({'create_parent_dir': True}, call[2])
945
958
 
946
959
    def test_read_utf8_version_id(self):
953
966
        index = self.get_knit_index(transport, "filename", "r")
954
967
        # _KndxIndex is a private class, and deals in utf8 revision_ids, not
955
968
        # Unicode revision_ids.
956
 
        self.assertEqual({(utf8_revision_id,):()},
957
 
            index.get_parent_map(index.keys()))
 
969
        self.assertEqual({(utf8_revision_id,): ()},
 
970
                         index.get_parent_map(index.keys()))
958
971
        self.assertFalse((unicode_revision_id,) in index.keys())
959
972
 
960
973
    def test_read_utf8_parents(self):
966
979
            ])
967
980
        index = self.get_knit_index(transport, "filename", "r")
968
981
        self.assertEqual({(b"version",): ((utf8_revision_id,),)},
969
 
            index.get_parent_map(index.keys()))
 
982
                         index.get_parent_map(index.keys()))
970
983
 
971
984
    def test_read_ignore_corrupted_lines(self):
972
985
        transport = MockTransport([
997
1010
        # check that the index used is the first one written. (Specific
998
1011
        # to KnitIndex style indices.
999
1012
        self.assertEqual(b"1", index._dictionary_compress([(b"version",)]))
1000
 
        self.assertEqual(((b"version",), 3, 4), index.get_position((b"version",)))
 
1013
        self.assertEqual(((b"version",), 3, 4),
 
1014
                         index.get_position((b"version",)))
1001
1015
        self.assertEqual([b"options3"], index.get_options((b"version",)))
1002
1016
        self.assertEqual({(b"version",): ((b"parent",), (b"other",))},
1003
 
            index.get_parent_map([(b"version",)]))
 
1017
                         index.get_parent_map([(b"version",)]))
1004
1018
 
1005
1019
    def test_read_compressed_parents(self):
1006
1020
        transport = MockTransport([
1010
1024
            b"c option 0 1 1 0 :",
1011
1025
            ])
1012
1026
        index = self.get_knit_index(transport, "filename", "r")
1013
 
        self.assertEqual({(b"b",):((b"a",),), (b"c",):((b"b",), (b"a",))},
1014
 
            index.get_parent_map([(b"b",), (b"c",)]))
 
1027
        self.assertEqual({(b"b",): ((b"a",),), (b"c",): ((b"b",), (b"a",))},
 
1028
                         index.get_parent_map([(b"b",), (b"c",)]))
1015
1029
 
1016
1030
    def test_write_utf8_version_id(self):
1017
1031
        unicode_revision_id = u"version-\N{CYRILLIC CAPITAL LETTER A}"
1028
1042
        self.assertEqual('filename.kndx', call[1][0])
1029
1043
        # With no history, _KndxIndex writes a new index:
1030
1044
        self.assertEqual(_KndxIndex.HEADER +
1031
 
            b"\n%s option 0 1  :" % (utf8_revision_id,),
1032
 
            call[1][1].getvalue())
 
1045
                         b"\n%s option 0 1  :" % (utf8_revision_id,),
 
1046
                         call[1][1].getvalue())
1033
1047
        self.assertEqual({'create_parent_dir': True}, call[2])
1034
1048
 
1035
1049
    def test_write_utf8_parents(self):
1047
1061
        self.assertEqual('filename.kndx', call[1][0])
1048
1062
        # With no history, _KndxIndex writes a new index:
1049
1063
        self.assertEqual(_KndxIndex.HEADER +
1050
 
            b"\nversion option 0 1 .%s :" % (utf8_revision_id,),
1051
 
            call[1][1].getvalue())
 
1064
                         b"\nversion option 0 1 .%s :" % (utf8_revision_id,),
 
1065
                         call[1][1].getvalue())
1052
1066
        self.assertEqual({'create_parent_dir': True}, call[2])
1053
1067
 
1054
1068
    def test_keys(self):
1123
1137
        # dir_mode=0777)
1124
1138
        self.assertEqual([], transport.calls)
1125
1139
        self.add_a_b(index)
1126
 
        #self.assertEqual(
1127
 
        #[    {"dir_mode": 0777, "create_parent_dir": True, "mode": "wb"},
 
1140
        # self.assertEqual(
 
1141
        # [    {"dir_mode": 0777, "create_parent_dir": True, "mode": "wb"},
1128
1142
        #    kwargs)
1129
1143
        # Two calls: one during which we load the existing index (and when its
1130
1144
        # missing create it), then a second where we write the contents out.
1233
1247
            _KndxIndex.HEADER,
1234
1248
            b"a option 0 1 :",
1235
1249
            b"b option 0 1 :",
1236
 
            b"c option 0 1 1v :", # Can't have a parent of '1v'
 
1250
            b"c option 0 1 1v :",  # Can't have a parent of '1v'
1237
1251
            ])
1238
1252
        index = self.get_knit_index(transport, 'filename', 'r')
1239
1253
        self.assertRaises(KnitCorrupt, index.keys)
1243
1257
            _KndxIndex.HEADER,
1244
1258
            b"a option 0 1 :",
1245
1259
            b"b option 0 1 :",
1246
 
            b"c option 0 1 1 v :", # Can't have a parent of 'v'
 
1260
            b"c option 0 1 1 v :",  # Can't have a parent of 'v'
1247
1261
            ])
1248
1262
        index = self.get_knit_index(transport, 'filename', 'r')
1249
1263
        self.assertRaises(KnitCorrupt, index.keys)
1277
1291
        transport = MockTransport([
1278
1292
            _KndxIndex.HEADER,
1279
1293
            b"a option 0 10  :",
1280
 
            b"b option 10 10 0", # This line isn't terminated, ignored
 
1294
            b"b option 10 10 0",  # This line isn't terminated, ignored
1281
1295
            ])
1282
1296
        index = self.get_knit_index(transport, "filename", "r")
1283
1297
        self.assertEqual({(b'a',)}, index.keys())
1287
1301
        transport = MockTransport([
1288
1302
            _KndxIndex.HEADER,
1289
1303
            b"a option 0 10  :",
1290
 
            b"b option 10 10 0", # This line isn't terminated, ignored
1291
 
            b"c option 20 10 0 :", # Properly terminated, and starts with '\n'
 
1304
            b"b option 10 10 0",  # This line isn't terminated, ignored
 
1305
            b"c option 20 10 0 :",  # Properly terminated, and starts with '\n'
1292
1306
            ])
1293
1307
        index = self.get_knit_index(transport, "filename", "r")
1294
1308
        self.assertEqual({(b'a',), (b'c',)}, index.keys())
1298
1312
        transport = MockTransport([
1299
1313
            _KndxIndex.HEADER,
1300
1314
            b"a option 0 10  :",
1301
 
            b"b option 10 10 0 :a", # This line has extra trailing characters
1302
 
            b"c option 20 10 0 :", # Properly terminated, and starts with '\n'
 
1315
            b"b option 10 10 0 :a",  # This line has extra trailing characters
 
1316
            b"c option 20 10 0 :",  # Properly terminated, and starts with '\n'
1303
1317
            ])
1304
1318
        index = self.get_knit_index(transport, "filename", "r")
1305
1319
        self.assertEqual({(b'a',), (b'c',)}, index.keys())
1327
1341
        rev_key = (b'rev-id',)
1328
1342
        ann._num_compression_children[rev_key] = 1
1329
1343
        res = ann._expand_record(rev_key, ((b'parent-id',),), None,
1330
 
                           [b'line1\n', b'line2\n'], ('fulltext', True))
 
1344
                                 [b'line1\n', b'line2\n'], ('fulltext', True))
1331
1345
        # The content object and text lines should be cached appropriately
1332
1346
        self.assertEqual([b'line1\n', b'line2'], res)
1333
1347
        content_obj = ann._content_objects[rev_key]
1390
1404
        rev2_key = (b'rev2-id',)
1391
1405
        record = [b'0,1,1\n', b'new-line\n']
1392
1406
        details = ('line-delta', False)
1393
 
        ann._expand_record(rev2_key, (parent_key,), parent_key, record, details)
 
1407
        ann._expand_record(rev2_key, (parent_key,),
 
1408
                           parent_key, record, details)
1394
1409
        self.assertEqual([(1, 1, 2), (3, 3, 0)],
1395
1410
                         ann._matching_blocks[(rev2_key, parent_key)])
1396
1411
 
1398
1413
        ann = self.make_annotator()
1399
1414
        rev_key = (b'rev-id',)
1400
1415
        parent_key = (b'parent-id',)
1401
 
        parent_ann = [(parent_key,)]*3
 
1416
        parent_ann = [(parent_key,)] * 3
1402
1417
        block_key = (rev_key, parent_key)
1403
1418
        ann._annotations_cache[parent_key] = parent_ann
1404
1419
        ann._matching_blocks[block_key] = [(0, 1, 1), (3, 3, 0)]
1405
1420
        # We should not try to access any parent_lines content, because we know
1406
1421
        # we already have the matching blocks
1407
1422
        par_ann, blocks = ann._get_parent_annotations_and_matches(rev_key,
1408
 
                                        [b'1\n', b'2\n', b'3\n'], parent_key)
 
1423
                                                                  [b'1\n', b'2\n', b'3\n'], parent_key)
1409
1424
        self.assertEqual(parent_ann, par_ann)
1410
1425
        self.assertEqual([(0, 1, 1), (3, 3, 0)], blocks)
1411
1426
        self.assertEqual({}, ann._matching_blocks)
1428
1443
        res = ann._expand_record(p1_key, (), None, p1_record,
1429
1444
                                 ('fulltext', False))
1430
1445
        self.assertEqual(p1_record, res)
1431
 
        ann._annotations_cache[p1_key] = [(p1_key,)]*2
 
1446
        ann._annotations_cache[p1_key] = [(p1_key,)] * 2
1432
1447
        res = ann._process_pending(p1_key)
1433
1448
        self.assertEqual([], res)
1434
1449
        self.assertFalse(p1_key in ann._pending_deltas)
1473
1488
                          (rev2_key, rev3_key),
1474
1489
                          (rev2_key,),
1475
1490
                          (rev3_key,),
1476
 
                         ], anns)
 
1491
                          ], anns)
1477
1492
        self.assertEqualDiff(spec_text, b''.join(lines))
1478
1493
 
1479
1494
 
1505
1520
        target.insert_record_stream(
1506
1521
            source.get_record_stream([broken], 'unordered', False))
1507
1522
        err = self.assertRaises(KnitCorrupt,
1508
 
            next(target.get_record_stream([broken], 'unordered', True
1509
 
            )).get_bytes_as, 'chunked')
 
1523
                                next(target.get_record_stream([broken], 'unordered', True
 
1524
                                                              )).get_bytes_as, 'chunked')
1510
1525
        self.assertEqual([b'gam\n', b'bar\n'], err.content)
1511
1526
        # Test for formatting with live data
1512
1527
        self.assertStartsWith(str(err), "Knit ")
1520
1535
        idx = knit._index
1521
1536
        idx.add_records([((b'a-1',), [b'fulltext'], ((b'a-1',), 0, 0), [])])
1522
1537
        self.check_file_contents('test.kndx',
1523
 
            b'# bzr knit index 8\n'
1524
 
            b'\n'
1525
 
            b'a-1 fulltext 0 0  :'
1526
 
            )
 
1538
                                 b'# bzr knit index 8\n'
 
1539
                                 b'\n'
 
1540
                                 b'a-1 fulltext 0 0  :'
 
1541
                                 )
1527
1542
        idx.add_records([
1528
1543
            ((b'a-2',), [b'fulltext'], ((b'a-2',), 0, 0), [(b'a-1',)]),
1529
1544
            ((b'a-3',), [b'fulltext'], ((b'a-3',), 0, 0), [(b'a-2',)]),
1530
1545
            ])
1531
1546
        self.check_file_contents('test.kndx',
1532
 
            b'# bzr knit index 8\n'
1533
 
            b'\n'
1534
 
            b'a-1 fulltext 0 0  :\n'
1535
 
            b'a-2 fulltext 0 0 0 :\n'
1536
 
            b'a-3 fulltext 0 0 1 :'
1537
 
            )
 
1547
                                 b'# bzr knit index 8\n'
 
1548
                                 b'\n'
 
1549
                                 b'a-1 fulltext 0 0  :\n'
 
1550
                                 b'a-2 fulltext 0 0 0 :\n'
 
1551
                                 b'a-3 fulltext 0 0 1 :'
 
1552
                                 )
1538
1553
        self.assertEqual({(b'a-3',), (b'a-1',), (b'a-2',)}, idx.keys())
1539
1554
        self.assertEqual({
1540
1555
            (b'a-1',): (((b'a-1',), 0, 0), None, (), ('fulltext', False)),
1542
1557
            (b'a-3',): (((b'a-3',), 0, 0), None, ((b'a-2',),), ('fulltext', False)),
1543
1558
            }, idx.get_build_details(idx.keys()))
1544
1559
        self.assertEqual({(b'a-1',): (),
1545
 
            (b'a-2',): ((b'a-1',),),
1546
 
            (b'a-3',): ((b'a-2',),),},
1547
 
            idx.get_parent_map(idx.keys()))
 
1560
                          (b'a-2',): ((b'a-1',),),
 
1561
                          (b'a-3',): ((b'a-2',),), },
 
1562
                         idx.get_parent_map(idx.keys()))
1548
1563
 
1549
1564
    def test_add_versions_fails_clean(self):
1550
1565
        """If add_versions fails in the middle, it restores a pristine state.
1577
1592
            self.assertEqual(
1578
1593
                {(b'a-1',): (((b'a-1',), 0, 0), None, (), ('fulltext', False))},
1579
1594
                idx.get_build_details([(b'a-1',)]))
1580
 
            self.assertEqual({(b'a-1',):()}, idx.get_parent_map(idx.keys()))
 
1595
            self.assertEqual({(b'a-1',): ()}, idx.get_parent_map(idx.keys()))
1581
1596
 
1582
1597
        assertA1Only()
1583
1598
        self.assertRaises(StopEarly, idx.add_records, generate_failure())
1626
1641
                ((b'tip', ), b'N0 100', ([(b'parent', )], [], )),
1627
1642
                ((b'tail', ), b'', ([], []))])
1628
1643
            index2 = self.make_g_index('2', 2, [
1629
 
                ((b'parent', ), b' 100 78', ([(b'tail', ), (b'ghost', )], [(b'tail', )])),
 
1644
                ((b'parent', ), b' 100 78',
 
1645
                 ([(b'tail', ), (b'ghost', )], [(b'tail', )])),
1630
1646
                ((b'separate', ), b'', ([], []))])
1631
1647
        else:
1632
1648
            # just blob location and graph in the index.
1643
1659
            add_callback = self.catch_add
1644
1660
        else:
1645
1661
            add_callback = None
1646
 
        return _KnitGraphIndex(combined_index, lambda:True, deltas=deltas,
1647
 
            add_callback=add_callback)
 
1662
        return _KnitGraphIndex(combined_index, lambda: True, deltas=deltas,
 
1663
                               add_callback=add_callback)
1648
1664
 
1649
1665
    def test_keys(self):
1650
1666
        index = self.two_graph_index()
1651
1667
        self.assertEqual({(b'tail',), (b'tip',), (b'parent',), (b'separate',)},
1652
 
            set(index.keys()))
 
1668
                         set(index.keys()))
1653
1669
 
1654
1670
    def test_get_position(self):
1655
1671
        index = self.two_graph_index()
1656
 
        self.assertEqual((index._graph_index._indices[0], 0, 100), index.get_position((b'tip',)))
1657
 
        self.assertEqual((index._graph_index._indices[1], 100, 78), index.get_position((b'parent',)))
 
1672
        self.assertEqual(
 
1673
            (index._graph_index._indices[0], 0, 100), index.get_position((b'tip',)))
 
1674
        self.assertEqual(
 
1675
            (index._graph_index._indices[1], 100, 78), index.get_position((b'parent',)))
1658
1676
 
1659
1677
    def test_get_method_deltas(self):
1660
1678
        index = self.two_graph_index(deltas=True)
1669
1687
 
1670
1688
    def test_get_options_deltas(self):
1671
1689
        index = self.two_graph_index(deltas=True)
1672
 
        self.assertEqual([b'fulltext', b'no-eol'], index.get_options((b'tip',)))
 
1690
        self.assertEqual([b'fulltext', b'no-eol'],
 
1691
                         index.get_options((b'tip',)))
1673
1692
        self.assertEqual([b'line-delta'], index.get_options((b'parent',)))
1674
1693
 
1675
1694
    def test_get_options_no_deltas(self):
1676
1695
        # check that the parent-history lookup is ignored with deltas=False.
1677
1696
        index = self.two_graph_index(deltas=False)
1678
 
        self.assertEqual([b'fulltext', b'no-eol'], index.get_options((b'tip',)))
 
1697
        self.assertEqual([b'fulltext', b'no-eol'],
 
1698
                         index.get_options((b'tip',)))
1679
1699
        self.assertEqual([b'fulltext'], index.get_options((b'parent',)))
1680
1700
 
1681
1701
    def test_get_parent_map(self):
1682
1702
        index = self.two_graph_index()
1683
 
        self.assertEqual({(b'parent',):((b'tail',), (b'ghost',))},
1684
 
            index.get_parent_map([(b'parent',), (b'ghost',)]))
 
1703
        self.assertEqual({(b'parent',): ((b'tail',), (b'ghost',))},
 
1704
                         index.get_parent_map([(b'parent',), (b'ghost',)]))
1685
1705
 
1686
1706
    def catch_add(self, entries):
1687
1707
        self.caught_entries.append(entries)
1689
1709
    def test_add_no_callback_errors(self):
1690
1710
        index = self.two_graph_index()
1691
1711
        self.assertRaises(errors.ReadOnlyError, index.add_records,
1692
 
            [((b'new',), b'fulltext,no-eol', (None, 50, 60), [b'separate'])])
 
1712
                          [((b'new',), b'fulltext,no-eol', (None, 50, 60), [b'separate'])])
1693
1713
 
1694
1714
    def test_add_version_smoke(self):
1695
1715
        index = self.two_graph_index(catch_adds=True)
1696
1716
        index.add_records([((b'new',), b'fulltext,no-eol', (None, 50, 60),
1697
 
            [(b'separate',)])])
 
1717
                            [(b'separate',)])])
1698
1718
        self.assertEqual([[((b'new', ), b'N50 60', (((b'separate',),),))]],
1699
 
            self.caught_entries)
 
1719
                         self.caught_entries)
1700
1720
 
1701
1721
    def test_add_version_delta_not_delta_index(self):
1702
1722
        index = self.two_graph_index(catch_adds=True)
1703
1723
        self.assertRaises(KnitCorrupt, index.add_records,
1704
 
            [((b'new',), b'no-eol,line-delta', (None, 0, 100), [(b'parent',)])])
 
1724
                          [((b'new',), b'no-eol,line-delta', (None, 0, 100), [(b'parent',)])])
1705
1725
        self.assertEqual([], self.caught_entries)
1706
1726
 
1707
1727
    def test_add_version_same_dup(self):
1708
1728
        index = self.two_graph_index(catch_adds=True)
1709
1729
        # options can be spelt two different ways
1710
 
        index.add_records([((b'tip',), b'fulltext,no-eol', (None, 0, 100), [(b'parent',)])])
1711
 
        index.add_records([((b'tip',), b'no-eol,fulltext', (None, 0, 100), [(b'parent',)])])
 
1730
        index.add_records(
 
1731
            [((b'tip',), b'fulltext,no-eol', (None, 0, 100), [(b'parent',)])])
 
1732
        index.add_records(
 
1733
            [((b'tip',), b'no-eol,fulltext', (None, 0, 100), [(b'parent',)])])
1712
1734
        # position/length are ignored (because each pack could have fulltext or
1713
1735
        # delta, and be at a different position.
1714
1736
        index.add_records([((b'tip',), b'fulltext,no-eol', (None, 50, 100),
1715
 
            [(b'parent',)])])
 
1737
                            [(b'parent',)])])
1716
1738
        index.add_records([((b'tip',), b'fulltext,no-eol', (None, 0, 1000),
1717
 
            [(b'parent',)])])
 
1739
                            [(b'parent',)])])
1718
1740
        # but neither should have added data:
1719
1741
        self.assertEqual([[], [], [], []], self.caught_entries)
1720
1742
 
1722
1744
        index = self.two_graph_index(deltas=True, catch_adds=True)
1723
1745
        # change options
1724
1746
        self.assertRaises(KnitCorrupt, index.add_records,
1725
 
            [((b'tip',), b'line-delta', (None, 0, 100), [(b'parent',)])])
 
1747
                          [((b'tip',), b'line-delta', (None, 0, 100), [(b'parent',)])])
1726
1748
        self.assertRaises(KnitCorrupt, index.add_records,
1727
 
            [((b'tip',), b'fulltext', (None, 0, 100), [(b'parent',)])])
 
1749
                          [((b'tip',), b'fulltext', (None, 0, 100), [(b'parent',)])])
1728
1750
        # parents
1729
1751
        self.assertRaises(KnitCorrupt, index.add_records,
1730
 
            [((b'tip',), b'fulltext,no-eol', (None, 0, 100), [])])
 
1752
                          [((b'tip',), b'fulltext,no-eol', (None, 0, 100), [])])
1731
1753
        self.assertEqual([], self.caught_entries)
1732
1754
 
1733
1755
    def test_add_versions_nodeltas(self):
1734
1756
        index = self.two_graph_index(catch_adds=True)
1735
1757
        index.add_records([
1736
 
                ((b'new',), b'fulltext,no-eol', (None, 50, 60), [(b'separate',)]),
1737
 
                ((b'new2',), b'fulltext', (None, 0, 6), [(b'new',)]),
1738
 
                ])
 
1758
            ((b'new',), b'fulltext,no-eol', (None, 50, 60), [(b'separate',)]),
 
1759
            ((b'new2',), b'fulltext', (None, 0, 6), [(b'new',)]),
 
1760
            ])
1739
1761
        self.assertEqual([((b'new', ), b'N50 60', (((b'separate',),),)),
1740
 
            ((b'new2', ), b' 0 6', (((b'new',),),))],
1741
 
            sorted(self.caught_entries[0]))
 
1762
                          ((b'new2', ), b' 0 6', (((b'new',),),))],
 
1763
                         sorted(self.caught_entries[0]))
1742
1764
        self.assertEqual(1, len(self.caught_entries))
1743
1765
 
1744
1766
    def test_add_versions_deltas(self):
1745
1767
        index = self.two_graph_index(deltas=True, catch_adds=True)
1746
1768
        index.add_records([
1747
 
                ((b'new',), b'fulltext,no-eol', (None, 50, 60), [(b'separate',)]),
1748
 
                ((b'new2',), b'line-delta', (None, 0, 6), [(b'new',)]),
1749
 
                ])
 
1769
            ((b'new',), b'fulltext,no-eol', (None, 50, 60), [(b'separate',)]),
 
1770
            ((b'new2',), b'line-delta', (None, 0, 6), [(b'new',)]),
 
1771
            ])
1750
1772
        self.assertEqual([((b'new', ), b'N50 60', (((b'separate',),), ())),
1751
 
            ((b'new2', ), b' 0 6', (((b'new',),), ((b'new',),), ))],
1752
 
            sorted(self.caught_entries[0]))
 
1773
                          ((b'new2', ), b' 0 6', (((b'new',),), ((b'new',),), ))],
 
1774
                         sorted(self.caught_entries[0]))
1753
1775
        self.assertEqual(1, len(self.caught_entries))
1754
1776
 
1755
1777
    def test_add_versions_delta_not_delta_index(self):
1756
1778
        index = self.two_graph_index(catch_adds=True)
1757
1779
        self.assertRaises(KnitCorrupt, index.add_records,
1758
 
            [((b'new',), b'no-eol,line-delta', (None, 0, 100), [(b'parent',)])])
 
1780
                          [((b'new',), b'no-eol,line-delta', (None, 0, 100), [(b'parent',)])])
1759
1781
        self.assertEqual([], self.caught_entries)
1760
1782
 
1761
1783
    def test_add_versions_random_id_accepted(self):
1766
1788
        index = self.two_graph_index(catch_adds=True)
1767
1789
        # options can be spelt two different ways
1768
1790
        index.add_records([((b'tip',), b'fulltext,no-eol', (None, 0, 100),
1769
 
            [(b'parent',)])])
 
1791
                            [(b'parent',)])])
1770
1792
        index.add_records([((b'tip',), b'no-eol,fulltext', (None, 0, 100),
1771
 
            [(b'parent',)])])
 
1793
                            [(b'parent',)])])
1772
1794
        # position/length are ignored (because each pack could have fulltext or
1773
1795
        # delta, and be at a different position.
1774
1796
        index.add_records([((b'tip',), b'fulltext,no-eol', (None, 50, 100),
1775
 
            [(b'parent',)])])
 
1797
                            [(b'parent',)])])
1776
1798
        index.add_records([((b'tip',), b'fulltext,no-eol', (None, 0, 1000),
1777
 
            [(b'parent',)])])
 
1799
                            [(b'parent',)])])
1778
1800
        # but neither should have added data.
1779
1801
        self.assertEqual([[], [], [], []], self.caught_entries)
1780
1802
 
1782
1804
        index = self.two_graph_index(deltas=True, catch_adds=True)
1783
1805
        # change options
1784
1806
        self.assertRaises(KnitCorrupt, index.add_records,
1785
 
            [((b'tip',), b'line-delta', (None, 0, 100), [(b'parent',)])])
 
1807
                          [((b'tip',), b'line-delta', (None, 0, 100), [(b'parent',)])])
1786
1808
        self.assertRaises(KnitCorrupt, index.add_records,
1787
 
            [((b'tip',), b'fulltext', (None, 0, 100), [(b'parent',)])])
 
1809
                          [((b'tip',), b'fulltext', (None, 0, 100), [(b'parent',)])])
1788
1810
        # parents
1789
1811
        self.assertRaises(KnitCorrupt, index.add_records,
1790
 
            [((b'tip',), b'fulltext,no-eol', (None, 0, 100), [])])
 
1812
                          [((b'tip',), b'fulltext,no-eol', (None, 0, 100), [])])
1791
1813
        # change options in the second record
1792
1814
        self.assertRaises(KnitCorrupt, index.add_records,
1793
 
            [((b'tip',), b'fulltext,no-eol', (None, 0, 100), [(b'parent',)]),
1794
 
             ((b'tip',), b'line-delta', (None, 0, 100), [(b'parent',)])])
 
1815
                          [((b'tip',), b'fulltext,no-eol', (None, 0, 100), [(b'parent',)]),
 
1816
                           ((b'tip',), b'line-delta', (None, 0, 100), [(b'parent',)])])
1795
1817
        self.assertEqual([], self.caught_entries)
1796
1818
 
1797
1819
    def make_g_index_missing_compression_parent(self):
1798
1820
        graph_index = self.make_g_index('missing_comp', 2,
1799
 
            [((b'tip', ), b' 100 78',
1800
 
              ([(b'missing-parent', ), (b'ghost', )], [(b'missing-parent', )]))])
 
1821
                                        [((b'tip', ), b' 100 78',
 
1822
                                          ([(b'missing-parent', ), (b'ghost', )], [(b'missing-parent', )]))])
1801
1823
        return graph_index
1802
1824
 
1803
1825
    def make_g_index_missing_parent(self):
1804
1826
        graph_index = self.make_g_index('missing_parent', 2,
1805
 
            [((b'parent', ), b' 100 78', ([], [])),
1806
 
             ((b'tip', ), b' 100 78',
1807
 
              ([(b'parent', ), (b'missing-parent', )], [(b'parent', )])),
1808
 
              ])
 
1827
                                        [((b'parent', ), b' 100 78', ([], [])),
 
1828
                                         ((b'tip', ), b' 100 78',
 
1829
                                            ([(b'parent', ), (b'missing-parent', )], [(b'parent', )])),
 
1830
                                         ])
1809
1831
        return graph_index
1810
1832
 
1811
1833
    def make_g_index_no_external_refs(self):
1812
1834
        graph_index = self.make_g_index('no_external_refs', 2,
1813
 
            [((b'rev', ), b' 100 78',
1814
 
              ([(b'parent', ), (b'ghost', )], []))])
 
1835
                                        [((b'rev', ), b' 100 78',
 
1836
                                          ([(b'parent', ), (b'ghost', )], []))])
1815
1837
        return graph_index
1816
1838
 
1817
1839
    def test_add_good_unvalidated_index(self):
1837
1859
        unvalidated = self.make_g_index_missing_parent()
1838
1860
        combined = CombinedGraphIndex([unvalidated])
1839
1861
        index = _KnitGraphIndex(combined, lambda: True, deltas=True,
1840
 
            track_external_parent_refs=True)
 
1862
                                track_external_parent_refs=True)
1841
1863
        index.scan_unvalidated_index(unvalidated)
1842
1864
        self.assertEqual(
1843
1865
            frozenset([(b'missing-parent',)]), index.get_missing_parents())
1846
1868
        g_index = self.make_g_index('empty', 2, [])
1847
1869
        combined = CombinedGraphIndex([g_index])
1848
1870
        index = _KnitGraphIndex(combined, lambda: True, deltas=True,
1849
 
            add_callback=self.catch_add, track_external_parent_refs=True)
 
1871
                                add_callback=self.catch_add, track_external_parent_refs=True)
1850
1872
        self.caught_entries = []
1851
1873
        index.add_records([
1852
1874
            ((b'new-key',), b'fulltext,no-eol', (None, 50, 60),
1868
1890
    def make_new_missing_parent_g_index(self, name):
1869
1891
        missing_parent = name.encode('ascii') + b'-missing-parent'
1870
1892
        graph_index = self.make_g_index(name, 2,
1871
 
            [((name.encode('ascii') + b'tip', ), b' 100 78',
1872
 
              ([(missing_parent, ), (b'ghost', )], [(missing_parent, )]))])
 
1893
                                        [((name.encode('ascii') + b'tip', ), b' 100 78',
 
1894
                                          ([(missing_parent, ), (b'ghost', )], [(missing_parent, )]))])
1873
1895
        return graph_index
1874
1896
 
1875
1897
    def test_add_mulitiple_unvalidated_indices_with_missing_parents(self):
1885
1907
 
1886
1908
    def test_add_mulitiple_unvalidated_indices_with_mutual_dependencies(self):
1887
1909
        graph_index_a = self.make_g_index('one', 2,
1888
 
            [((b'parent-one', ), b' 100 78', ([(b'non-compression-parent',)], [])),
1889
 
             ((b'child-of-two', ), b' 100 78',
1890
 
              ([(b'parent-two',)], [(b'parent-two',)]))])
 
1910
                                          [((b'parent-one', ), b' 100 78', ([(b'non-compression-parent',)], [])),
 
1911
                                           ((b'child-of-two', ), b' 100 78',
 
1912
                                              ([(b'parent-two',)], [(b'parent-two',)]))])
1891
1913
        graph_index_b = self.make_g_index('two', 2,
1892
 
            [((b'parent-two', ), b' 100 78', ([(b'non-compression-parent',)], [])),
1893
 
             ((b'child-of-one', ), b' 100 78',
1894
 
              ([(b'parent-one',)], [(b'parent-one',)]))])
 
1914
                                          [((b'parent-two', ), b' 100 78', ([(b'non-compression-parent',)], [])),
 
1915
                                           ((b'child-of-one', ), b' 100 78',
 
1916
                                              ([(b'parent-one',)], [(b'parent-one',)]))])
1895
1917
        combined = CombinedGraphIndex([graph_index_a, graph_index_b])
1896
1918
        index = _KnitGraphIndex(combined, lambda: True, deltas=True)
1897
1919
        index.scan_unvalidated_index(graph_index_a)
1918
1940
        index = _KnitGraphIndex(combined, lambda: True, parents=False)
1919
1941
        index.scan_unvalidated_index(unvalidated)
1920
1942
        self.assertEqual(frozenset(),
1921
 
            index.get_missing_compression_parents())
 
1943
                         index.get_missing_compression_parents())
1922
1944
 
1923
1945
    def test_parents_deltas_incompatible(self):
1924
1946
        index = CombinedGraphIndex([])
1925
 
        self.assertRaises(knit.KnitError, _KnitGraphIndex, lambda:True,
1926
 
            index, deltas=True, parents=False)
 
1947
        self.assertRaises(knit.KnitError, _KnitGraphIndex, lambda: True,
 
1948
                          index, deltas=True, parents=False)
1927
1949
 
1928
1950
    def two_graph_index(self, catch_adds=False):
1929
1951
        """Build a two-graph index.
1945
1967
            add_callback = self.catch_add
1946
1968
        else:
1947
1969
            add_callback = None
1948
 
        return _KnitGraphIndex(combined_index, lambda:True, parents=False,
1949
 
            add_callback=add_callback)
 
1970
        return _KnitGraphIndex(combined_index, lambda: True, parents=False,
 
1971
                               add_callback=add_callback)
1950
1972
 
1951
1973
    def test_keys(self):
1952
1974
        index = self.two_graph_index()
1953
1975
        self.assertEqual({(b'tail',), (b'tip',), (b'parent',), (b'separate',)},
1954
 
            set(index.keys()))
 
1976
                         set(index.keys()))
1955
1977
 
1956
1978
    def test_get_position(self):
1957
1979
        index = self.two_graph_index()
1958
1980
        self.assertEqual((index._graph_index._indices[0], 0, 100),
1959
 
            index.get_position((b'tip',)))
 
1981
                         index.get_position((b'tip',)))
1960
1982
        self.assertEqual((index._graph_index._indices[1], 100, 78),
1961
 
            index.get_position((b'parent',)))
 
1983
                         index.get_position((b'parent',)))
1962
1984
 
1963
1985
    def test_get_method(self):
1964
1986
        index = self.two_graph_index()
1967
1989
 
1968
1990
    def test_get_options(self):
1969
1991
        index = self.two_graph_index()
1970
 
        self.assertEqual([b'fulltext', b'no-eol'], index.get_options((b'tip',)))
 
1992
        self.assertEqual([b'fulltext', b'no-eol'],
 
1993
                         index.get_options((b'tip',)))
1971
1994
        self.assertEqual([b'fulltext'], index.get_options((b'parent',)))
1972
1995
 
1973
1996
    def test_get_parent_map(self):
1974
1997
        index = self.two_graph_index()
1975
 
        self.assertEqual({(b'parent',):None},
1976
 
            index.get_parent_map([(b'parent',), (b'ghost',)]))
 
1998
        self.assertEqual({(b'parent',): None},
 
1999
                         index.get_parent_map([(b'parent',), (b'ghost',)]))
1977
2000
 
1978
2001
    def catch_add(self, entries):
1979
2002
        self.caught_entries.append(entries)
1981
2004
    def test_add_no_callback_errors(self):
1982
2005
        index = self.two_graph_index()
1983
2006
        self.assertRaises(errors.ReadOnlyError, index.add_records,
1984
 
            [((b'new',), b'fulltext,no-eol', (None, 50, 60), [(b'separate',)])])
 
2007
                          [((b'new',), b'fulltext,no-eol', (None, 50, 60), [(b'separate',)])])
1985
2008
 
1986
2009
    def test_add_version_smoke(self):
1987
2010
        index = self.two_graph_index(catch_adds=True)
1988
 
        index.add_records([((b'new',), b'fulltext,no-eol', (None, 50, 60), [])])
 
2011
        index.add_records(
 
2012
            [((b'new',), b'fulltext,no-eol', (None, 50, 60), [])])
1989
2013
        self.assertEqual([[((b'new', ), b'N50 60')]],
1990
 
            self.caught_entries)
 
2014
                         self.caught_entries)
1991
2015
 
1992
2016
    def test_add_version_delta_not_delta_index(self):
1993
2017
        index = self.two_graph_index(catch_adds=True)
1994
2018
        self.assertRaises(KnitCorrupt, index.add_records,
1995
 
            [((b'new',), b'no-eol,line-delta', (None, 0, 100), [])])
 
2019
                          [((b'new',), b'no-eol,line-delta', (None, 0, 100), [])])
1996
2020
        self.assertEqual([], self.caught_entries)
1997
2021
 
1998
2022
    def test_add_version_same_dup(self):
1999
2023
        index = self.two_graph_index(catch_adds=True)
2000
2024
        # options can be spelt two different ways
2001
 
        index.add_records([((b'tip',), b'fulltext,no-eol', (None, 0, 100), [])])
2002
 
        index.add_records([((b'tip',), b'no-eol,fulltext', (None, 0, 100), [])])
 
2025
        index.add_records(
 
2026
            [((b'tip',), b'fulltext,no-eol', (None, 0, 100), [])])
 
2027
        index.add_records(
 
2028
            [((b'tip',), b'no-eol,fulltext', (None, 0, 100), [])])
2003
2029
        # position/length are ignored (because each pack could have fulltext or
2004
2030
        # delta, and be at a different position.
2005
 
        index.add_records([((b'tip',), b'fulltext,no-eol', (None, 50, 100), [])])
2006
 
        index.add_records([((b'tip',), b'fulltext,no-eol', (None, 0, 1000), [])])
 
2031
        index.add_records(
 
2032
            [((b'tip',), b'fulltext,no-eol', (None, 50, 100), [])])
 
2033
        index.add_records(
 
2034
            [((b'tip',), b'fulltext,no-eol', (None, 0, 1000), [])])
2007
2035
        # but neither should have added data.
2008
2036
        self.assertEqual([[], [], [], []], self.caught_entries)
2009
2037
 
2011
2039
        index = self.two_graph_index(catch_adds=True)
2012
2040
        # change options
2013
2041
        self.assertRaises(KnitCorrupt, index.add_records,
2014
 
            [((b'tip',), b'no-eol,line-delta', (None, 0, 100), [])])
2015
 
        self.assertRaises(KnitCorrupt, index.add_records,
2016
 
            [((b'tip',), b'line-delta,no-eol', (None, 0, 100), [])])
2017
 
        self.assertRaises(KnitCorrupt, index.add_records,
2018
 
            [((b'tip',), b'fulltext', (None, 0, 100), [])])
 
2042
                          [((b'tip',), b'no-eol,line-delta', (None, 0, 100), [])])
 
2043
        self.assertRaises(KnitCorrupt, index.add_records,
 
2044
                          [((b'tip',), b'line-delta,no-eol', (None, 0, 100), [])])
 
2045
        self.assertRaises(KnitCorrupt, index.add_records,
 
2046
                          [((b'tip',), b'fulltext', (None, 0, 100), [])])
2019
2047
        # parents
2020
2048
        self.assertRaises(KnitCorrupt, index.add_records,
2021
 
            [((b'tip',), b'fulltext,no-eol', (None, 0, 100), [(b'parent',)])])
 
2049
                          [((b'tip',), b'fulltext,no-eol', (None, 0, 100), [(b'parent',)])])
2022
2050
        self.assertEqual([], self.caught_entries)
2023
2051
 
2024
2052
    def test_add_versions(self):
2025
2053
        index = self.two_graph_index(catch_adds=True)
2026
2054
        index.add_records([
2027
 
                ((b'new',), b'fulltext,no-eol', (None, 50, 60), []),
2028
 
                ((b'new2',), b'fulltext', (None, 0, 6), []),
2029
 
                ])
 
2055
            ((b'new',), b'fulltext,no-eol', (None, 50, 60), []),
 
2056
            ((b'new2',), b'fulltext', (None, 0, 6), []),
 
2057
            ])
2030
2058
        self.assertEqual([((b'new', ), b'N50 60'), ((b'new2', ), b' 0 6')],
2031
 
            sorted(self.caught_entries[0]))
 
2059
                         sorted(self.caught_entries[0]))
2032
2060
        self.assertEqual(1, len(self.caught_entries))
2033
2061
 
2034
2062
    def test_add_versions_delta_not_delta_index(self):
2035
2063
        index = self.two_graph_index(catch_adds=True)
2036
2064
        self.assertRaises(KnitCorrupt, index.add_records,
2037
 
            [((b'new',), b'no-eol,line-delta', (None, 0, 100), [(b'parent',)])])
 
2065
                          [((b'new',), b'no-eol,line-delta', (None, 0, 100), [(b'parent',)])])
2038
2066
        self.assertEqual([], self.caught_entries)
2039
2067
 
2040
2068
    def test_add_versions_parents_not_parents_index(self):
2041
2069
        index = self.two_graph_index(catch_adds=True)
2042
2070
        self.assertRaises(KnitCorrupt, index.add_records,
2043
 
            [((b'new',), b'no-eol,fulltext', (None, 0, 100), [(b'parent',)])])
 
2071
                          [((b'new',), b'no-eol,fulltext', (None, 0, 100), [(b'parent',)])])
2044
2072
        self.assertEqual([], self.caught_entries)
2045
2073
 
2046
2074
    def test_add_versions_random_id_accepted(self):
2050
2078
    def test_add_versions_same_dup(self):
2051
2079
        index = self.two_graph_index(catch_adds=True)
2052
2080
        # options can be spelt two different ways
2053
 
        index.add_records([((b'tip',), b'fulltext,no-eol', (None, 0, 100), [])])
2054
 
        index.add_records([((b'tip',), b'no-eol,fulltext', (None, 0, 100), [])])
 
2081
        index.add_records(
 
2082
            [((b'tip',), b'fulltext,no-eol', (None, 0, 100), [])])
 
2083
        index.add_records(
 
2084
            [((b'tip',), b'no-eol,fulltext', (None, 0, 100), [])])
2055
2085
        # position/length are ignored (because each pack could have fulltext or
2056
2086
        # delta, and be at a different position.
2057
 
        index.add_records([((b'tip',), b'fulltext,no-eol', (None, 50, 100), [])])
2058
 
        index.add_records([((b'tip',), b'fulltext,no-eol', (None, 0, 1000), [])])
 
2087
        index.add_records(
 
2088
            [((b'tip',), b'fulltext,no-eol', (None, 50, 100), [])])
 
2089
        index.add_records(
 
2090
            [((b'tip',), b'fulltext,no-eol', (None, 0, 1000), [])])
2059
2091
        # but neither should have added data.
2060
2092
        self.assertEqual([[], [], [], []], self.caught_entries)
2061
2093
 
2063
2095
        index = self.two_graph_index(catch_adds=True)
2064
2096
        # change options
2065
2097
        self.assertRaises(KnitCorrupt, index.add_records,
2066
 
            [((b'tip',), b'no-eol,line-delta', (None, 0, 100), [])])
2067
 
        self.assertRaises(KnitCorrupt, index.add_records,
2068
 
            [((b'tip',), b'line-delta,no-eol', (None, 0, 100), [])])
2069
 
        self.assertRaises(KnitCorrupt, index.add_records,
2070
 
            [((b'tip',), b'fulltext', (None, 0, 100), [])])
 
2098
                          [((b'tip',), b'no-eol,line-delta', (None, 0, 100), [])])
 
2099
        self.assertRaises(KnitCorrupt, index.add_records,
 
2100
                          [((b'tip',), b'line-delta,no-eol', (None, 0, 100), [])])
 
2101
        self.assertRaises(KnitCorrupt, index.add_records,
 
2102
                          [((b'tip',), b'fulltext', (None, 0, 100), [])])
2071
2103
        # parents
2072
2104
        self.assertRaises(KnitCorrupt, index.add_records,
2073
 
            [((b'tip',), b'fulltext,no-eol', (None, 0, 100), [(b'parent',)])])
 
2105
                          [((b'tip',), b'fulltext,no-eol', (None, 0, 100), [(b'parent',)])])
2074
2106
        # change options in the second record
2075
2107
        self.assertRaises(KnitCorrupt, index.add_records,
2076
 
            [((b'tip',), b'fulltext,no-eol', (None, 0, 100), []),
2077
 
             ((b'tip',), b'no-eol,line-delta', (None, 0, 100), [])])
 
2108
                          [((b'tip',), b'fulltext,no-eol', (None, 0, 100), []),
 
2109
                           ((b'tip',), b'no-eol,line-delta', (None, 0, 100), [])])
2078
2110
        self.assertEqual([], self.caught_entries)
2079
2111
 
2080
2112
 
2086
2118
        if _min_buffer_size is None:
2087
2119
            _min_buffer_size = knit._STREAM_MIN_BUFFER_SIZE
2088
2120
        self.assertEqual(exp_groups, kvf._group_keys_for_io(keys,
2089
 
                                        non_local_keys, positions,
2090
 
                                        _min_buffer_size=_min_buffer_size))
 
2121
                                                            non_local_keys, positions,
 
2122
                                                            _min_buffer_size=_min_buffer_size))
2091
2123
 
2092
2124
    def assertSplitByPrefix(self, expected_map, expected_prefix_order,
2093
2125
                            keys):
2138
2170
    def test__split_by_prefix(self):
2139
2171
        self.assertSplitByPrefix({b'f': [(b'f', b'a'), (b'f', b'b')],
2140
2172
                                  b'g': [(b'g', b'b'), (b'g', b'a')],
2141
 
                                 }, [b'f', b'g'],
 
2173
                                  }, [b'f', b'g'],
2142
2174
                                 [(b'f', b'a'), (b'g', b'b'),
2143
2175
                                  (b'g', b'a'), (b'f', b'b')])
2144
2176
 
2145
2177
        self.assertSplitByPrefix({b'f': [(b'f', b'a'), (b'f', b'b')],
2146
2178
                                  b'g': [(b'g', b'b'), (b'g', b'a')],
2147
 
                                 }, [b'f', b'g'],
 
2179
                                  }, [b'f', b'g'],
2148
2180
                                 [(b'f', b'a'), (b'f', b'b'),
2149
2181
                                  (b'g', b'b'), (b'g', b'a')])
2150
2182
 
2151
2183
        self.assertSplitByPrefix({b'f': [(b'f', b'a'), (b'f', b'b')],
2152
2184
                                  b'g': [(b'g', b'b'), (b'g', b'a')],
2153
 
                                 }, [b'f', b'g'],
 
2185
                                  }, [b'f', b'g'],
2154
2186
                                 [(b'f', b'a'), (b'f', b'b'),
2155
2187
                                  (b'g', b'b'), (b'g', b'a')])
2156
2188
 
2157
2189
        self.assertSplitByPrefix({b'f': [(b'f', b'a'), (b'f', b'b')],
2158
2190
                                  b'g': [(b'g', b'b'), (b'g', b'a')],
2159
2191
                                  b'': [(b'a',), (b'b',)]
2160
 
                                 }, [b'f', b'g', b''],
 
2192
                                  }, [b'f', b'g', b''],
2161
2193
                                 [(b'f', b'a'), (b'g', b'b'),
2162
2194
                                  (b'a',), (b'b',),
2163
2195
                                  (b'g', b'a'), (b'f', b'b')])
2208
2240
        basis, test = self.get_basis_and_test_knit()
2209
2241
        key = (b'foo',)
2210
2242
        key_basis = (b'bar',)
2211
 
        key_missing = (b'missing',)
2212
2243
        test.add_lines(key, (), [b'foo\n'])
2213
2244
        details = test.annotate(key)
2214
2245
        self.assertEqual([(key, b'foo\n')], details)
2218
2249
        basis.add_lines(key_basis, (), [b'foo\n', b'bar\n'])
2219
2250
        basis.calls = []
2220
2251
        details = test.annotate(key_basis)
2221
 
        self.assertEqual([(key_basis, b'foo\n'), (key_basis, b'bar\n')], details)
 
2252
        self.assertEqual(
 
2253
            [(key_basis, b'foo\n'), (key_basis, b'bar\n')], details)
2222
2254
        # Not optimised to date:
2223
2255
        # self.assertEqual([("annotate", key_basis)], basis.calls)
2224
2256
        self.assertEqual([('get_parent_map', {key_basis}),
2225
 
            ('get_parent_map', {key_basis}),
2226
 
            ('get_record_stream', [key_basis], 'topological', True)],
2227
 
            basis.calls)
 
2257
                          ('get_parent_map', {key_basis}),
 
2258
                          ('get_record_stream', [key_basis], 'topological', True)],
 
2259
                         basis.calls)
2228
2260
 
2229
2261
    def test_check(self):
2230
2262
        # At the moment checking a stacked knit does implicitly check the
2247
2279
        basis.calls = []
2248
2280
        parent_map = test.get_parent_map([key, key_basis, key_missing])
2249
2281
        self.assertEqual({key: (),
2250
 
            key_basis: ()}, parent_map)
 
2282
                          key_basis: ()}, parent_map)
2251
2283
        self.assertEqual([("get_parent_map", {key_basis, key_missing})],
2252
 
            basis.calls)
 
2284
                         basis.calls)
2253
2285
 
2254
2286
    def test_get_record_stream_unordered_fulltexts(self):
2255
2287
        # records from the test knit are answered without asking the basis:
2265
2297
        basis.add_lines(key_basis, (), [b'foo\n', b'bar\n'])
2266
2298
        basis.calls = []
2267
2299
        records = list(test.get_record_stream([key_basis, key_missing],
2268
 
            'unordered', True))
 
2300
                                              'unordered', True))
2269
2301
        self.assertEqual(2, len(records))
2270
2302
        calls = list(basis.calls)
2271
2303
        for record in records:
2274
2306
                self.assertIsInstance(record, AbsentContentFactory)
2275
2307
            else:
2276
2308
                reference = list(basis.get_record_stream([key_basis],
2277
 
                    'unordered', True))[0]
 
2309
                                                         'unordered', True))[0]
2278
2310
                self.assertEqual(reference.key, record.key)
2279
2311
                self.assertEqual(reference.sha1, record.sha1)
2280
2312
                self.assertEqual(reference.storage_kind, record.storage_kind)
2281
2313
                self.assertEqual(reference.get_bytes_as(reference.storage_kind),
2282
 
                    record.get_bytes_as(record.storage_kind))
 
2314
                                 record.get_bytes_as(record.storage_kind))
2283
2315
                self.assertEqual(reference.get_bytes_as('fulltext'),
2284
 
                    record.get_bytes_as('fulltext'))
 
2316
                                 record.get_bytes_as('fulltext'))
2285
2317
        # It's not strictly minimal, but it seems reasonable for now for it to
2286
2318
        # ask which fallbacks have which parents.
2287
2319
        self.assertEqual([
2308
2340
        results = []
2309
2341
        for record in records:
2310
2342
            self.assertSubset([record.key],
2311
 
                (key_basis, key_missing, key_basis_2, key))
 
2343
                              (key_basis, key_missing, key_basis_2, key))
2312
2344
            if record.key == key_missing:
2313
2345
                self.assertIsInstance(record, AbsentContentFactory)
2314
2346
            else:
2315
2347
                results.append((record.key, record.sha1, record.storage_kind,
2316
 
                    record.get_bytes_as('fulltext')))
 
2348
                                record.get_bytes_as('fulltext')))
2317
2349
        calls = list(basis.calls)
2318
2350
        order = [record[0] for record in results]
2319
2351
        self.assertEqual([key_basis_2, key_basis, key], order)
2323
2355
            else:
2324
2356
                source = basis
2325
2357
            record = next(source.get_record_stream([result[0]], 'unordered',
2326
 
                True))
 
2358
                                                   True))
2327
2359
            self.assertEqual(record.key, result[0])
2328
2360
            self.assertEqual(record.sha1, result[1])
2329
2361
            # We used to check that the storage kind matched, but actually it
2335
2367
        # ask which fallbacks have which parents.
2336
2368
        self.assertEqual(2, len(calls))
2337
2369
        self.assertEqual(
2338
 
                ("get_parent_map", {key_basis, key_basis_2, key_missing}),
2339
 
                calls[0])
 
2370
            ("get_parent_map", {key_basis, key_basis_2, key_missing}),
 
2371
            calls[0])
2340
2372
        # topological is requested from the fallback, because that is what
2341
2373
        # was requested at the top level.
2342
2374
        self.assertIn(
2343
 
                calls[1], [
2344
 
                ("get_record_stream", [key_basis_2, key_basis], 'topological', True),
 
2375
            calls[1], [
 
2376
                ("get_record_stream", [key_basis_2,
 
2377
                                       key_basis], 'topological', True),
2345
2378
                ("get_record_stream", [key_basis, key_basis_2], 'topological', True)])
2346
2379
 
2347
2380
    def test_get_record_stream_unordered_deltas(self):
2358
2391
        basis.add_lines(key_basis, (), [b'foo\n', b'bar\n'])
2359
2392
        basis.calls = []
2360
2393
        records = list(test.get_record_stream([key_basis, key_missing],
2361
 
            'unordered', False))
 
2394
                                              'unordered', False))
2362
2395
        self.assertEqual(2, len(records))
2363
2396
        calls = list(basis.calls)
2364
2397
        for record in records:
2367
2400
                self.assertIsInstance(record, AbsentContentFactory)
2368
2401
            else:
2369
2402
                reference = list(basis.get_record_stream([key_basis],
2370
 
                    'unordered', False))[0]
 
2403
                                                         'unordered', False))[0]
2371
2404
                self.assertEqual(reference.key, record.key)
2372
2405
                self.assertEqual(reference.sha1, record.sha1)
2373
2406
                self.assertEqual(reference.storage_kind, record.storage_kind)
2374
2407
                self.assertEqual(reference.get_bytes_as(reference.storage_kind),
2375
 
                    record.get_bytes_as(record.storage_kind))
 
2408
                                 record.get_bytes_as(record.storage_kind))
2376
2409
        # It's not strictly minimal, but it seems reasonable for now for it to
2377
2410
        # ask which fallbacks have which parents.
2378
2411
        self.assertEqual([
2399
2432
        results = []
2400
2433
        for record in records:
2401
2434
            self.assertSubset([record.key],
2402
 
                (key_basis, key_missing, key_basis_2, key))
 
2435
                              (key_basis, key_missing, key_basis_2, key))
2403
2436
            if record.key == key_missing:
2404
2437
                self.assertIsInstance(record, AbsentContentFactory)
2405
2438
            else:
2406
2439
                results.append((record.key, record.sha1, record.storage_kind,
2407
 
                    record.get_bytes_as(record.storage_kind)))
 
2440
                                record.get_bytes_as(record.storage_kind)))
2408
2441
        calls = list(basis.calls)
2409
2442
        order = [record[0] for record in results]
2410
2443
        self.assertEqual([key_basis_2, key_basis, key], order)
2414
2447
            else:
2415
2448
                source = basis
2416
2449
            record = next(source.get_record_stream([result[0]], 'unordered',
2417
 
                False))
 
2450
                                                   False))
2418
2451
            self.assertEqual(record.key, result[0])
2419
2452
            self.assertEqual(record.sha1, result[1])
2420
2453
            self.assertEqual(record.storage_kind, result[2])
2421
 
            self.assertEqual(record.get_bytes_as(record.storage_kind), result[3])
 
2454
            self.assertEqual(record.get_bytes_as(
 
2455
                record.storage_kind), result[3])
2422
2456
        # It's not strictly minimal, but it seems reasonable for now for it to
2423
2457
        # ask which fallbacks have which parents.
2424
2458
        self.assertEqual([
2445
2479
        basis.calls = []
2446
2480
        sha1s = test.get_sha1s([key, key_missing, key_basis])
2447
2481
        self.assertEqual({key: key_sha1sum,
2448
 
            key_basis: basis_sha1sum}, sha1s)
 
2482
                          key_basis: basis_sha1sum}, sha1s)
2449
2483
        self.assertEqual([("get_sha1s", {key_basis, key_missing})],
2450
 
            basis.calls)
 
2484
                         basis.calls)
2451
2485
 
2452
2486
    def test_insert_record_stream(self):
2453
2487
        # records are inserted as normal; insert_record_stream builds on
2454
2488
        # add_lines, so a smoke test should be all that's needed:
2455
 
        key = (b'foo',)
2456
2489
        key_basis = (b'bar',)
2457
2490
        key_delta = (b'zaphod',)
2458
2491
        basis, test = self.get_basis_and_test_knit()
2466
2499
        # XXX: this does somewhat too many calls in making sure of whether it
2467
2500
        # has to recreate the full text.
2468
2501
        self.assertEqual([("get_parent_map", {key_basis}),
2469
 
             ('get_parent_map', {key_basis}),
2470
 
             ('get_record_stream', [key_basis], 'unordered', True)],
2471
 
            basis.calls)
2472
 
        self.assertEqual({key_delta:(key_basis,)},
2473
 
            test.get_parent_map([key_delta]))
 
2502
                          ('get_parent_map', {key_basis}),
 
2503
                          ('get_record_stream', [key_basis], 'unordered', True)],
 
2504
                         basis.calls)
 
2505
        self.assertEqual({key_delta: (key_basis,)},
 
2506
                         test.get_parent_map([key_delta]))
2474
2507
        self.assertEqual(b'bar\n', next(test.get_record_stream([key_delta],
2475
 
            'unordered', True)).get_bytes_as('fulltext'))
 
2508
                                                               'unordered', True)).get_bytes_as('fulltext'))
2476
2509
 
2477
2510
    def test_iter_lines_added_or_present_in_keys(self):
2478
2511
        # Lines from the basis are returned, and lines for a given key are only
2486
2519
        lines = list(test.iter_lines_added_or_present_in_keys([key1]))
2487
2520
        self.assertEqual([(b"foo\n", key1)], lines)
2488
2521
        self.assertEqual([("iter_lines_added_or_present_in_keys", {key1})],
2489
 
            basis.calls)
 
2522
                         basis.calls)
2490
2523
        # keys in both are not duplicated:
2491
2524
        test.add_lines(key2, (), [b"bar\n"])
2492
2525
        basis.add_lines(key2, (), [b"bar\n"])
2521
2554
    def test_add_mpdiffs(self):
2522
2555
        # records are inserted as normal; add_mpdiff builds on
2523
2556
        # add_lines, so a smoke test should be all that's needed:
2524
 
        key = (b'foo',)
2525
2557
        key_basis = (b'bar',)
2526
2558
        key_delta = (b'zaphod',)
2527
2559
        basis, test = self.get_basis_and_test_knit()
2532
2564
        source.add_lines(key_delta, (key_basis,), [b'bar\n'])
2533
2565
        diffs = source.make_mpdiffs([key_delta])
2534
2566
        test.add_mpdiffs([(key_delta, (key_basis,),
2535
 
            source.get_sha1s([key_delta])[key_delta], diffs[0])])
 
2567
                           source.get_sha1s([key_delta])[key_delta], diffs[0])])
2536
2568
        self.assertEqual([("get_parent_map", {key_basis}),
2537
 
            ('get_record_stream', [key_basis], 'unordered', True),],
2538
 
            basis.calls)
2539
 
        self.assertEqual({key_delta:(key_basis,)},
2540
 
            test.get_parent_map([key_delta]))
 
2569
                          ('get_record_stream', [key_basis], 'unordered', True), ],
 
2570
                         basis.calls)
 
2571
        self.assertEqual({key_delta: (key_basis,)},
 
2572
                         test.get_parent_map([key_delta]))
2541
2573
        self.assertEqual(b'bar\n', next(test.get_record_stream([key_delta],
2542
 
            'unordered', True)).get_bytes_as('fulltext'))
 
2574
                                                               'unordered', True)).get_bytes_as('fulltext'))
2543
2575
 
2544
2576
    def test_make_mpdiffs(self):
2545
2577
        # Generating an mpdiff across a stacking boundary should detect parent
2552
2584
        basis.add_lines(key_right, (), [b'zaphod\n'])
2553
2585
        basis.calls = []
2554
2586
        test.add_lines(key, (key_left, key_right),
2555
 
            [b'bar\n', b'foo\n', b'zaphod\n'])
 
2587
                       [b'bar\n', b'foo\n', b'zaphod\n'])
2556
2588
        diffs = test.make_mpdiffs([key])
2557
2589
        self.assertEqual([
2558
2590
            multiparent.MultiParent([multiparent.ParentText(0, 0, 0, 1),
2559
 
                multiparent.NewText([b'foo\n']),
2560
 
                multiparent.ParentText(1, 0, 2, 1)])],
2561
 
            diffs)
 
2591
                                     multiparent.NewText([b'foo\n']),
 
2592
                                     multiparent.ParentText(1, 0, 2, 1)])],
 
2593
                         diffs)
2562
2594
        self.assertEqual(3, len(basis.calls))
2563
2595
        self.assertEqual([
2564
2596
            ("get_parent_map", {key_left, key_right}),
2583
2615
        vf.add_lines((b'd2',), ((b'd1',),), [b'd2\n'])
2584
2616
        # But heuristics could interfere, so check what happened:
2585
2617
        self.assertEqual(['knit-ft-gz', 'knit-delta-gz', 'knit-delta-gz'],
2586
 
            [record.storage_kind for record in
2587
 
             vf.get_record_stream([(b'base',), (b'd1',), (b'd2',)],
2588
 
                'topological', False)])
 
2618
                         [record.storage_kind for record in
 
2619
                          vf.get_record_stream([(b'base',), (b'd1',), (b'd2',)],
 
2620
                                               'topological', False)])
2589
2621
        # generate a stream of just the deltas include_delta_closure=True,
2590
2622
        # serialise to the network, and check that we get a delta closure on the wire.
2591
 
        stream = vf.get_record_stream([(b'd1',), (b'd2',)], 'topological', True)
 
2623
        stream = vf.get_record_stream(
 
2624
            [(b'd1',), (b'd2',)], 'topological', True)
2592
2625
        netb = [record.get_bytes_as(record.storage_kind) for record in stream]
2593
2626
        # The first bytes should be a memo from _ContentMapGenerator, and the
2594
2627
        # second bytes should be empty (because its a API proxy not something
2610
2643
        vf.add_lines((b'd2',), ((b'd1',),), [b'd2\n'])
2611
2644
        keys = [(b'd1',), (b'd2',)]
2612
2645
        generator = _VFContentMapGenerator(vf, keys,
2613
 
            global_map=vf.get_parent_map(keys))
 
2646
                                           global_map=vf.get_parent_map(keys))
2614
2647
        for record in generator.get_record_stream():
2615
2648
            if record.key == (b'd1',):
2616
2649
                self.assertEqual(b'd1\n', record.get_bytes_as('fulltext'))
2625
2658
        vf.add_lines((b'd2',), ((b'd1',),), [b'd2\n'])
2626
2659
        keys = [(b'base',), (b'd1',), (b'd2',)]
2627
2660
        generator = _VFContentMapGenerator(vf, keys,
2628
 
            global_map=vf.get_parent_map(keys))
 
2661
                                           global_map=vf.get_parent_map(keys))
2629
2662
        kinds = {(b'base',): 'knit-delta-closure',
2630
 
            (b'd1',): 'knit-delta-closure-ref',
2631
 
            (b'd2',): 'knit-delta-closure-ref',
2632
 
            }
 
2663
                 (b'd1',): 'knit-delta-closure-ref',
 
2664
                 (b'd2',): 'knit-delta-closure-ref',
 
2665
                 }
2633
2666
        for record in generator.get_record_stream():
2634
2667
            self.assertEqual(kinds[record.key], record.storage_kind)