~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_groupcompress.py

  • Committer: John Arbash Meinel
  • Date: 2009-06-19 17:53:37 UTC
  • mto: This revision was merged to the branch mainline in revision 4466.
  • Revision ID: john@arbash-meinel.com-20090619175337-uozt3bntdd48lh4z
Update time_graph to use X:1 ratios rather than 0.xxx ratios.
It is just easier to track now that the new code is much faster.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2008, 2009, 2010 Canonical Ltd
 
1
# Copyright (C) 2008, 2009 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
25
25
    index as _mod_index,
26
26
    osutils,
27
27
    tests,
28
 
    trace,
29
28
    versionedfile,
30
29
    )
31
30
from bzrlib.osutils import sha_string
32
 
from bzrlib.tests.test__groupcompress import compiled_groupcompress_feature
 
31
from bzrlib.tests.test__groupcompress import CompiledGroupCompressFeature
33
32
 
34
33
 
35
34
def load_tests(standard_tests, module, loader):
39
38
    scenarios = [
40
39
        ('python', {'compressor': groupcompress.PythonGroupCompressor}),
41
40
        ]
42
 
    if compiled_groupcompress_feature.available():
 
41
    if CompiledGroupCompressFeature.available():
43
42
        scenarios.append(('C',
44
43
            {'compressor': groupcompress.PyrexGroupCompressor}))
45
44
    return tests.multiply_tests(to_adapt, scenarios, result)
135
134
 
136
135
class TestPyrexGroupCompressor(TestGroupCompressor):
137
136
 
138
 
    _test_needs_features = [compiled_groupcompress_feature]
 
137
    _test_needs_features = [CompiledGroupCompressFeature]
139
138
    compressor = groupcompress.PyrexGroupCompressor
140
139
 
141
140
    def test_stats(self):
347
346
        self.assertEqual(z_content, block._z_content)
348
347
        self.assertEqual(content, block._content)
349
348
 
350
 
    def test_to_chunks(self):
351
 
        content_chunks = ['this is some content\n',
352
 
                          'this content will be compressed\n']
353
 
        content_len = sum(map(len, content_chunks))
354
 
        content = ''.join(content_chunks)
355
 
        gcb = groupcompress.GroupCompressBlock()
356
 
        gcb.set_chunked_content(content_chunks, content_len)
357
 
        total_len, block_chunks = gcb.to_chunks()
358
 
        block_bytes = ''.join(block_chunks)
359
 
        self.assertEqual(gcb._z_content_length, len(gcb._z_content))
360
 
        self.assertEqual(total_len, len(block_bytes))
361
 
        self.assertEqual(gcb._content_length, content_len)
362
 
        expected_header =('gcb1z\n' # group compress block v1 zlib
363
 
                          '%d\n' # Length of compressed content
364
 
                          '%d\n' # Length of uncompressed content
365
 
                         ) % (gcb._z_content_length, gcb._content_length)
366
 
        # The first chunk should be the header chunk. It is small, fixed size,
367
 
        # and there is no compelling reason to split it up
368
 
        self.assertEqual(expected_header, block_chunks[0])
369
 
        self.assertStartsWith(block_bytes, expected_header)
370
 
        remaining_bytes = block_bytes[len(expected_header):]
371
 
        raw_bytes = zlib.decompress(remaining_bytes)
372
 
        self.assertEqual(content, raw_bytes)
373
 
 
374
349
    def test_to_bytes(self):
375
350
        content = ('this is some content\n'
376
351
                   'this content will be compressed\n')
388
363
        raw_bytes = zlib.decompress(remaining_bytes)
389
364
        self.assertEqual(content, raw_bytes)
390
365
 
391
 
        # we should get the same results if using the chunked version
392
 
        gcb = groupcompress.GroupCompressBlock()
393
 
        gcb.set_chunked_content(['this is some content\n'
394
 
                                 'this content will be compressed\n'],
395
 
                                 len(content))
396
 
        old_bytes = bytes
397
 
        bytes = gcb.to_bytes()
