~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_knit.py

  • Committer: INADA Naoki
  • Date: 2011-05-18 06:01:08 UTC
  • mto: This revision was merged to the branch mainline in revision 5894.
  • Revision ID: songofacandy@gmail.com-20110518060108-86t2kffcrzu0nf6i
Update Japanese docs.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2007 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
17
17
"""Tests for Knit data structure"""
18
18
 
19
19
from cStringIO import StringIO
20
 
import difflib
21
20
import gzip
22
21
import sys
23
22
 
24
23
from bzrlib import (
25
24
    errors,
26
 
    generate_ids,
27
25
    knit,
28
26
    multiparent,
29
27
    osutils,
30
28
    pack,
 
29
    tests,
 
30
    transport,
31
31
    )
32
32
from bzrlib.errors import (
33
 
    RevisionAlreadyPresent,
34
33
    KnitHeaderError,
35
 
    RevisionNotPresent,
36
34
    NoSuchFile,
37
35
    )
38
36
from bzrlib.index import *
39
37
from bzrlib.knit import (
40
38
    AnnotatedKnitContent,
41
39
    KnitContent,
42
 
    KnitSequenceMatcher,
43
40
    KnitVersionedFiles,
44
41
    PlainKnitContent,
45
42
    _VFContentMapGenerator,
46
 
    _DirectPackAccess,
47
43
    _KndxIndex,
48
44
    _KnitGraphIndex,
49
45
    _KnitKeyAccess,
50
46
    make_file_factory,
51
47
    )
52
 
from bzrlib.repofmt import pack_repo
 
48
from bzrlib.patiencediff import PatienceSequenceMatcher
 
49
from bzrlib.repofmt import (
 
50
    knitpack_repo,
 
51
    pack_repo,
 
52
    )
53
53
from bzrlib.tests import (
54
 
    Feature,
55
 
    KnownFailure,
56
54
    TestCase,
57
55
    TestCaseWithMemoryTransport,
58
56
    TestCaseWithTransport,
59
57
    TestNotApplicable,
60
58
    )
61
 
from bzrlib.transport import get_transport
62
 
from bzrlib.transport.memory import MemoryTransport
63
 
from bzrlib.tuned_gzip import GzipFile
64
59
from bzrlib.versionedfile import (
65
60
    AbsentContentFactory,
66
61
    ConstantMapper,
69
64
    )
70
65
 
71
66
 
72
 
class _CompiledKnitFeature(Feature):
73
 
 
74
 
    def _probe(self):
75
 
        try:
76
 
            import bzrlib._knit_load_data_pyx
77
 
        except ImportError:
78
 
            return False
79
 
        return True
80
 
 
81
 
    def feature_name(self):
82
 
        return 'bzrlib._knit_load_data_pyx'
83
 
 
84
 
CompiledKnitFeature = _CompiledKnitFeature()
 
67
compiled_knit_feature = tests.ModuleAvailableFeature(
 
68
                            'bzrlib._knit_load_data_pyx')
85
69
 
86
70
 
87
71
class KnitContentTestsMixin(object):
116
100
        line_delta = source_content.line_delta(target_content)
117
101
        delta_blocks = list(KnitContent.get_line_delta_blocks(line_delta,
118
102
            source_lines, target_lines))
119
 
        matcher = KnitSequenceMatcher(None, source_lines, target_lines)
120
 
        matcher_blocks = list(list(matcher.get_matching_blocks()))
 
103
        matcher = PatienceSequenceMatcher(None, source_lines, target_lines)
 
104
        matcher_blocks = list(matcher.get_matching_blocks())
121
105
        self.assertEqual(matcher_blocks, delta_blocks)
122
106
 
123
107
    def test_get_line_delta_blocks(self):
343
327
            transport.append_bytes(packname, bytes)
344
328
        writer = pack.ContainerWriter(write_data)
345
329
        writer.begin()
346
 
        access = _DirectPackAccess({})
 
330
        access = pack_repo._DirectPackAccess({})
347
331
        access.set_writer(writer, index, (transport, packname))
348
332
        return access, writer
349
333
 
356
340
        writer.end()
357
341
        return memos
358
342
 
 
343
    def test_pack_collection_pack_retries(self):
 
344
        """An explicit pack of a pack collection succeeds even when a
 
345
        concurrent pack happens.
 
346
        """
 
347
        builder = self.make_branch_builder('.')
 
