/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: 2017-07-10 20:12:25 UTC
  • mto: This revision was merged to the branch mainline in revision 6733.
  • Revision ID: jelmer@jelmer.uk-20170710201225-49fe7zy6plr03ep0
Move bugtracker errors to breezy.bugtracker.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2010 Canonical Ltd
 
1
# Copyright (C) 2006-2011 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
16
16
 
17
17
"""Tests for Knit data structure"""
18
18
 
19
 
from cStringIO import StringIO
20
 
import difflib
21
19
import gzip
22
20
import sys
23
21
 
24
 
from bzrlib import (
 
22
from .. import (
25
23
    errors,
26
 
    generate_ids,
27
 
    knit,
28
24
    multiparent,
29
25
    osutils,
 
26
    tests,
 
27
    transport,
 
28
    )
 
29
from ..bzr import (
 
30
    knit,
30
31
    pack,
31
 
    tests,
32
32
    )
33
 
from bzrlib.errors import (
34
 
    RevisionAlreadyPresent,
 
33
from ..errors import (
35
34
    KnitHeaderError,
36
 
    RevisionNotPresent,
37
35
    NoSuchFile,
38
36
    )
39
 
from bzrlib.index import *
40
 
from bzrlib.knit import (
 
37
from ..bzr.index import *
 
38
from ..bzr.knit import (
41
39
    AnnotatedKnitContent,
42
40
    KnitContent,
43
 
    KnitSequenceMatcher,
44
41
    KnitVersionedFiles,
45
42
    PlainKnitContent,
46
43
    _VFContentMapGenerator,
47
 
    _DirectPackAccess,
48
44
    _KndxIndex,
49
45
    _KnitGraphIndex,
50
46
    _KnitKeyAccess,
51
47
    make_file_factory,
52
48
    )
53
 
from bzrlib.repofmt import pack_repo
54
 
from bzrlib.tests import (
55
 
    Feature,
56
 
    KnownFailure,
 
49
from ..patiencediff import PatienceSequenceMatcher
 
50
from ..bzr import (
 
51
    knitpack_repo,
 
52
    pack_repo,
 
53
    )
 
54
from ..sixish import (
 
55
    BytesIO,
 
56
    )
 
57
from . import (
57
58
    TestCase,
58
59
    TestCaseWithMemoryTransport,
59
60
    TestCaseWithTransport,
60
61
    TestNotApplicable,
61
62
    )
62
 
from bzrlib.transport import get_transport
63
 
from bzrlib.transport.memory import MemoryTransport
64
 
from bzrlib.tuned_gzip import GzipFile
65
 
from bzrlib.versionedfile import (
 
63
from ..bzr.versionedfile import (
66
64
    AbsentContentFactory,
67
65
    ConstantMapper,
68
66
    network_bytes_to_kind_and_offset,
69
67
    RecordingVersionedFilesDecorator,
70
68
    )
71
 
 
72
 
 
73
 
compiled_knit_feature = tests.ModuleAvailableFeature(
74
 
                            'bzrlib._knit_load_data_pyx')
 
69
from . import (
 
70
    features,
 
71
    )
 
72
 
 
73
 
 
74
compiled_knit_feature = features.ModuleAvailableFeature(
 
75
    'breezy.bzr._knit_load_data_pyx')
75
76
 
76
77
 
77
78
class KnitContentTestsMixin(object):
106
107
        line_delta = source_content.line_delta(target_content)
107
108
        delta_blocks = list(KnitContent.get_line_delta_blocks(line_delta,
108
109
            source_lines, target_lines))
109
 
        matcher = KnitSequenceMatcher(None, source_lines, target_lines)
110
 
        matcher_blocks = list(list(matcher.get_matching_blocks()))
 
110
        matcher = PatienceSequenceMatcher(None, source_lines, target_lines)
 
111
        matcher_blocks = list(matcher.get_matching_blocks())
111
112
        self.assertEqual(matcher_blocks, delta_blocks)
112
113
 
113
114
    def test_get_line_delta_blocks(self):
206
207
        content1 = self._make_content([("", "a"), ("", "b")])
207
208
        content2 = self._make_content([("", "a"), ("", "a"), ("", "c")])
208
209
        it = content1.line_delta_iter(content2)
209
 
        self.assertEqual(it.next(), (1, 2, 2, ["a", "c"]))
210
 
        self.assertRaises(StopIteration, it.next)
 
210
        self.assertEqual(next(it), (1, 2, 2, ["a", "c"]))
 
211
        self.assertRaises(StopIteration, next, it)
211
212
 
212
213
 
213
214
class TestAnnotatedKnitContent(TestCase, KnitContentTestsMixin):
233
234
        content1 = self._make_content([("", "a"), ("", "b")])
234
235
        content2 = self._make_content([("", "a"), ("", "a"), ("", "c")])
235
236
        it = content1.line_delta_iter(content2)
236
 
        self.assertEqual(it.next(), (1, 2, 2, [("", "a"), ("", "c")]))
237
 
        self.assertRaises(StopIteration, it.next)
 
237
        self.assertEqual(next(it), (1, 2, 2, [("", "a"), ("", "c")]))
 
238
        self.assertRaises(StopIteration, next, it)
238
239
 
239
240
 
240
241
class MockTransport(object):
249
250
        if self.file_lines is None:
250
251
            raise NoSuchFile(filename)
251
252
        else:
252
 
            return StringIO("\n".join(self.file_lines))
 
253
            return BytesIO(b"\n".join(self.file_lines))
253
254
 
254
255
    def readv(self, relpath, offsets):
255
256
        fp = self.get(relpath)
333
334
            transport.append_bytes(packname, bytes)
334
335
        writer = pack.ContainerWriter(write_data)
335
336
        writer.begin()
336
 
        access = _DirectPackAccess({})
 
337
        access = pack_repo._DirectPackAccess({})
337
338
        access.set_writer(writer, index, (transport, packname))
338
339
        return access, writer
339
340
 
346
347
        writer.end()
347
348
        return memos
348
349
 
 
350
    def test_pack_collection_pack_retries(self):
 
351
        """An explicit pack of a pack collection succeeds even when a
 
352
        concurrent pack happens.
 
353
        """
 
354
        builder = self.make_branch_builder('.')
 
355
        builder.start_series()
 
356
        builder.build_snapshot('rev-1', None, [
 
357
            ('add', ('', 'root-id', 'directory', None)),
 
358
            ('add', ('file', 'file-id', 'file', 'content\nrev 1\n')),
 
359
            ])
 
360
        builder.build_snapshot('rev-2', ['rev-1'], [
 
361
            ('modify', ('file-id', 'content\nrev 2\n')),
 
362
            ])
 
363
        builder.build_snapshot('rev-3', ['rev-2'], [
 
364
            ('modify', ('file-id', 'content\nrev 3\n')),
 
365
            ])
 
366
        self.addCleanup(builder.finish_series)
 
367
        b = builder.get_branch()
 
368
        self.addCleanup(b.lock_write().unlock)
 
369
        repo = b.repository
 
370
        collection = repo._pack_collection
 
371
        # Concurrently repack the repo.
 
372
        reopened_repo = repo.controldir.open_repository()
 
373
        reopened_repo.pack()
 
374
        # Pack the new pack.
 
375
        collection.pack()
 
376
 
349
377
    def make_vf_for_retrying(self):
350
378
        """Create 3 packs and a reload function.
351
379
 
378
406
        collection = repo._pack_collection
379
407
        collection.ensure_loaded()
380
408
        orig_packs = collection.packs
381
 
        packer = pack_repo.Packer(collection, orig_packs, '.testpack')
 
409
        packer = knitpack_repo.KnitPacker(collection, orig_packs, '.testpack')
382
410
        new_pack = packer.pack()
383
411
        # forget about the new pack
384
412
        collection.reset()
420
448
        # populated
421
449
        try:
422
450
            raise _TestException('foobar')
423
 
        except _TestException, e:
 
451
        except _TestException as e:
424
452
            retry_exc = errors.RetryWithNewPacks(None, reload_occurred=False,
425
453
                                                 exc_info=sys.exc_info())
 
454
        # GZ 2010-08-10: Cycle with exc_info affects 3 tests
426
455
        return retry_exc
427
456
 
428
457
    def test_read_from_several_packs(self):
437
466
        memos.extend(access.add_raw_records([('key', 5)], 'alpha'))
438
467
        writer.end()
439
468
        transport = self.get_transport()
440
 
        access = _DirectPackAccess({"FOO":(transport, 'packfile'),
 
469
        access = pack_repo._DirectPackAccess({"FOO":(transport, 'packfile'),
441
470
            "FOOBAR":(transport, 'pack2'),
442
471
            "BAZ":(transport, 'pack3')})
443
472
        self.assertEqual(['1234567890', '12345', 'alpha'],
453
482
 
454
483
    def test_set_writer(self):
455
484
        """The writer should be settable post construction."""
456
 
        access = _DirectPackAccess({})
 
485
        access = pack_repo._DirectPackAccess({})
457
486
        transport = self.get_transport()
458
487
        packname = 'packfile'
459
488
        index = 'foo'
471
500
        transport = self.get_transport()
472
501
        reload_called, reload_func = self.make_reload_func()
473
502
        # Note that the index key has changed from 'foo' to 'bar'
474
 
        access = _DirectPackAccess({'bar':(transport, 'packname')},
 
503
        access = pack_repo._DirectPackAccess({'bar':(transport, 'packname')},
475
504
                                   reload_func=reload_func)
476
505
        e = self.assertListRaises(errors.RetryWithNewPacks,
477
506
                                  access.get_raw_records, memos)
486
515
        memos = self.make_pack_file()
487
516
        transport = self.get_transport()
488
517
        # Note that the index key has changed from 'foo' to 'bar'
489
 
        access = _DirectPackAccess({'bar':(transport, 'packname')})
 
518
        access = pack_repo._DirectPackAccess({'bar':(transport, 'packname')})
490
519
        e = self.assertListRaises(KeyError, access.get_raw_records, memos)
491
520
 
492
521
    def test_missing_file_raises_retry(self):
494
523
        transport = self.get_transport()
495
524
        reload_called, reload_func = self.make_reload_func()
496
525
        # Note that the 'filename' has been changed to 'different-packname'
497
 
        access = _DirectPackAccess({'foo':(transport, 'different-packname')},
498
 
                                   reload_func=reload_func)
 
526
        access = pack_repo._DirectPackAccess(
 
527
            {'foo':(transport, 'different-packname')},
 
528
            reload_func=reload_func)
499
529
        e = self.assertListRaises(errors.RetryWithNewPacks,
500
530
                                  access.get_raw_records, memos)
501
531
        # The file has gone missing, so we assume we need to reload
509
539
        memos = self.make_pack_file()
510
540
        transport = self.get_transport()
511
541
        # Note that the 'filename' has been changed to 'different-packname'
512
 
        access = _DirectPackAccess({'foo':(transport, 'different-packname')})
 
542
        access = pack_repo._DirectPackAccess(
 
543
            {'foo': (transport, 'different-packname')})
513
544
        e = self.assertListRaises(errors.NoSuchFile,
514
545
                                  access.get_raw_records, memos)
515
546
 
519
550
        failing_transport = MockReadvFailingTransport(
520
551
                                [transport.get_bytes('packname')])
521
552
        reload_called, reload_func = self.make_reload_func()
522
 
        access = _DirectPackAccess({'foo':(failing_transport, 'packname')},
523
 
                                   reload_func=reload_func)
 
553
        access = pack_repo._DirectPackAccess(
 
554
            {'foo': (failing_transport, 'packname')},
 
555
            reload_func=reload_func)
524
556
        # Asking for a single record will not trigger the Mock failure
525
557
        self.assertEqual(['1234567890'],
526
558
            list(access.get_raw_records(memos[:1])))
542
574
        failing_transport = MockReadvFailingTransport(
543
575
                                [transport.get_bytes('packname')])
544
576
        reload_called, reload_func = self.make_reload_func()
545
 
        access = _DirectPackAccess({'foo':(failing_transport, 'packname')})
 
577
        access = pack_repo._DirectPackAccess(
 
578
            {'foo':(failing_transport, 'packname')})
546
579
        # Asking for a single record will not trigger the Mock failure
547
580
        self.assertEqual(['1234567890'],
548
581
            list(access.get_raw_records(memos[:1])))
553
586
                                  access.get_raw_records, memos)
554
587
 
555
588
    def test_reload_or_raise_no_reload(self):
556
 
        access = _DirectPackAccess({}, reload_func=None)
 
589
        access = pack_repo._DirectPackAccess({}, reload_func=None)
557
590
        retry_exc = self.make_retry_exception()
558
591
        # Without a reload_func, we will just re-raise the original exception
559
592
        self.assertRaises(_TestException, access.reload_or_raise, retry_exc)
560
593
 
561
594
    def test_reload_or_raise_reload_changed(self):
562
595
        reload_called, reload_func = self.make_reload_func(return_val=True)
563
 
        access = _DirectPackAccess({}, reload_func=reload_func)
 
596
        access = pack_repo._DirectPackAccess({}, reload_func=reload_func)
564
597
        retry_exc = self.make_retry_exception()
565
598
        access.reload_or_raise(retry_exc)
566
599
        self.assertEqual([1], reload_called)
570
603
 
571
604
    def test_reload_or_raise_reload_no_change(self):
572
605
        reload_called, reload_func = self.make_reload_func(return_val=False)
573
 
        access = _DirectPackAccess({}, reload_func=reload_func)
 
606
        access = pack_repo._DirectPackAccess({}, reload_func=reload_func)
574
607
        retry_exc = self.make_retry_exception()
575
608
        # If reload_occurred is False, then we consider it an error to have
576
609
        # reload_func() return False (no changes).
595
628
            self.fail('Annotation was not identical with reloading.')
596
629
        # Now delete the packs-in-use, which should trigger another reload, but
597
630
        # this time we just raise an exception because we can't recover
598
 
        for trans, name in vf._access._indices.itervalues():
 
631
        for trans, name in vf._access._indices.values():
599
632
            trans.delete(name)
600
633
        self.assertRaises(errors.NoSuchFile, vf.annotate, key)
601
634
        self.assertEqual([2, 1, 1], reload_counter)
608
641
        self.assertEqual([1, 1, 0], reload_counter)
609
642
        # Now delete the packs-in-use, which should trigger another reload, but
610
643
        # this time we just raise an exception because we can't recover
611
 
        for trans, name in vf._access._indices.itervalues():
 
644
        for trans, name in vf._access._indices.values():
612
645
            trans.delete(name)
613
646
        self.assertRaises(errors.NoSuchFile, vf._get_record_map, keys)
614
647
        self.assertEqual([2, 1, 1], reload_counter)
617
650
        vf, reload_counter = self.make_vf_for_retrying()
618
651
        keys = [('rev-1',), ('rev-2',), ('rev-3',)]
619
652
        record_stream = vf.get_record_stream(keys, 'topological', False)
620
 
        record = record_stream.next()
 
653
        record = next(record_stream)
621
654
        self.assertEqual(('rev-1',), record.key)
622
655
        self.assertEqual([0, 0, 0], reload_counter)
623
 
        record = record_stream.next()
 
656
        record = next(record_stream)
624
657
        self.assertEqual(('rev-2',), record.key)
625
658
        self.assertEqual([1, 1, 0], reload_counter)
626
 
        record = record_stream.next()
 
659
        record = next(record_stream)
627
660
        self.assertEqual(('rev-3',), record.key)
628
661
        self.assertEqual([1, 1, 0], reload_counter)
629
662
        # Now delete all pack files, and see that we raise the right error
630
 
        for trans, name in vf._access._indices.itervalues():
 
663
        for trans, name in vf._access._indices.values():
631
664
            trans.delete(name)
632
665
        self.assertListRaises(errors.NoSuchFile,
633
666
            vf.get_record_stream, keys, 'topological', False)
651
684
        self.assertEqual(plain_lines, reload_lines)
652
685
        self.assertEqual(21, len(plain_lines))
653
686
        # Now delete all pack files, and see that we raise the right error
654
 
        for trans, name in vf._access._indices.itervalues():
 
687
        for trans, name in vf._access._indices.values():
655
688
            trans.delete(name)
656
689
        self.assertListRaises(errors.NoSuchFile,
657
690
            vf.iter_lines_added_or_present_in_keys, keys)
699
732
class LowLevelKnitDataTests(TestCase):
700
733
 
701
734
    def create_gz_content(self, text):
702
 
        sio = StringIO()
 
735
        sio = BytesIO()
703
736
        gz_file = gzip.GzipFile(mode='wb', fileobj=sio)
704
737
        gz_file.write(text)
705
738
        gz_file.close()
707
740
 
708
741
    def make_multiple_records(self):
709
742
        """Create the content for multiple records."""
710
 
        sha1sum = osutils.sha('foo\nbar\n').hexdigest()
 
743
        sha1sum = osutils.sha_string('foo\nbar\n')
711
744
        total_txt = []
712
745
        gz_txt = self.create_gz_content('version rev-id-1 2 %s\n'
713
746
                                        'foo\n'
716
749
                                        % (sha1sum,))
717
750
        record_1 = (0, len(gz_txt), sha1sum)
718
751
        total_txt.append(gz_txt)
719
 
        sha1sum = osutils.sha('baz\n').hexdigest()
 
752
        sha1sum = osutils.sha_string('baz\n')
720
753
        gz_txt = self.create_gz_content('version rev-id-2 1 %s\n'
721
754
                                        'baz\n'
722
755
                                        'end rev-id-2\n'
726
759
        return total_txt, record_1, record_2
727
760
 
728
761
    def test_valid_knit_data(self):
729
 
        sha1sum = osutils.sha('foo\nbar\n').hexdigest()
 
762
        sha1sum = osutils.sha_string('foo\nbar\n')
730
763
        gz_txt = self.create_gz_content('version rev-id-1 2 %s\n'
731
764
                                        'foo\n'
732
765
                                        'bar\n'
763
796
                         raw_contents)
764
797
 
765
798
    def test_not_enough_lines(self):
766
 
        sha1sum = osutils.sha('foo\n').hexdigest()
 
799
        sha1sum = osutils.sha_string('foo\n')
767
800
        # record says 2 lines data says 1
768
801
        gz_txt = self.create_gz_content('version rev-id-1 2 %s\n'
769
802
                                        'foo\n'
781
814
        self.assertEqual([(('rev-id-1',),  gz_txt, sha1sum)], raw_contents)
782
815
 
783
816
    def test_too_many_lines(self):
784
 
        sha1sum = osutils.sha('foo\nbar\n').hexdigest()
 
817
        sha1sum = osutils.sha_string('foo\nbar\n')
785
818
        # record says 1 lines data says 2
786
819
        gz_txt = self.create_gz_content('version rev-id-1 1 %s\n'
787
820
                                        'foo\n'
800
833
        self.assertEqual([(('rev-id-1',), gz_txt, sha1sum)], raw_contents)
801
834
 
802
835
    def test_mismatched_version_id(self):
803
 
        sha1sum = osutils.sha('foo\nbar\n').hexdigest()
 
836
        sha1sum = osutils.sha_string('foo\nbar\n')
804
837
        gz_txt = self.create_gz_content('version rev-id-1 2 %s\n'
805
838
                                        'foo\n'
806
839
                                        'bar\n'
819
852
            knit._read_records_iter_raw(records))
820
853
 
821
854
    def test_uncompressed_data(self):
822
 
        sha1sum = osutils.sha('foo\nbar\n').hexdigest()
 
855
        sha1sum = osutils.sha_string('foo\nbar\n')
823
856
        txt = ('version rev-id-1 2 %s\n'
824
857
               'foo\n'
825
858
               'bar\n'
839
872
            knit._read_records_iter_raw(records))
840
873
 
841
874
    def test_corrupted_data(self):
842
 
        sha1sum = osutils.sha('foo\nbar\n').hexdigest()
 
875
        sha1sum = osutils.sha_string('foo\nbar\n')
843
876
        gz_txt = self.create_gz_content('version rev-id-1 2 %s\n'
844
877
                                        'foo\n'
845
878
                                        'bar\n'
862
895
 
863
896
    def get_knit_index(self, transport, name, mode):
864
897
        mapper = ConstantMapper(name)
865
 
        from bzrlib._knit_load_data_py import _load_data_py
 
898
        from ..bzr._knit_load_data_py import _load_data_py
866
899
        self.overrideAttr(knit, '_load_data', _load_data_py)
867
900
        allow_writes = lambda: 'w' in mode
868
901
        return _KndxIndex(transport, mapper, lambda:None, allow_writes, lambda:True)
872
905
        index = self.get_knit_index(transport, "filename", "w")
873
906
        index.keys()
874
907
        call = transport.calls.pop(0)
875
 
        # call[1][1] is a StringIO - we can't test it by simple equality.
 
908
        # call[1][1] is a BytesIO - we can't test it by simple equality.
876
909
        self.assertEqual('put_file_non_atomic', call[0])
877
910
        self.assertEqual('filename.kndx', call[1][0])
878
911
        # With no history, _KndxIndex writes a new index:
914
947
            ])
915
948
        index = self.get_knit_index(transport, "filename", "r")
916
949
        self.assertEqual(1, len(index.keys()))
917
 
        self.assertEqual(set([("version",)]), index.keys())
 
950
        self.assertEqual({("version",)}, index.keys())
918
951
 
919
952
    def test_read_corrupted_header(self):
920
953
        transport = MockTransport(['not a bzr knit index header\n'])
960
993
        index.add_records([
961
994
            ((utf8_revision_id,), ["option"], ((utf8_revision_id,), 0, 1), [])])
962
995
        call = transport.calls.pop(0)
963
 
        # call[1][1] is a StringIO - we can't test it by simple equality.
 
996
        # call[1][1] is a BytesIO - we can't test it by simple equality.
964
997
        self.assertEqual('put_file_non_atomic', call[0])
965
998
        self.assertEqual('filename.kndx', call[1][0])
966
999
        # With no history, _KndxIndex writes a new index:
979
1012
        index.add_records([
980
1013
            (("version",), ["option"], (("version",), 0, 1), [(utf8_revision_id,)])])
981
1014
        call = transport.calls.pop(0)
982
 
        # call[1][1] is a StringIO - we can't test it by simple equality.
 
1015
        # call[1][1] is a BytesIO - we can't test it by simple equality.
983
1016
        self.assertEqual('put_file_non_atomic', call[0])
984
1017
        self.assertEqual('filename.kndx', call[1][0])
985
1018
        # With no history, _KndxIndex writes a new index:
997
1030
        self.assertEqual(set(), index.keys())
998
1031
 
999
1032
        index.add_records([(("a",), ["option"], (("a",), 0, 1), [])])
1000
 
        self.assertEqual(set([("a",)]), index.keys())
 
1033
        self.assertEqual({("a",)}, index.keys())
1001
1034
 
1002
1035
        index.add_records([(("a",), ["option"], (("a",), 0, 1), [])])
1003
 
        self.assertEqual(set([("a",)]), index.keys())
 
1036
        self.assertEqual({("a",)}, index.keys())
1004
1037
 
1005
1038
        index.add_records([(("b",), ["option"], (("b",), 0, 1), [])])
1006
 
        self.assertEqual(set([("a",), ("b",)]), index.keys())
 
1039
        self.assertEqual({("a",), ("b",)}, index.keys())
1007
1040
 
1008
1041
    def add_a_b(self, index, random_id=None):
1009
1042
        kwargs = {}
1033
1066
 
1034
1067
        self.add_a_b(index)
1035
1068
        call = transport.calls.pop(0)
1036
 
        # call[1][1] is a StringIO - we can't test it by simple equality.
 
1069
        # call[1][1] is a BytesIO - we can't test it by simple equality.
1037
1070
        self.assertEqual('put_file_non_atomic', call[0])
1038
1071
        self.assertEqual('filename.kndx', call[1][0])
1039
1072
        # With no history, _KndxIndex writes a new index:
1073
1106
        self.assertEqual(_KndxIndex.HEADER, call[1][1].getvalue())
1074
1107
        self.assertEqual({'create_parent_dir': True}, call[2])
1075
1108
        call = transport.calls.pop(0)
1076
 
        # call[1][1] is a StringIO - we can't test it by simple equality.
 
1109
        # call[1][1] is a BytesIO - we can't test it by simple equality.
1077
1110
        self.assertEqual('put_file_non_atomic', call[0])
1078
1111
        self.assertEqual('filename.kndx', call[1][0])
1079
1112
        # With no history, _KndxIndex writes a new index:
1163
1196
            "b option 0 1 4 :"  # We don't have a 4th record
1164
1197
            ])
1165
1198
        index = self.get_knit_index(transport, 'filename', 'r')
1166
 
        try:
1167
 
            self.assertRaises(errors.KnitCorrupt, index.keys)
1168
 
        except TypeError, e:
1169
 
            if (str(e) == ('exceptions must be strings, classes, or instances,'
1170
 
                           ' not exceptions.IndexError')
1171
 
                and sys.version_info[0:2] >= (2,5)):
1172
 
                self.knownFailure('Pyrex <0.9.5 fails with TypeError when'
1173
 
                                  ' raising new style exceptions with python'
1174
 
                                  ' >=2.5')
1175
 
            else:
1176
 
                raise
 
1199
        self.assertRaises(errors.KnitCorrupt, index.keys)
1177
1200
 
1178
1201
    def test_corrupted_parent(self):
1179
1202
        transport = MockTransport([
1183
1206
            "c option 0 1 1v :", # Can't have a parent of '1v'
1184
1207
            ])
1185
1208
        index = self.get_knit_index(transport, 'filename', 'r')
1186
 
        try:
1187
 
            self.assertRaises(errors.KnitCorrupt, index.keys)
1188
 
        except TypeError, e:
1189
 
            if (str(e) == ('exceptions must be strings, classes, or instances,'
1190
 
                           ' not exceptions.ValueError')
1191
 
                and sys.version_info[0:2] >= (2,5)):
1192
 
                self.knownFailure('Pyrex <0.9.5 fails with TypeError when'
1193
 
                                  ' raising new style exceptions with python'
1194
 
                                  ' >=2.5')
1195
 
            else:
1196
 
                raise
 
1209
        self.assertRaises(errors.KnitCorrupt, index.keys)
1197
1210
 
1198
1211
    def test_corrupted_parent_in_list(self):
1199
1212
        transport = MockTransport([
1203
1216
            "c option 0 1 1 v :", # Can't have a parent of 'v'
1204
1217
            ])
1205
1218
        index = self.get_knit_index(transport, 'filename', 'r')
1206
 
        try:
1207
 
            self.assertRaises(errors.KnitCorrupt, index.keys)
1208
 
        except TypeError, e:
1209
 
            if (str(e) == ('exceptions must be strings, classes, or instances,'
1210
 
                           ' not exceptions.ValueError')
1211
 
                and sys.version_info[0:2] >= (2,5)):
1212
 
                self.knownFailure('Pyrex <0.9.5 fails with TypeError when'
1213
 
                                  ' raising new style exceptions with python'
1214
 
                                  ' >=2.5')
1215
 
            else:
1216
 
                raise
 
1219
        self.assertRaises(errors.KnitCorrupt, index.keys)
1217
1220
 
1218
1221
    def test_invalid_position(self):
1219
1222
        transport = MockTransport([
1221
1224
            "a option 1v 1 :",
1222
1225
            ])
1223
1226
        index = self.get_knit_index(transport, 'filename', 'r')
1224
 
        try:
1225
 
            self.assertRaises(errors.KnitCorrupt, index.keys)
1226
 
        except TypeError, e:
1227
 
            if (str(e) == ('exceptions must be strings, classes, or instances,'
1228
 
                           ' not exceptions.ValueError')
1229
 
                and sys.version_info[0:2] >= (2,5)):
1230
 
                self.knownFailure('Pyrex <0.9.5 fails with TypeError when'
1231
 
                                  ' raising new style exceptions with python'
1232
 
                                  ' >=2.5')
1233
 
            else:
1234
 
                raise
 
1227
        self.assertRaises(errors.KnitCorrupt, index.keys)
1235
1228
 
1236
1229
    def test_invalid_size(self):
1237
1230
        transport = MockTransport([
1239
1232
            "a option 1 1v :",
1240
1233
            ])
1241
1234
        index = self.get_knit_index(transport, 'filename', 'r')
1242
 
        try:
1243
 
            self.assertRaises(errors.KnitCorrupt, index.keys)
1244
 
        except TypeError, e:
1245
 
            if (str(e) == ('exceptions must be strings, classes, or instances,'
1246
 
                           ' not exceptions.ValueError')
1247
 
                and sys.version_info[0:2] >= (2,5)):
1248
 
                self.knownFailure('Pyrex <0.9.5 fails with TypeError when'
1249
 
                                  ' raising new style exceptions with python'
1250
 
                                  ' >=2.5')
1251
 
            else:
1252
 
                raise
 
1235
        self.assertRaises(errors.KnitCorrupt, index.keys)
1253
1236
 
1254
1237
    def test_scan_unvalidated_index_not_implemented(self):
1255
1238
        transport = MockTransport()
1267
1250
            "b option 10 10 0", # This line isn't terminated, ignored
1268
1251
            ])
1269
1252
        index = self.get_knit_index(transport, "filename", "r")
1270
 
        self.assertEqual(set([('a',)]), index.keys())
 
1253
        self.assertEqual({('a',)}, index.keys())
1271
1254
 
1272
1255
    def test_skip_incomplete_record(self):
1273
1256
        # A line with bogus data should just be skipped
1278
1261
            "c option 20 10 0 :", # Properly terminated, and starts with '\n'
1279
1262
            ])
1280
1263
        index = self.get_knit_index(transport, "filename", "r")
1281
 
        self.assertEqual(set([('a',), ('c',)]), index.keys())
 
1264
        self.assertEqual({('a',), ('c',)}, index.keys())
1282
1265
 
1283
1266
    def test_trailing_characters(self):
1284
1267
        # A line with bogus data should just be skipped
1289
1272
            "c option 20 10 0 :", # Properly terminated, and starts with '\n'
1290
1273
            ])
1291
1274
        index = self.get_knit_index(transport, "filename", "r")
1292
 
        self.assertEqual(set([('a',), ('c',)]), index.keys())
 
1275
        self.assertEqual({('a',), ('c',)}, index.keys())
1293
1276
 
1294
1277
 
1295
1278
class LowLevelKnitIndexTests_c(LowLevelKnitIndexTests):
1298
1281
 
1299
1282
    def get_knit_index(self, transport, name, mode):
1300
1283
        mapper = ConstantMapper(name)
1301
 
        from bzrlib._knit_load_data_pyx import _load_data_c
 
1284
        from ..bzr._knit_load_data_pyx import _load_data_c
1302
1285
        self.overrideAttr(knit, '_load_data', _load_data_c)
1303
1286
        allow_writes = lambda: mode == 'w'
1304
1287
        return _KndxIndex(transport, mapper, lambda:None,
1525
1508
            'a-2 fulltext 0 0 0 :\n'
1526
1509
            'a-3 fulltext 0 0 1 :'
1527
1510
            )
1528
 
        self.assertEqual(set([('a-3',), ('a-1',), ('a-2',)]), idx.keys())
 
1511
        self.assertEqual({('a-3',), ('a-1',), ('a-2',)}, idx.keys())
1529
1512
        self.assertEqual({
1530
1513
            ('a-1',): ((('a-1',), 0, 0), None, (), ('fulltext', False)),
1531
1514
            ('a-2',): ((('a-2',), 0, 0), None, (('a-1',),), ('fulltext', False)),
1563
1546
 
1564
1547
        # Assert the pre-condition
1565
1548
        def assertA1Only():
1566
 
            self.assertEqual(set([('a-1',)]), set(idx.keys()))
 
1549
            self.assertEqual({('a-1',)}, set(idx.keys()))
1567
1550
            self.assertEqual(
1568
1551
                {('a-1',): ((('a-1',), 0, 0), None, (), ('fulltext', False))},
1569
1552
                idx.get_build_details([('a-1',)]))
1579
1562
        # could leave an empty .kndx file, which bzr would later claim was a
1580
1563
        # corrupted file since the header was not present. In reality, the file
1581
1564
        # just wasn't created, so it should be ignored.
1582
 
        t = get_transport('.')
 
1565
        t = transport.get_transport_from_path('.')
1583
1566
        t.put_bytes('test.kndx', '')
1584
1567
 
1585
1568
        knit = self.make_test_knit()
1586
1569
 
1587
1570
    def test_knit_index_checks_header(self):
1588
 
        t = get_transport('.')
 
1571
        t = transport.get_transport_from_path('.')
1589
1572
        t.put_bytes('test.kndx', '# not really a knit header\n\n')
1590
1573
        k = self.make_test_knit()
1591
1574
        self.assertRaises(KnitHeaderError, k.keys)
1638
1621
 
1639
1622
    def test_keys(self):
1640
1623
        index = self.two_graph_index()
1641
 
        self.assertEqual(set([('tail',), ('tip',), ('parent',), ('separate',)]),
 
1624
        self.assertEqual({('tail',), ('tip',), ('parent',), ('separate',)},
1642
1625
            set(index.keys()))
1643
1626
 
1644
1627
    def test_get_position(self):
1940
1923
 
1941
1924
    def test_keys(self):
1942
1925
        index = self.two_graph_index()
1943
 
        self.assertEqual(set([('tail',), ('tip',), ('parent',), ('separate',)]),
 
1926
        self.assertEqual({('tail',), ('tip',), ('parent',), ('separate',)},
1944
1927
            set(index.keys()))
1945
1928
 
1946
1929
    def test_get_position(self):
2104
2087
            }
2105
2088
        self.assertGroupKeysForIo([([f_a], set())],
2106
2089
                                  [f_a], [], positions)
2107
 
        self.assertGroupKeysForIo([([f_a], set([f_a]))],
 
2090
        self.assertGroupKeysForIo([([f_a], {f_a})],
2108
2091
                                  [f_a], [f_a], positions)
2109
2092
        self.assertGroupKeysForIo([([f_a, f_b], set([]))],
2110
2093
                                  [f_a, f_b], [], positions)
2111
 
        self.assertGroupKeysForIo([([f_a, f_b], set([f_b]))],
 
2094
        self.assertGroupKeysForIo([([f_a, f_b], {f_b})],
2112
2095
                                  [f_a, f_b], [f_b], positions)
2113
2096
        self.assertGroupKeysForIo([([f_a, f_b, g_a, g_b], set())],
2114
2097
                                  [f_a, g_a, f_b, g_b], [], positions)
2211
2194
        self.assertEqual([(key_basis, 'foo\n'), (key_basis, 'bar\n')], details)
2212
2195
        # Not optimised to date:
2213
2196
        # self.assertEqual([("annotate", key_basis)], basis.calls)
2214
 
        self.assertEqual([('get_parent_map', set([key_basis])),
2215
 
            ('get_parent_map', set([key_basis])),
 
2197
        self.assertEqual([('get_parent_map', {key_basis}),
 
2198
            ('get_parent_map', {key_basis}),
2216
2199
            ('get_record_stream', [key_basis], 'topological', True)],
2217
2200
            basis.calls)
2218
2201
 
2238
2221
        parent_map = test.get_parent_map([key, key_basis, key_missing])
2239
2222
        self.assertEqual({key: (),
2240
2223
            key_basis: ()}, parent_map)
2241
 
        self.assertEqual([("get_parent_map", set([key_basis, key_missing]))],
 
2224
        self.assertEqual([("get_parent_map", {key_basis, key_missing})],
2242
2225
            basis.calls)
2243
2226
 
2244
2227
    def test_get_record_stream_unordered_fulltexts(self):
2275
2258
        # It's not strictly minimal, but it seems reasonable for now for it to
2276
2259
        # ask which fallbacks have which parents.
2277
2260
        self.assertEqual([
2278
 
            ("get_parent_map", set([key_basis, key_missing])),
 
2261
            ("get_parent_map", {key_basis, key_missing}),
2279
2262
            ("get_record_stream", [key_basis], 'unordered', True)],
2280
2263
            calls)
2281
2264
 
2312
2295
                source = test
2313
2296
            else:
2314
2297
                source = basis
2315
 
            record = source.get_record_stream([result[0]], 'unordered',
2316
 
                True).next()
 
2298
            record = next(source.get_record_stream([result[0]], 'unordered',
 
2299
                True))
2317
2300
            self.assertEqual(record.key, result[0])
2318
2301
            self.assertEqual(record.sha1, result[1])
2319
2302
            # We used to check that the storage kind matched, but actually it
2324
2307
        # It's not strictly minimal, but it seems reasonable for now for it to
2325
2308
        # ask which fallbacks have which parents.
2326
2309
        self.assertEqual([
2327
 
            ("get_parent_map", set([key_basis, key_basis_2, key_missing])),
 
2310
            ("get_parent_map", {key_basis, key_basis_2, key_missing}),
2328
2311
            # topological is requested from the fallback, because that is what
2329
2312
            # was requested at the top level.
2330
2313
            ("get_record_stream", [key_basis_2, key_basis], 'topological', True)],
2362
2345
        # It's not strictly minimal, but it seems reasonable for now for it to
2363
2346
        # ask which fallbacks have which parents.
2364
2347
        self.assertEqual([
2365
 
            ("get_parent_map", set([key_basis, key_missing])),
 
2348
            ("get_parent_map", {key_basis, key_missing}),
2366
2349
            ("get_record_stream", [key_basis], 'unordered', False)],
2367
2350
            calls)
2368
2351
 
2399
2382
                source = test
2400
2383
            else:
2401
2384
                source = basis
2402
 
            record = source.get_record_stream([result[0]], 'unordered',
2403
 
                False).next()
 
2385
            record = next(source.get_record_stream([result[0]], 'unordered',
 
2386
                False))
2404
2387
            self.assertEqual(record.key, result[0])
2405
2388
            self.assertEqual(record.sha1, result[1])
2406
2389
            self.assertEqual(record.storage_kind, result[2])
2408
2391
        # It's not strictly minimal, but it seems reasonable for now for it to
2409
2392
        # ask which fallbacks have which parents.
2410
2393
        self.assertEqual([
2411
 
            ("get_parent_map", set([key_basis, key_basis_2, key_missing])),
 
2394
            ("get_parent_map", {key_basis, key_basis_2, key_missing}),
2412
2395
            ("get_record_stream", [key_basis_2, key_basis], 'topological', False)],
2413
2396
            calls)
2414
2397
 
2419
2402
        key_basis = ('bar',)
2420
2403
        key_missing = ('missing',)
2421
2404
        test.add_lines(key, (), ['foo\n'])
2422
 
        key_sha1sum = osutils.sha('foo\n').hexdigest()
 
2405
        key_sha1sum = osutils.sha_string('foo\n')
2423
2406
        sha1s = test.get_sha1s([key])
2424
2407
        self.assertEqual({key: key_sha1sum}, sha1s)
2425
2408
        self.assertEqual([], basis.calls)
2427
2410
        # directly (rather than via text reconstruction) so that remote servers
2428
2411
        # etc don't have to answer with full content.
2429
2412
        basis.add_lines(key_basis, (), ['foo\n', 'bar\n'])
2430
 
        basis_sha1sum = osutils.sha('foo\nbar\n').hexdigest()
 
2413
        basis_sha1sum = osutils.sha_string('foo\nbar\n')
2431
2414
        basis.calls = []
2432
2415
        sha1s = test.get_sha1s([key, key_missing, key_basis])
2433
2416
        self.assertEqual({key: key_sha1sum,
2434
2417
            key_basis: basis_sha1sum}, sha1s)
2435
 
        self.assertEqual([("get_sha1s", set([key_basis, key_missing]))],
 
2418
        self.assertEqual([("get_sha1s", {key_basis, key_missing})],
2436
2419
            basis.calls)
2437
2420
 
2438
2421
    def test_insert_record_stream(self):
2451
2434
        test.insert_record_stream(stream)
2452
2435
        # XXX: this does somewhat too many calls in making sure of whether it
2453
2436
        # has to recreate the full text.
2454
 
        self.assertEqual([("get_parent_map", set([key_basis])),
2455
 
             ('get_parent_map', set([key_basis])),
 
2437
        self.assertEqual([("get_parent_map", {key_basis}),
 
2438
             ('get_parent_map', {key_basis}),
2456
2439
             ('get_record_stream', [key_basis], 'unordered', True)],
2457
2440
            basis.calls)
2458
2441
        self.assertEqual({key_delta:(key_basis,)},
2471
2454
        basis.calls = []
2472
2455
        lines = list(test.iter_lines_added_or_present_in_keys([key1]))
2473
2456
        self.assertEqual([("foo\n", key1)], lines)
2474
 
        self.assertEqual([("iter_lines_added_or_present_in_keys", set([key1]))],
 
2457
        self.assertEqual([("iter_lines_added_or_present_in_keys", {key1})],
2475
2458
            basis.calls)
2476
2459
        # keys in both are not duplicated:
2477
2460
        test.add_lines(key2, (), ["bar\n"])
2493
2476
        basis.add_lines(key1, (), [])
2494
2477
        basis.calls = []
2495
2478
        keys = test.keys()
2496
 
        self.assertEqual(set([key1]), set(keys))
 
2479
        self.assertEqual({key1}, set(keys))
2497
2480
        self.assertEqual([("keys",)], basis.calls)
2498
2481
        # keys in both are not duplicated:
2499
2482
        test.add_lines(key2, (), [])
2501
2484
        basis.calls = []
2502
2485
        keys = test.keys()
2503
2486
        self.assertEqual(2, len(keys))
2504
 
        self.assertEqual(set([key1, key2]), set(keys))
 
2487
        self.assertEqual({key1, key2}, set(keys))
2505
2488
        self.assertEqual([("keys",)], basis.calls)
2506
2489
 
2507
2490
    def test_add_mpdiffs(self):
2519
2502
        diffs = source.make_mpdiffs([key_delta])
2520
2503
        test.add_mpdiffs([(key_delta, (key_basis,),
2521
2504
            source.get_sha1s([key_delta])[key_delta], diffs[0])])
2522
 
        self.assertEqual([("get_parent_map", set([key_basis])),
 
2505
        self.assertEqual([("get_parent_map", {key_basis}),
2523
2506
            ('get_record_stream', [key_basis], 'unordered', True),],
2524
2507
            basis.calls)
2525
2508
        self.assertEqual({key_delta:(key_basis,)},
2547
2530
            diffs)
2548
2531
        self.assertEqual(3, len(basis.calls))
2549
2532
        self.assertEqual([
2550
 
            ("get_parent_map", set([key_left, key_right])),
2551
 
            ("get_parent_map", set([key_left, key_right])),
 
2533
            ("get_parent_map", {key_left, key_right}),
 
2534
            ("get_parent_map", {key_left, key_right}),
2552
2535
            ],
2553
2536
            basis.calls[:-1])
2554
2537
        last_call = basis.calls[-1]
2555
2538
        self.assertEqual('get_record_stream', last_call[0])
2556
 
        self.assertEqual(set([key_left, key_right]), set(last_call[1]))
 
2539
        self.assertEqual({key_left, key_right}, set(last_call[1]))
2557
2540
        self.assertEqual('topological', last_call[2])
2558
2541
        self.assertEqual(True, last_call[3])
2559
2542