398
 
        self.assertEqual(old_bytes, bytes)
399
 
 
400
366
    def test_partial_decomp(self):
401
367
        content_chunks = []
402
368
        # We need a sufficient amount of data so that zlib.decompress has
413
379
        z_content = zlib.compress(content)
414
380
        self.assertEqual(57182, len(z_content))
415
381
        block = groupcompress.GroupCompressBlock()
416
 
        block._z_content_chunks = (z_content,)
 
382
        block._z_content = z_content
417
383
        block._z_content_length = len(z_content)
418
384
        block._compressor_name = 'zlib'
419
385
        block._content_length = 158634
442
408
        # And the decompressor is finalized
443
409
        self.assertIs(None, block._z_content_decompressor)
444
410
 
445
 
    def test__ensure_all_content(self):
 
411
    def test_partial_decomp_no_known_length(self):
446
412
        content_chunks = []
447
 
        # We need a sufficient amount of data so that zlib.decompress has
448
 
        # partial decompression to work with. Most auto-generated data
449
 
        # compresses a bit too well, we want a combination, so we combine a sha
450
 
        # hash with compressible data.
451
413
        for i in xrange(2048):
452
414
            next_content = '%d\nThis is a bit of duplicate text\n' % (i,)
453
415
            content_chunks.append(next_content)
458
420
        z_content = zlib.compress(content)
459
421
        self.assertEqual(57182, len(z_content))
460
422
        block = groupcompress.GroupCompressBlock()
461
 
        block._z_content_chunks = (z_content,)
 
423
        block._z_content = z_content
462
424
        block._z_content_length = len(z_content)
463
425
        block._compressor_name = 'zlib'
464
 
        block._content_length = 158634
 
426
        block._content_length = None # Don't tell the decompressed length
465
427
        self.assertIs(None, block._content)
466
 
        # The first _ensure_content got all of the required data
467
 
        block._ensure_content(158634)
 
428
        block._ensure_content(100)
 
429
        self.assertIsNot(None, block._content)
 
430
        # We have decompressed at least 100 bytes
 
431
        self.assertTrue(len(block._content) >= 100)
 
432
        # We have not decompressed the whole content
 
433
        self.assertTrue(len(block._content) < 158634)
 
434
        self.assertEqualDiff(content[:len(block._content)], block._content)
 
435
        # ensuring content that we already have shouldn't cause any more data
 
436
        # to be extracted
 
437
        cur_len = len(block._content)
 
438
        block._ensure_content(cur_len - 10)
 
439
        self.assertEqual(cur_len, len(block._content))
 
440
        # Now we want a bit more content
 
441
        cur_len += 10
 
442
        block._ensure_content(cur_len)
 
443
        self.assertTrue(len(block._content) >= cur_len)
 
444
        self.assertTrue(len(block._content) < 158634)
 
445
        self.assertEqualDiff(content[:len(block._content)], block._content)
 
446
        # And now lets finish
 
447
        block._ensure_content()
468
448
        self.assertEqualDiff(content, block._content)
469
 
        # And we should have released the _z_content_decompressor since it was
470
 
        # fully consumed
 
449
        # And the decompressor is finalized
471
450
        self.assertIs(None, block._z_content_decompressor)
472
451
 
473
452
    def test__dump(self):
483
462
                         ], block._dump())
484
463
 
485
464
 
486
 
class TestCaseWithGroupCompressVersionedFiles(
487
 
        tests.TestCaseWithMemoryTransport):
 
465
class TestCaseWithGroupCompressVersionedFiles(tests.TestCaseWithTransport):
488
466
 
489
467
    def make_test_vf(self, create_graph, keylength=1, do_cleanup=True,
490
 
                     dir='.', inconsistency_fatal=True):
 
468
                     dir='.'):
491
469
        t = self.get_transport(dir)
492
470
        t.ensure_base()
493
471
        vf = groupcompress.make_pack_factory(graph=create_graph,
494
 
            delta=False, keylength=keylength,
495
 
            inconsistency_fatal=inconsistency_fatal)(t)
 