348
        builder.start_series()
 
349
        builder.build_snapshot('rev-1', None, [
 
350
            ('add', ('', 'root-id', 'directory', None)),
 
351
            ('add', ('file', 'file-id', 'file', 'content\nrev 1\n')),
 
352
            ])
 
353
        builder.build_snapshot('rev-2', ['rev-1'], [
 
354
            ('modify', ('file-id', 'content\nrev 2\n')),
 
355
            ])
 
356
        builder.build_snapshot('rev-3', ['rev-2'], [
 
357
            ('modify', ('file-id', 'content\nrev 3\n')),
 
358
            ])
 
359
        self.addCleanup(builder.finish_series)
 
360
        b = builder.get_branch()
 
361
        self.addCleanup(b.lock_write().unlock)
 
362
        repo = b.repository
 
363
        collection = repo._pack_collection
 
364
        # Concurrently repack the repo.
 
365
        reopened_repo = repo.bzrdir.open_repository()
 
366
        reopened_repo.pack()
 
367
        # Pack the new pack.
 
368
        collection.pack()
 
369
 
359
370
    def make_vf_for_retrying(self):
360
371
        """Create 3 packs and a reload function.
361
372
 
388
399
        collection = repo._pack_collection
389
400
        collection.ensure_loaded()
390
401
        orig_packs = collection.packs
391
 
        packer = pack_repo.Packer(collection, orig_packs, '.testpack')
 
402
        packer = knitpack_repo.KnitPacker(collection, orig_packs, '.testpack')
392
403
        new_pack = packer.pack()
393
404
        # forget about the new pack
394
405
        collection.reset()
447
458
        memos.extend(access.add_raw_records([('key', 5)], 'alpha'))
448
459
        writer.end()
449
460
        transport = self.get_transport()
450
 
        access = _DirectPackAccess({"FOO":(transport, 'packfile'),
 
461
        access = pack_repo._DirectPackAccess({"FOO":(transport, 'packfile'),
451
462
            "FOOBAR":(transport, 'pack2'),
452
463
            "BAZ":(transport, 'pack3')})
453
464
        self.assertEqual(['1234567890', '12345', 'alpha'],
463
474
 
464
475
    def test_set_writer(self):
465
476
        """The writer should be settable post construction."""
466
 
        access = _DirectPackAccess({})
 
477
        access = pack_repo._DirectPackAccess({})
467
478
        transport = self.get_transport()
468
479
        packname = 'packfile'
469
480
        index = 'foo'
481
492
        transport = self.get_transport()
482
493
        reload_called, reload_func = self.make_reload_func()
483
494
        # Note that the index key has changed from 'foo' to 'bar'
484
 
        access = _DirectPackAccess({'bar':(transport, 'packname')},
 
495
        access = pack_repo._DirectPackAccess({'bar':(transport, 'packname')},
485
496
                                   reload_func=reload_func)
486
497
        e = self.assertListRaises(errors.RetryWithNewPacks,
487
498
                                  access.get_raw_records, memos)
496
507
        memos = self.make_pack_file()
497
508
        transport = self.get_transport()
498
509
        # Note that the index key has changed from 'foo' to 'bar'
499
 
        access = _DirectPackAccess({'bar':(transport, 'packname')})
 
510
        access = pack_repo._DirectPackAccess({'bar':(transport, 'packname')})
500
511
        e = self.assertListRaises(KeyError, access.get_raw_records, memos)
501
512
 
502
513
    def test_missing_file_raises_retry(self):
504
515
        transport = self.get_transport()
505
516
        reload_called, reload_func = self.make_reload_func()
506
517
        # Note that the 'filename' has been changed to 'different-packname'
507
 
        access = _DirectPackAccess({'foo':(transport, 'different-packname')},
508
 
                                   reload_func=reload_func)
 
518
        access = pack_repo._DirectPackAccess(
 
519
            {'foo':(transport, 'different-packname')},
 
520
            reload_func=reload_func)
509
521
        e = self.assertListRaises(errors.RetryWithNewPacks,
510
522
                                  access.get_raw_records, memos)
511
523
        # The file has gone missing, so we assume we need to reload
519
531
        memos = self.make_pack_file()
520
532
        transport = self.get_transport()
521
533
        # Note that the 'filename' has been changed to 'different-packname'
522
 
        access = _DirectPackAccess({'foo':(transport, 'different-packname')})
 
534
        access = pack_repo._DirectPackAccess(
 
535
            {'foo': (transport, 'different-packname')})
523
536
        e = self.assertListRaises(errors.NoSuchFile,
524
537
                                  access.get_raw_records, memos)
525
538
 
529
542
        failing_transport = MockReadvFailingTransport(
530
543
                                [transport.get_bytes('packname')])
531
544
        reload_called, reload_func = self.make_reload_func()
532
 
        access = _DirectPackAccess({'foo':(failing_transport, 'packname')},
533
 
                                   reload_func=reload_func)
 
545
        access = pack_repo._DirectPackAccess(
 
546
            {'foo': (failing_transport, 'packname')},
 
547
            reload_func=reload_func)
534
548
        # Asking for a single record will not trigger the Mock failure
535
549
        self.assertEqual(['1234567890'],
536
550
            list(access.get_raw_records(memos[:1])))
552
566
        failing_transport = MockReadvFailingTransport(
553
567
                                [transport.get_bytes('packname')])
554
568
        reload_called, reload_func = self.make_reload_func()
555
 
        access = _DirectPackAccess({'foo':(failing_transport, 'packname')})
 
569
        access = pack_repo._DirectPackAccess(
 
570
            {'foo':(failing_transport, 'packname')})
556
571
        # Asking for a single record will not trigger the Mock failure
557
572
        self.assertEqual(['1234567890'],
558
573
            list(access.get_raw_records(memos[:1])))
563
578
                                  access.get_raw_records, memos)
564
579
 
565
580
    def test_reload_or_raise_no_reload(self):
566
 
        access = _DirectPackAccess({}, reload_func=None)
 
581
        access = pack_repo._DirectPackAccess({}, reload_func=None)
567
582
        retry_exc = self.make_retry_exception()
568
583
        # Without a reload_func, we will just re-raise the original exception
569
584
        self.assertRaises(_TestException, access.reload_or_raise, retry_exc)
570
585
 
571
586
    def test_reload_or_raise_reload_changed(self):
572
587
        reload_called, reload_func = self.make_reload_func(return_val=True)
573
 
        access = _DirectPackAccess({}, reload_func=reload_func)
 
588
        access = pack_repo._DirectPackAccess({}, reload_func=reload_func)
574
589
        retry_exc = self.make_retry_exception()
575
590
        access.reload_or_raise(retry_exc)
576
591
        self.assertEqual([1], reload_called)
580
595
 
581
596
    def test_reload_or_raise_reload_no_change(self):
582
597
        reload_called, reload_func = self.make_reload_func(return_val=False)
583
 
        access = _DirectPackAccess({}, reload_func=reload_func)
 
598
        access = pack_repo._DirectPackAccess({}, reload_func=reload_func)
584
599
        retry_exc = self.make_retry_exception()
585
600
        # If reload_occurred is False, then we consider it an error to have
586
601
        # reload_func() return False (no changes).
717
732
 
718
733
    def make_multiple_records(self):
719
734
        """Create the content for multiple records."""
720
 
        sha1sum = osutils.sha('foo\nbar\n').hexdigest()
 
735
        sha1sum = osutils.sha_string('foo\nbar\n')
721
736
        total_txt = []
722
737
        gz_txt = self.create_gz_content('version rev-id-1 2 %s\n'
723
738
                                        'foo\n'
726
741
                                        % (sha1sum,))
727
742
        record_1 = (0, len(gz_txt), sha1sum)
728
743
        total_txt.append(gz_txt)
729
 
        sha1sum = osutils.sha('baz\n').hexdigest()
 
744
        sha1sum = osutils.sha_string('baz\n')
730
745
        gz_txt = self.create_gz_content('version rev-id-2 1 %s\n'
731
746
                                        'baz\n'
732
747
                                        'end rev-id-2\n'
736
751
        return total_txt, record_1, record_2
737
752
 
738
753
    def test_valid_knit_data(self):
739
 
        sha1sum = osutils.sha('foo\nbar\n').hexdigest()
 
754
        sha1sum = osutils.sha_string('foo\nbar\n')
740
755
        gz_txt = self.create_gz_content('version rev-id-1 2 %s\n'
741
756
                                        'foo\n'
742
757
                                        'bar\n'
773
788
                         raw_contents)
774
789
 
775
790
    def test_not_enough_lines(self):
776
 
        sha1sum = osutils.sha('foo\n').hexdigest()
 
791
        sha1sum = osutils.sha_string('foo\n')
777
792
        # record says 2 lines data says 1
778
793
        gz_txt = self.create_gz_content('version rev-id-1 2 %s\n'
779
794
                                        'foo\n'
791
806
        self.assertEqual([(('rev-id-1',),  gz_txt, sha1sum)], raw_contents)
792
807
 
793
808
    def test_too_many_lines(self):
794
 
        sha1sum = osutils.sha('foo\nbar\n').hexdigest()
 
809
        sha1sum = osutils.sha_string('foo\nbar\n')
795
810
        # record says 1 lines data says 2
796
811
        gz_txt = self.create_gz_content('version rev-id-1 1 %s\n'
797
812
                                        'foo\n'
810
825
        self.assertEqual([(('rev-id-1',), gz_txt, sha1sum)], raw_contents)
811
826
 
812
827
    def test_mismatched_version_id(self):
813
 
        sha1sum = osutils.sha('foo\nbar\n').hexdigest()
 
828
        sha1sum = osutils.sha_string('foo\nbar\n')
814
829
        gz_txt = self.create_gz_content('version rev-id-1 2 %s\n'
815
830
                                        'foo\n'
816
831
                                        'bar\n'
829
844
            knit._read_records_iter_raw(records))
830
845
 
831
846
    def test_uncompressed_data(self):
832
 
        sha1sum = osutils.sha('foo\nbar\n').hexdigest()
 
847
        sha1sum = osutils.sha_string('foo\nbar\n')
833
848
        txt = ('version rev-id-1 2 %s\n'
834
849
               'foo\n'
835
850
               'bar\n'
849
864
            knit._read_records_iter_raw(records))
850
865
 
851
866
    def test_corrupted_data(self):
852
 
        sha1sum = osutils.sha('foo\nbar\n').hexdigest()
 
867
        sha1sum = osutils.sha_string('foo\nbar\n')
853
868
        gz_txt = self.create_gz_content('version rev-id-1 2 %s\n'
854
869
                                        'foo\n'
855
870
                                        'bar\n'
872
887
 
873
888
    def get_knit_index(self, transport, name, mode):
874
889
        mapper = ConstantMapper(name)
875
 
        orig = knit._load_data
876
 
        def reset():
877
 
            knit._load_data = orig
878
 
        self.addCleanup(reset)
879
890
        from bzrlib._knit_load_data_py import _load_data_py
880
 
        knit._load_data = _load_data_py
 
891
        self.overrideAttr(knit, '_load_data', _load_data_py)
881
892
        allow_writes = lambda: 'w' in mode
882
893
        return _KndxIndex(transport, mapper, lambda:None, allow_writes, lambda:True)
883
894
 
1181
1192
            self.assertRaises(errors.KnitCorrupt, index.keys)
1182
1193
        except TypeError, e:
1183
1194
            if (str(e) == ('exceptions must be strings, classes, or instances,'
1184
 
                           ' not exceptions.IndexError')
1185
 
                and sys.version_info[0:2] >= (2,5)):
 
1195
                           ' not exceptions.IndexError')):
1186
1196
                self.knownFailure('Pyrex <0.9.5 fails with TypeError when'
1187
1197
                                  ' raising new style exceptions with python'
1188
1198
                                  ' >=2.5')
1201
1211
            self.assertRaises(errors.KnitCorrupt, index.keys)
1202
1212
        except TypeError, e:
1203
1213
            if (str(e) == ('exceptions must be strings, classes, or instances,'
1204
 
                           ' not exceptions.ValueError')
1205
 
                and sys.version_info[0:2] >= (2,5)):
 
1214
                           ' not exceptions.ValueError')):
1206
1215
                self.knownFailure('Pyrex <0.9.5 fails with TypeError when'
1207
1216
                                  ' raising new style exceptions with python'
1208
1217
                                  ' >=2.5')
1221
1230
            self.assertRaises(errors.KnitCorrupt, index.keys)
1222
1231
        except TypeError, e:
1223
1232
            if (str(e) == ('exceptions must be strings, classes, or instances,'
1224
 
                           ' not exceptions.ValueError')
1225
 
                and sys.version_info[0:2] >= (2,5)):
 
1233
                           ' not exceptions.ValueError')):
1226
1234
                self.knownFailure('Pyrex <0.9.5 fails with TypeError when'
1227
1235
                                  ' raising new style exceptions with python'
1228
1236
                                  ' >=2.5')
1239
1247
            self.assertRaises(errors.KnitCorrupt, index.keys)
1240
1248
        except TypeError, e:
1241
1249
            if (str(e) == ('exceptions must be strings, classes, or instances,'
1242
 
                           ' not exceptions.ValueError')
1243
 
                and sys.version_info[0:2] >= (2,5)):
 
1250
                           ' not exceptions.ValueError')):
1244
1251
                self.knownFailure('Pyrex <0.9.5 fails with TypeError when'
1245
1252
                                  ' raising new style exceptions with python'
1246
1253
                                  ' >=2.5')
1257
1264
            self.assertRaises(errors.KnitCorrupt, index.keys)
1258
1265
        except TypeError, e:
1259
1266
            if (str(e) == ('exceptions must be strings, classes, or instances,'
1260
 
                           ' not exceptions.ValueError')
1261
 
                and sys.version_info[0:2] >= (2,5)):
 
1267
                           ' not exceptions.ValueError')):
1262
1268
                self.knownFailure('Pyrex <0.9.5 fails with TypeError when'
1263
1269
                                  ' raising new style exceptions with python'
1264
1270
                                  ' >=2.5')
1308
1314
 
1309
1315
class LowLevelKnitIndexTests_c(LowLevelKnitIndexTests):
1310
1316
 
1311
 
    _test_needs_features = [CompiledKnitFeature]
 
1317
    _test_needs_features = [compiled_knit_feature]
1312
1318
 
1313
1319
    def get_knit_index(self, transport, name, mode):
1314
1320
        mapper = ConstantMapper(name)
1315
 
        orig = knit._load_data
1316
 
        def reset():
1317
 
            knit._load_data = orig
1318
 
        self.addCleanup(reset)
1319
1321
        from bzrlib._knit_load_data_pyx import _load_data_c
1320
 
        knit._load_data = _load_data_c
 
1322
        self.overrideAttr(knit, '_load_data', _load_data_c)
1321
1323
        allow_writes = lambda: mode == 'w'
1322
 
        return _KndxIndex(transport, mapper, lambda:None, allow_writes, lambda:True)
 
1324
        return _KndxIndex(transport, mapper, lambda:None,
 
1325
                          allow_writes, lambda:True)
1323
1326
 
1324
1327
 
1325
1328
class Test_KnitAnnotator(TestCaseWithMemoryTransport):
1596
1599
        # could leave an empty .kndx file, which bzr would later claim was a
1597
1600
        # corrupted file since the header was not present. In reality, the file
1598
1601
        # just wasn't created, so it should be ignored.
1599
 
        t = get_transport('.')
 
1602
        t = transport.get_transport('.')
1600
1603
        t.put_bytes('test.kndx', '')
1601
1604
 
1602
1605
        knit = self.make_test_knit()
1603
1606
 
1604
1607
    def test_knit_index_checks_header(self):
1605
 
        t = get_transport('.')
 
1608
        t = transport.get_transport('.')
1606
1609
        t.put_bytes('test.kndx', '# not really a knit header\n\n')
1607
1610
        k = self.make_test_knit()
1608
1611
        self.assertRaises(KnitHeaderError, k.keys)
2436
2439
        key_basis = ('bar',)
2437
2440
        key_missing = ('missing',)
2438
2441
        test.add_lines(key, (), ['foo\n'])
2439
 
        key_sha1sum = osutils.sha('foo\n').hexdigest()
 
2442
        key_sha1sum = osutils.sha_string('foo\n')
2440
2443
        sha1s = test.get_sha1s([key])
2441
2444
        self.assertEqual({key: key_sha1sum}, sha1s)
2442
2445
        self.assertEqual([], basis.calls)
2444
2447
        # directly (rather than via text reconstruction) so that remote servers
2445
2448
        # etc don't have to answer with full content.
2446
2449
        basis.add_lines(key_basis, (), ['foo\n', 'bar\n'])
2447
 
        basis_sha1sum = osutils.sha('foo\nbar\n').hexdigest()
 
2450
        basis_sha1sum = osutils.sha_string('foo\nbar\n')
2448
2451
        basis.calls = []
2449
2452
        sha1s = test.get_sha1s([key, key_missing, key_basis])
2450
2453
        self.assertEqual({key: key_sha1sum,