476
489
class TestGroupCompressVersionedFiles(TestCaseWithGroupCompressVersionedFiles):
491
def make_g_index(self, name, ref_lists=0, nodes=[]):
492
builder = btree_index.BTreeBuilder(ref_lists)
493
for node, references, value in nodes:
494
builder.add_node(node, references, value)
495
stream = builder.finish()
496
trans = self.get_transport()
497
size = trans.put_file(name, stream)
498
return btree_index.BTreeGraphIndex(trans, name, size)
500
def make_g_index_missing_parent(self):
501
graph_index = self.make_g_index('missing_parent', 1,
502
[(('parent', ), '2 78 2 10', ([],)),
503
(('tip', ), '2 78 2 10',
504
([('parent', ), ('missing-parent', )],)),
478
508
def test_get_record_stream_as_requested(self):
479
509
# Consider promoting 'as-requested' to general availability, and
480
510
# make this a VF interface test
564
600
record._manager._block._z_content)
565
601
self.assertEqual(8, num_records)
603
def test_insert_record_stream_packs_on_the_fly(self):
604
vf = self.make_test_vf(True, dir='source')
605
def grouped_stream(revision_ids, first_parents=()):
606
parents = first_parents
607
for revision_id in revision_ids:
609
record = versionedfile.FulltextContentFactory(
611
'some content that is\n'
612
'identical except for\n'
613
'revision_id:%s\n' % (revision_id,))
617
vf.insert_record_stream(grouped_stream(['a', 'b', 'c', 'd']))
619
vf.insert_record_stream(grouped_stream(['e', 'f', 'g', 'h'],
620
first_parents=(('d',),)))
621
# Now copy the blocks into another vf, and see that the
622
# insert_record_stream rebuilt a new block on-the-fly because of
624
vf2 = self.make_test_vf(True, dir='target')
625
vf2.insert_record_stream(vf.get_record_stream(
626
[(r,) for r in 'abcdefgh'], 'groupcompress', False))
627
stream = vf2.get_record_stream([(r,) for r in 'abcdefgh'],
628
'groupcompress', False)
631
# All of the records should be recombined into a single block
633
for record in stream:
636
block = record._manager._block
638
self.assertIs(block, record._manager._block)
639
self.assertEqual(8, num_records)
567
641
def test__insert_record_stream_no_reuse_block(self):
568
642
vf = self.make_test_vf(True, dir='source')
569
643
def grouped_stream(revision_ids, first_parents=()):
607
681
self.assertIs(block, record._manager._block)
683
def test_add_missing_noncompression_parent_unvalidated_index(self):
684
unvalidated = self.make_g_index_missing_parent()
685
combined = _mod_index.CombinedGraphIndex([unvalidated])
686
index = groupcompress._GCGraphIndex(combined,
687
is_locked=lambda: True, parents=True,
688
track_external_parent_refs=True)
689
index.scan_unvalidated_index(unvalidated)
691
frozenset([('missing-parent',)]), index.get_missing_parents())
693
def test_track_external_parent_refs(self):
694
g_index = self.make_g_index('empty', 1, [])
695
mod_index = btree_index.BTreeBuilder(1, 1)
696
combined = _mod_index.CombinedGraphIndex([g_index, mod_index])
697
index = groupcompress._GCGraphIndex(combined,
698
is_locked=lambda: True, parents=True,
699
add_callback=mod_index.add_nodes,
700
track_external_parent_refs=True)
702
(('new-key',), '2 10 2 10', [(('parent-1',), ('parent-2',))])])
704
frozenset([('parent-1',), ('parent-2',)]),
705
index.get_missing_parents())
707
def make_source_with_b(self, a_parent, path):
708
source = self.make_test_vf(True, dir=path)
709
source.add_lines(('a',), (), ['lines\n'])
711
b_parents = (('a',),)
714
source.add_lines(('b',), b_parents, ['lines\n'])
717
def do_inconsistent_inserts(self, inconsistency_fatal):
718
target = self.make_test_vf(True, dir='target',
719
inconsistency_fatal=inconsistency_fatal)
721
source = self.make_source_with_b(x==1, 'source%s' % x)
722
target.insert_record_stream(source.get_record_stream(
723
[('b',)], 'unordered', False))
725
def test_inconsistent_redundant_inserts_warn(self):
726
"""Should not insert a record that is already present."""
728
def warning(template, args):
729
warnings.append(template % args)
730
_trace_warning = trace.warning
731
trace.warning = warning
733
self.do_inconsistent_inserts(inconsistency_fatal=False)
735
trace.warning = _trace_warning
736
self.assertEqual(["inconsistent details in skipped record: ('b',)"
737
" ('42 32 0 8', ((),)) ('74 32 0 8', ((('a',),),))"],
740
def test_inconsistent_redundant_inserts_raises(self):
741
e = self.assertRaises(errors.KnitCorrupt, self.do_inconsistent_inserts,
742
inconsistency_fatal=True)
743
self.assertContainsRe(str(e), "Knit.* corrupt: inconsistent details"
745
" \('b',\) \('42 32 0 8', \(\(\),\)\) \('74 32"
746
" 0 8', \(\(\('a',\),\),\)\)")
610
749
class TestLazyGroupCompress(tests.TestCaseWithTransport):
613
752
('key1',): "this is a text\n"
614
"with a reasonable amount of compressible bytes\n",
753
"with a reasonable amount of compressible bytes\n"
754
"which can be shared between various other texts\n",
615
755
('key2',): "another text\n"
616
"with a reasonable amount of compressible bytes\n",
756
"with a reasonable amount of compressible bytes\n"
757
"which can be shared between various other texts\n",
617
758
('key3',): "yet another text which won't be extracted\n"
618
"with a reasonable amount of compressible bytes\n",
759
"with a reasonable amount of compressible bytes\n"
760
"which can be shared between various other texts\n",
619
761
('key4',): "this will be extracted\n"
620
762
"but references most of its bytes from\n"
621
763
"yet another text which won't be extracted\n"
622
"with a reasonable amount of compressible bytes\n",
764
"with a reasonable amount of compressible bytes\n"
765
"which can be shared between various other texts\n",
624
767
def make_block(self, key_to_text):
625
768
"""Create a GroupCompressBlock, filling it with the given texts."""
734
884
self.assertEqual([('key1',), ('key4',)], result_order)
736
886
def test__check_rebuild_no_changes(self):
737
locations, block = self.make_block(self._texts)
738
manager = groupcompress._LazyGroupContentManager(block)
739
# Request all the keys, which ensures that we won't rebuild
740
self.add_key_to_manager(('key1',), locations, block, manager)
741
self.add_key_to_manager(('key2',), locations, block, manager)
742
self.add_key_to_manager(('key3',), locations, block, manager)
743
self.add_key_to_manager(('key4',), locations, block, manager)
887
block, manager = self.make_block_and_full_manager(self._texts)
744
888
manager._check_rebuild_block()
745
889
self.assertIs(block, manager._block)
771
915
self.assertEqual(('key4',), record.key)
772
916
self.assertEqual(self._texts[record.key],
773
917
record.get_bytes_as('fulltext'))
919
def test_check_is_well_utilized_all_keys(self):
920
block, manager = self.make_block_and_full_manager(self._texts)
921
self.assertFalse(manager.check_is_well_utilized())
922
# Though we can fake it by changing the recommended minimum size
923
manager._full_enough_block_size = block._content_length
924
self.assertTrue(manager.check_is_well_utilized())
925
# Setting it just above causes it to fail
926
manager._full_enough_block_size = block._content_length + 1
927
self.assertFalse(manager.check_is_well_utilized())
928
# Setting the mixed-block size doesn't do anything, because the content
929
# is considered to not be 'mixed'
930
manager._full_enough_mixed_block_size = block._content_length
931
self.assertFalse(manager.check_is_well_utilized())
933
def test_check_is_well_utilized_mixed_keys(self):
939
texts[f1k1] = self._texts[('key1',)]
940
texts[f1k2] = self._texts[('key2',)]
941
texts[f2k1] = self._texts[('key3',)]
942
texts[f2k2] = self._texts[('key4',)]
943
block, manager = self.make_block_and_full_manager(texts)
944
self.assertFalse(manager.check_is_well_utilized())
945
manager._full_enough_block_size = block._content_length
946
self.assertTrue(manager.check_is_well_utilized())
947
manager._full_enough_block_size = block._content_length + 1
948
self.assertFalse(manager.check_is_well_utilized())
949
manager._full_enough_mixed_block_size = block._content_length
950
self.assertTrue(manager.check_is_well_utilized())
952
def test_check_is_well_utilized_partial_use(self):
953
locations, block = self.make_block(self._texts)
954
manager = groupcompress._LazyGroupContentManager(block)
955
manager._full_enough_block_size = block._content_length
956
self.add_key_to_manager(('key1',), locations, block, manager)
957
self.add_key_to_manager(('key2',), locations, block, manager)
958
# Just using the content from key1 and 2 is not enough to be considered
960
self.assertFalse(manager.check_is_well_utilized())
961
# However if we add key3, then we have enough, as we only require 75%
963
self.add_key_to_manager(('key4',), locations, block, manager)
964
self.assertTrue(manager.check_is_well_utilized())