472
            delta=False, keylength=keylength)(t)
496
473
        if do_cleanup:
497
474
            self.addCleanup(groupcompress.cleanup_pack_group, vf)
498
475
        return vf
550
527
                    'as-requested', False)]
551
528
        self.assertEqual([('b',), ('a',), ('d',), ('c',)], keys)
552
529
 
553
 
    def test_insert_record_stream_reuses_blocks(self):
 
530
    def test_insert_record_stream_re_uses_blocks(self):
554
531
        vf = self.make_test_vf(True, dir='source')
555
532
        def grouped_stream(revision_ids, first_parents=()):
556
533
            parents = first_parents
594
571
        vf2 = self.make_test_vf(True, dir='target')
595
572
        # ordering in 'groupcompress' order, should actually swap the groups in
596
573
        # the target vf, but the groups themselves should not be disturbed.
597
 
        def small_size_stream():
598
 
            for record in vf.get_record_stream([(r,) for r in 'abcdefgh'],
599
 
                                               'groupcompress', False):
600
 
                record._manager._full_enough_block_size = \
601
 
                    record._manager._block._content_length
602
 
                yield record
603
 
                        
604
 
        vf2.insert_record_stream(small_size_stream())
 
574
        vf2.insert_record_stream(vf.get_record_stream(
 
575
            [(r,) for r in 'abcdefgh'], 'groupcompress', False))
605
576
        stream = vf2.get_record_stream([(r,) for r in 'abcdefgh'],
606
577
                                       'groupcompress', False)
607
578
        vf2.writer.end()
612
583
                             record._manager._block._z_content)
613
584
        self.assertEqual(8, num_records)
614
585
 
615
 
    def test_insert_record_stream_packs_on_the_fly(self):
616
 
        vf = self.make_test_vf(True, dir='source')
617
 
        def grouped_stream(revision_ids, first_parents=()):
618
 
            parents = first_parents
619
 
            for revision_id in revision_ids:
620
 
                key = (revision_id,)
621
 
                record = versionedfile.FulltextContentFactory(
622
 
                    key, parents, None,
623
 
                    'some content that is\n'
624
 
                    'identical except for\n'
625
 
                    'revision_id:%s\n' % (revision_id,))
626
 
                yield record
627
 
                parents = (key,)
628
 
        # One group, a-d
629
 
        vf.insert_record_stream(grouped_stream(['a', 'b', 'c', 'd']))
630
 
        # Second group, e-h
631
 
        vf.insert_record_stream(grouped_stream(['e', 'f', 'g', 'h'],
632
 
                                               first_parents=(('d',),)))
633
 
        # Now copy the blocks into another vf, and see that the
634
 
        # insert_record_stream rebuilt a new block on-the-fly because of
635
 
        # under-utilization
636
 
        vf2 = self.make_test_vf(True, dir='target')
637
 
        vf2.insert_record_stream(vf.get_record_stream(
638
 
            [(r,) for r in 'abcdefgh'], 'groupcompress', False))
639
 
        stream = vf2.get_record_stream([(r,) for r in 'abcdefgh'],
640
 
                                       'groupcompress', False)
641
 
        vf2.writer.end()
642
 
        num_records = 0
643
 
        # All of the records should be recombined into a single block
644
 
        block = None
645
 
        for record in stream:
646
 
            num_records += 1
647
 
            if block is None:
648
 
                block = record._manager._block
649
 
            else:
650
 
                self.assertIs(block, record._manager._block)
651
 
        self.assertEqual(8, num_records)
652
 
 
653
586
    def test__insert_record_stream_no_reuse_block(self):
654
587
        vf = self.make_test_vf(True, dir='source')
655
588
        def grouped_stream(revision_ids, first_parents=()):
716
649
            frozenset([('parent-1',), ('parent-2',)]),
717
650
            index.get_missing_parents())
718
651
 
719
 
    def make_source_with_b(self, a_parent, path):
720
 
        source = self.make_test_vf(True, dir=path)
