437
433
block._z_content = z_content
438
434
block._z_content_length = len(z_content)
439
435
block._compressor_name = 'zlib'
440
block._content_length = 158634
436
block._content_length = None # Don't tell the decompressed length
441
437
self.assertIs(None, block._content)
442
# The first _ensure_content got all of the required data
443
block._ensure_content(158634)
438
block._ensure_content(100)
439
self.assertIsNot(None, block._content)
440
# We have decompressed at least 100 bytes
441
self.assertTrue(len(block._content) >= 100)
442
# We have not decompressed the whole content
443
self.assertTrue(len(block._content) < 158634)
444
self.assertEqualDiff(content[:len(block._content)], block._content)
445
# ensuring content that we already have shouldn't cause any more data
447
cur_len = len(block._content)
448
block._ensure_content(cur_len - 10)
449
self.assertEqual(cur_len, len(block._content))
450
# Now we want a bit more content
452
block._ensure_content(cur_len)
453
self.assertTrue(len(block._content) >= cur_len)
454
self.assertTrue(len(block._content) < 158634)
455
self.assertEqualDiff(content[:len(block._content)], block._content)
456
# And now lets finish
457
block._ensure_content()
444
458
self.assertEqualDiff(content, block._content)
445
# And we should have released the _z_content_decompressor since it was
459
# And the decompressor is finalized
447
460
self.assertIs(None, block._z_content_decompressor)
449
462
def test__dump(self):
733
745
" \('b',\) \('42 32 0 8', \(\(\),\)\) \('74 32"
734
746
" 0 8', \(\(\('a',\),\),\)\)")
736
def test_clear_cache(self):
737
vf = self.make_source_with_b(True, 'source')
739
for record in vf.get_record_stream([('a',), ('b',)], 'unordered',
742
self.assertTrue(len(vf._group_cache) > 0)
744
self.assertEqual(0, len(vf._group_cache))
748
class StubGCVF(object):
749
def __init__(self, canned_get_blocks=None):
750
self._group_cache = {}
751
self._canned_get_blocks = canned_get_blocks or []
752
def _get_blocks(self, read_memos):
753
return iter(self._canned_get_blocks)
756
class Test_BatchingBlockFetcher(TestCaseWithGroupCompressVersionedFiles):
757
"""Simple whitebox unit tests for _BatchingBlockFetcher."""
759
def test_add_key_new_read_memo(self):
760
"""Adding a key with an uncached read_memo new to this batch adds that
761
read_memo to the list of memos to fetch.
763
# locations are: index_memo, ignored, parents, ignored
764
# where index_memo is: (idx, offset, len, factory_start, factory_end)
765
# and (idx, offset, size) is known as the 'read_memo', identifying the
767
read_memo = ('fake index', 100, 50)
769
('key',): (read_memo + (None, None), None, None, None)}
770
batcher = groupcompress._BatchingBlockFetcher(StubGCVF(), locations)
771
total_size = batcher.add_key(('key',))
772
self.assertEqual(50, total_size)
773
self.assertEqual([('key',)], batcher.keys)
774
self.assertEqual([read_memo], batcher.memos_to_get)
776
def test_add_key_duplicate_read_memo(self):
777
"""read_memos that occur multiple times in a batch will only be fetched
780
read_memo = ('fake index', 100, 50)
781
# Two keys, both sharing the same read memo (but different overall
784
('key1',): (read_memo + (0, 1), None, None, None),
785
('key2',): (read_memo + (1, 2), None, None, None)}
786
batcher = groupcompress._BatchingBlockFetcher(StubGCVF(), locations)
787
total_size = batcher.add_key(('key1',))
788
total_size = batcher.add_key(('key2',))
789
self.assertEqual(50, total_size)
790
self.assertEqual([('key1',), ('key2',)], batcher.keys)
791
self.assertEqual([read_memo], batcher.memos_to_get)
793
def test_add_key_cached_read_memo(self):
794
"""Adding a key with a cached read_memo will not cause that read_memo
795
to be added to the list to fetch.
797
read_memo = ('fake index', 100, 50)
799
gcvf._group_cache[read_memo] = 'fake block'
801
('key',): (read_memo + (None, None), None, None, None)}
802
batcher = groupcompress._BatchingBlockFetcher(gcvf, locations)
803
total_size = batcher.add_key(('key',))
804
self.assertEqual(0, total_size)
805
self.assertEqual([('key',)], batcher.keys)
806
self.assertEqual([], batcher.memos_to_get)
808
def test_yield_factories_empty(self):
809
"""An empty batch yields no factories."""
810
batcher = groupcompress._BatchingBlockFetcher(StubGCVF(), {})
811
self.assertEqual([], list(batcher.yield_factories()))
813
def test_yield_factories_calls_get_blocks(self):
814
"""Uncached memos are retrieved via get_blocks."""
815
read_memo1 = ('fake index', 100, 50)
816
read_memo2 = ('fake index', 150, 40)
819
(read_memo1, groupcompress.GroupCompressBlock()),
820
(read_memo2, groupcompress.GroupCompressBlock())])
822
('key1',): (read_memo1 + (None, None), None, None, None),
823
('key2',): (read_memo2 + (None, None), None, None, None)}
824
batcher = groupcompress._BatchingBlockFetcher(gcvf, locations)
825
batcher.add_key(('key1',))
826
batcher.add_key(('key2',))
827
factories = list(batcher.yield_factories(full_flush=True))
828
self.assertLength(2, factories)
829
keys = [f.key for f in factories]
830
kinds = [f.storage_kind for f in factories]
831
self.assertEqual([('key1',), ('key2',)], keys)
832
self.assertEqual(['groupcompress-block', 'groupcompress-block'], kinds)
834
def test_yield_factories_flushing(self):
835
"""yield_factories holds back on yielding results from the final block
836
unless passed full_flush=True.
838
fake_block = groupcompress.GroupCompressBlock()
839
read_memo = ('fake index', 100, 50)
841
gcvf._group_cache[read_memo] = fake_block
843
('key',): (read_memo + (None, None), None, None, None)}
844
batcher = groupcompress._BatchingBlockFetcher(gcvf, locations)
845
batcher.add_key(('key',))
846
self.assertEqual([], list(batcher.yield_factories()))
847
factories = list(batcher.yield_factories(full_flush=True))
848
self.assertLength(1, factories)
849
self.assertEqual(('key',), factories[0].key)
850
self.assertEqual('groupcompress-block', factories[0].storage_kind)
853
749
class TestLazyGroupCompress(tests.TestCaseWithTransport):
1067
963
self.add_key_to_manager(('key4',), locations, block, manager)
1068
964
self.assertTrue(manager.check_is_well_utilized())
1071
class Test_GCBuildDetails(tests.TestCase):
1073
def test_acts_like_tuple(self):
1074
# _GCBuildDetails inlines some of the data that used to be spread out
1075
# across a bunch of tuples
1076
bd = groupcompress._GCBuildDetails((('parent1',), ('parent2',)),
1077
('INDEX', 10, 20, 0, 5))
1078
self.assertEqual(4, len(bd))
1079
self.assertEqual(('INDEX', 10, 20, 0, 5), bd[0])
1080
self.assertEqual(None, bd[1]) # Compression Parent is always None
1081
self.assertEqual((('parent1',), ('parent2',)), bd[2])
1082
self.assertEqual(('group', None), bd[3]) # Record details
1084
def test__repr__(self):
1085
bd = groupcompress._GCBuildDetails((('parent1',), ('parent2',)),
1086
('INDEX', 10, 20, 0, 5))
1087
self.assertEqual("_GCBuildDetails(('INDEX', 10, 20, 0, 5),"
1088
" (('parent1',), ('parent2',)))",