721
 
        source.add_lines(('a',), (), ['lines\n'])
722
 
        if a_parent:
723
 
            b_parents = (('a',),)
724
 
        else:
725
 
            b_parents = ()
726
 
        source.add_lines(('b',), b_parents, ['lines\n'])
727
 
        return source
728
 
 
729
 
    def do_inconsistent_inserts(self, inconsistency_fatal):
730
 
        target = self.make_test_vf(True, dir='target',
731
 
                                   inconsistency_fatal=inconsistency_fatal)
732
 
        for x in range(2):
733
 
            source = self.make_source_with_b(x==1, 'source%s' % x)
734
 
            target.insert_record_stream(source.get_record_stream(
735
 
                [('b',)], 'unordered', False))
736
 
 
737
 
    def test_inconsistent_redundant_inserts_warn(self):
738
 
        """Should not insert a record that is already present."""
739
 
        warnings = []
740
 
        def warning(template, args):
741
 
            warnings.append(template % args)
742
 
        _trace_warning = trace.warning
743
 
        trace.warning = warning
744
 
        try:
745
 
            self.do_inconsistent_inserts(inconsistency_fatal=False)
746
 
        finally:
747
 
            trace.warning = _trace_warning
748
 
        self.assertEqual(["inconsistent details in skipped record: ('b',)"
749
 
                          " ('42 32 0 8', ((),)) ('74 32 0 8', ((('a',),),))"],
750
 
                         warnings)
751
 
 
752
 
    def test_inconsistent_redundant_inserts_raises(self):
753
 
        e = self.assertRaises(errors.KnitCorrupt, self.do_inconsistent_inserts,
754
 
                              inconsistency_fatal=True)
755
 
        self.assertContainsRe(str(e), "Knit.* corrupt: inconsistent details"
756
 
                              " in add_records:"
757
 
                              " \('b',\) \('42 32 0 8', \(\(\),\)\) \('74 32"
758
 
                              " 0 8', \(\(\('a',\),\),\)\)")
759
 
 
760
 
    def test_clear_cache(self):
761
 
        vf = self.make_source_with_b(True, 'source')
762
 
        vf.writer.end()
763
 
        for record in vf.get_record_stream([('a',), ('b',)], 'unordered',
764
 
                                           True):
765
 
            pass
766
 
        self.assertTrue(len(vf._group_cache) > 0)
767
 
        vf.clear_cache()
768
 
        self.assertEqual(0, len(vf._group_cache))
769
 
 
770
 
 
771
 
 
772
 
class StubGCVF(object):
773
 
    def __init__(self, canned_get_blocks=None):
774
 
        self._group_cache = {}
775
 
        self._canned_get_blocks = canned_get_blocks or []
776
 
    def _get_blocks(self, read_memos):
777
 
        return iter(self._canned_get_blocks)
778
 
    
779
 
 
780
 
class Test_BatchingBlockFetcher(TestCaseWithGroupCompressVersionedFiles):
781
 
    """Simple whitebox unit tests for _BatchingBlockFetcher."""
782
 
    
783
 
    def test_add_key_new_read_memo(self):
784
 
        """Adding a key with an uncached read_memo new to this batch adds that
785
 
        read_memo to the list of memos to fetch.
786
 
        """
787
 
        # locations are: index_memo, ignored, parents, ignored
788
 
        # where index_memo is: (idx, offset, len, factory_start, factory_end)
789
 
        # and (idx, offset, size) is known as the 'read_memo', identifying the
790
 
        # raw bytes needed.
791
 
        read_memo = ('fake index', 100, 50)
792
 
        locations = {
793
 
            ('key',): (read_memo + (None, None), None, None, None)}
794
 
        batcher = groupcompress._BatchingBlockFetcher(StubGCVF(), locations)
795
 
        total_size = batcher.add_key(('key',))
796
 
        self.assertEqual(50, total_size)
797
 
        self.assertEqual([('key',)], batcher.keys)
798
 
        self.assertEqual([read_memo], batcher.memos_to_get)
799
 
 
800
 
    def test_add_key_duplicate_read_memo(self):
801
 
        """read_memos that occur multiple times in a batch will only be fetched
802
 
        once.
803
 
        """
804
 
        read_memo = ('fake index', 100, 50)
805
 
        # Two keys, both sharing the same read memo (but different overall
806
 
        # index_memos).
807
 
        locations = {
808
 
            ('key1',): (read_memo + (0, 1), None, None, None),
809
 
            ('key2',): (read_memo + (1, 2), None, None, None)}
810
 
        batcher = groupcompress._BatchingBlockFetcher(StubGCVF(), locations)
811
 
        total_size = batcher.add_key(('key1',))
812
 
        total_size = batcher.add_key(('key2',))
813
 
        self.assertEqual(50, total_size)
814
 
        self.assertEqual([('key1',), ('key2',)], batcher.keys)
815
 
        self.assertEqual([read_memo], batcher.memos_to_get)
816
 
 
817
 
    def test_add_key_cached_read_memo(self):
818
 
        """Adding a key with a cached read_memo will not cause that read_memo
819
 
        to be added to the list to fetch.
820
 
        """
821
 
        read_memo = ('fake index', 100, 50)
822
 
        gcvf = StubGCVF()
823
 
        gcvf._group_cache[read_memo] = 'fake block'
824
 
        locations = {
825
 
            ('key',): (read_memo + (None, None), None, None, None)}
826
 
        batcher = groupcompress._BatchingBlockFetcher(gcvf, locations)
827
 
        total_size = batcher.add_key(('key',))
828
 
        self.assertEqual(0, total_size)
829
 
        self.assertEqual([('key',)], batcher.keys)
830
 
        self.assertEqual([], batcher.memos_to_get)
831
 
 
832
 
    def test_yield_factories_empty(self):
833
 
        """An empty batch yields no factories."""
834
 
        batcher = groupcompress._BatchingBlockFetcher(StubGCVF(), {})
835
 
        self.assertEqual([], list(batcher.yield_factories()))
836
 
 
837
 
    def test_yield_factories_calls_get_blocks(self):
838
 
        """Uncached memos are retrieved via get_blocks."""
839
 
        read_memo1 = ('fake index', 100, 50)
840
 
        read_memo2 = ('fake index', 150, 40)
841
 
        gcvf = StubGCVF(
842
 
            canned_get_blocks=[
843
 
                (read_memo1, groupcompress.GroupCompressBlock()),
844
 
                (read_memo2, groupcompress.GroupCompressBlock())])
845
 
        locations = {
846
 
            ('key1',): (read_memo1 + (None, None), None, None, None),
847
 
            ('key2',): (read_memo2 + (None, None), None, None, None)}
848
 
        batcher = groupcompress._BatchingBlockFetcher(gcvf, locations)
849
 
        batcher.add_key(('key1',))
850
 
        batcher.add_key(('key2',))
851
 
        factories = list(batcher.yield_factories(full_flush=True))
852
 
        self.assertLength(2, factories)
853
 
        keys = [f.key for f in factories]
854
 
        kinds = [f.storage_kind for f in factories]
855
 
        self.assertEqual([('key1',), ('key2',)], keys)
856
 
        self.assertEqual(['groupcompress-block', 'groupcompress-block'], kinds)
857
 
 
858
 
    def test_yield_factories_flushing(self):
859
 
        """yield_factories holds back on yielding results from the final block
860
 
        unless passed full_flush=True.
861
 
        """
862
 
        fake_block = groupcompress.GroupCompressBlock()
863
 
        read_memo = ('fake index', 100, 50)
864
 
        gcvf = StubGCVF()
865
 
        gcvf._group_cache[read_memo] = fake_block
866
 
        locations = {
867
 
            ('key',): (read_memo + (None, None), None, None, None)}
868
 
        batcher = groupcompress._BatchingBlockFetcher(gcvf, locations)
869
 
        batcher.add_key(('key',))
870
 
        self.assertEqual([], list(batcher.yield_factories()))
871
 
        factories = list(batcher.yield_factories(full_flush=True))
872
 
        self.assertLength(1, factories)
873
 
        self.assertEqual(('key',), factories[0].key)
874
 
        self.assertEqual('groupcompress-block', factories[0].storage_kind)
875
 
 
876
652
 
877
653
class TestLazyGroupCompress(tests.TestCaseWithTransport):
878
654
 
879
655
    _texts = {
880
656
        ('key1',): "this is a text\n"
881
 
                   "with a reasonable amount of compressible bytes\n"
882
 
                   "which can be shared between various other texts\n",
 
657
                   "with a reasonable amount of compressible bytes\n",
883
658
        ('key2',): "another text\n"
884
 
                   "with a reasonable amount of compressible bytes\n"
885
 
                   "which can be shared between various other texts\n",
 
659
                   "with a reasonable amount of compressible bytes\n",
886
660
        ('key3',): "yet another text which won't be extracted\n"
887
 
                   "with a reasonable amount of compressible bytes\n"
888
 
                   "which can be shared between various other texts\n",
 
661
                   "with a reasonable amount of compressible bytes\n",
889
662
        ('key4',): "this will be extracted\n"
890
663
                   "but references most of its bytes from\n"
891
664
                   "yet another text which won't be extracted\n"
892
 
                   "with a reasonable amount of compressible bytes\n"
893
 
                   "which can be shared between various other texts\n",
 
665
                   "with a reasonable amount of compressible bytes\n",
894
666
    }
895
667
    def make_block(self, key_to_text):
896
668
        """Create a GroupCompressBlock, filling it with the given texts."""
908
680
        start, end = locations[key]
909
681
        manager.add_factory(key, (), start, end)
910
682
 
911
 
    def make_block_and_full_manager(self, texts):
912
 
        locations, block = self.make_block(texts)
913
 
        manager = groupcompress._LazyGroupContentManager(block)
914
 
        for key in sorted(texts):
915
 
            self.add_key_to_manager(key, locations, block, manager)
916
 
        return block, manager
917
 
 
918
683
    def test_get_fulltexts(self):
919
684
        locations, block = self.make_block(self._texts)
920
685
        manager = groupcompress._LazyGroupContentManager(block)
971
736
        header_len = int(header_len)
972
737
        block_len = int(block_len)
973
738
        self.assertEqual('groupcompress-block', storage_kind)
974
 
        self.assertEqual(34, z_header_len)
975
 
        self.assertEqual(26, header_len)
 
739
        self.assertEqual(33, z_header_len)
 
740
        self.assertEqual(25, header_len)
976
741
        self.assertEqual(len(block_bytes), block_len)
977
742
        z_header = rest[:z_header_len]
978
743
        header = zlib.decompress(z_header)
1012
777
        self.assertEqual([('key1',), ('key4',)], result_order)
1013
778
 
1014
779
    def test__check_rebuild_no_changes(self):
1015
 
        block, manager = self.make_block_and_full_manager(self._texts)
 
780
        locations, block = self.make_block(self._texts)
 
781
        manager = groupcompress._LazyGroupContentManager(block)
 
782
        # Request all the keys, which ensures that we won't rebuild
 
783
        self.add_key_to_manager(('key1',), locations, block, manager)
 
784
        self.add_key_to_manager(('key2',), locations, block, manager)
 
785
        self.add_key_to_manager(('key3',), locations, block, manager)
 
786
        self.add_key_to_manager(('key4',), locations, block, manager)
1016
787
        manager._check_rebuild_block()
1017
788
        self.assertIs(block, manager._block)
1018
789
 
1043
814
            self.assertEqual(('key4',), record.key)
1044
815
            self.assertEqual(self._texts[record.key],
1045
816
                             record.get_bytes_as('fulltext'))
1046
 
 
1047
 
    def test_check_is_well_utilized_all_keys(self):
1048
 
        block, manager = self.make_block_and_full_manager(self._texts)
1049
 
        self.assertFalse(manager.check_is_well_utilized())
1050
 
        # Though we can fake it by changing the recommended minimum size
1051
 
        manager._full_enough_block_size = block._content_length
1052
 
        self.assertTrue(manager.check_is_well_utilized())
1053
 
        # Setting it just above causes it to fail
1054
 
        manager._full_enough_block_size = block._content_length + 1
1055
 
        self.assertFalse(manager.check_is_well_utilized())
1056
 
        # Setting the mixed-block size doesn't do anything, because the content
1057
 
        # is considered to not be 'mixed'
1058
 
        manager._full_enough_mixed_block_size = block._content_length
1059
 
        self.assertFalse(manager.check_is_well_utilized())
1060
 
 
1061
 
    def test_check_is_well_utilized_mixed_keys(self):
1062
 
        texts = {}
1063
 
        f1k1 = ('f1', 'k1')
1064
 
        f1k2 = ('f1', 'k2')
1065
 
        f2k1 = ('f2', 'k1')
1066
 
        f2k2 = ('f2', 'k2')
1067
 
        texts[f1k1] = self._texts[('key1',)]
1068
 
        texts[f1k2] = self._texts[('key2',)]
1069
 
        texts[f2k1] = self._texts[('key3',)]
1070
 
        texts[f2k2] = self._texts[('key4',)]
1071
 
        block, manager = self.make_block_and_full_manager(texts)
1072
 
        self.assertFalse(manager.check_is_well_utilized())
1073
 
        manager._full_enough_block_size = block._content_length
1074
 
        self.assertTrue(manager.check_is_well_utilized())
1075
 
        manager._full_enough_block_size = block._content_length + 1
1076
 
        self.assertFalse(manager.check_is_well_utilized())
1077
 
        manager._full_enough_mixed_block_size = block._content_length
1078
 
        self.assertTrue(manager.check_is_well_utilized())
1079
 
 
1080
 
    def test_check_is_well_utilized_partial_use(self):
1081
 
        locations, block = self.make_block(self._texts)
1082
 
        manager = groupcompress._LazyGroupContentManager(block)
1083
 
        manager._full_enough_block_size = block._content_length
1084
 
        self.add_key_to_manager(('key1',), locations, block, manager)
1085
 
        self.add_key_to_manager(('key2',), locations, block, manager)
1086
 
        # Just using the content from key1 and 2 is not enough to be considered
1087
 
        # 'complete'
1088
 
        self.assertFalse(manager.check_is_well_utilized())
1089
 
        # However if we add key3, then we have enough, as we only require 75%
1090
 
        # consumption
1091
 
        self.add_key_to_manager(('key4',), locations, block, manager)
1092
 
        self.assertTrue(manager.check_is_well_utilized())
1093
 
 
1094
 
 
1095
 
class Test_GCBuildDetails(tests.TestCase):
1096
 
 
1097
 
    def test_acts_like_tuple(self):
1098
 
        # _GCBuildDetails inlines some of the data that used to be spread out
1099
 
        # across a bunch of tuples
1100
 
        bd = groupcompress._GCBuildDetails((('parent1',), ('parent2',)),
1101
 
            ('INDEX', 10, 20, 0, 5))
1102
 
        self.assertEqual(4, len(bd))
1103
 
        self.assertEqual(('INDEX', 10, 20, 0, 5), bd[0])
1104
 
        self.assertEqual(None, bd[1]) # Compression Parent is always None
1105
 
        self.assertEqual((('parent1',), ('parent2',)), bd[2])
1106
 
        self.assertEqual(('group', None), bd[3]) # Record details
1107
 
 
1108
 
    def test__repr__(self):
1109
 
        bd = groupcompress._GCBuildDetails((('parent1',), ('parent2',)),
1110
 
            ('INDEX', 10, 20, 0, 5))
1111
 
        self.assertEqual("_GCBuildDetails(('INDEX', 10, 20, 0, 5),"
1112
 
                         " (('parent1',), ('parent2',)))",
1113
 
                         repr(bd))
1114