767
806
self.doMerge(['aaa', 'bbb', 'ccc'],
768
807
['aaa', 'ddd', 'ccc'],
770
['<<<<', 'aaa', '====', '>>>>', 'ccc'])
776
from unittest import TestSuite, TestLoader
781
suite.addTest(tl.loadTestsFromModule(testweave))
783
return int(not testsweet.run_suite(suite)) # for shell 0=true
786
if __name__ == '__main__':
788
sys.exit(testweave())
809
['<<<<<<<<', 'aaa', '=======', '>>>>>>>', 'ccc'])
812
class JoinWeavesTests(TestBase):
814
super(JoinWeavesTests, self).setUp()
815
self.weave1 = Weave()
816
self.lines1 = ['hello\n']
817
self.lines3 = ['hello\n', 'cruel\n', 'world\n']
818
self.weave1.add('v1', [], self.lines1)
819
self.weave1.add('v2', [0], ['hello\n', 'world\n'])
820
self.weave1.add('v3', [1], self.lines3)
822
def test_join_empty(self):
823
"""Join two empty weaves."""
824
eq = self.assertEqual
828
eq(w1.numversions(), 0)
830
def test_join_empty_to_nonempty(self):
831
"""Join empty weave onto nonempty."""
832
self.weave1.join(Weave())
833
self.assertEqual(len(self.weave1), 3)
835
def test_join_unrelated(self):
836
"""Join two weaves with no history in common."""
838
wb.add('b1', [], ['line from b\n'])
841
eq = self.assertEqual
843
eq(sorted(list(w1.iter_names())),
844
['b1', 'v1', 'v2', 'v3'])
846
def test_join_related(self):
847
wa = self.weave1.copy()
848
wb = self.weave1.copy()
849
wa.add('a1', ['v3'], ['hello\n', 'sweet\n', 'world\n'])
850
wb.add('b1', ['v3'], ['hello\n', 'pale blue\n', 'world\n'])
851
eq = self.assertEquals
856
eq(wa.get_lines('b1'),
857
['hello\n', 'pale blue\n', 'world\n'])
859
def test_join_parent_disagreement(self):
860
"""Cannot join weaves with different parents for a version."""
863
wa.add('v1', [], ['hello\n'])
865
wb.add('v1', ['v0'], ['hello\n'])
866
self.assertRaises(WeaveError,
869
def test_join_text_disagreement(self):
870
"""Cannot join weaves with different texts for a version."""
873
wa.add('v1', [], ['hello\n'])
874
wb.add('v1', [], ['not\n', 'hello\n'])
875
self.assertRaises(WeaveError,
878
def test_join_unordered(self):
879
"""Join weaves where indexes differ.
881
The source weave contains a different version at index 0."""
882
wa = self.weave1.copy()
884
wb.add('x1', [], ['line from x1\n'])
885
wb.add('v1', [], ['hello\n'])
886
wb.add('v2', ['v1'], ['hello\n', 'world\n'])
888
eq = self.assertEquals
889
eq(sorted(wa.iter_names()), ['v1', 'v2', 'v3', 'x1',])
890
eq(wa.get_text('x1'), 'line from x1\n')
893
class Corruption(TestCase):
895
def test_detection(self):
896
# Test weaves detect corruption.
898
# Weaves contain a checksum of their texts.
899
# When a text is extracted, this checksum should be
903
w.add('v1', [], ['hello\n'])
904
w.add('v2', ['v1'], ['hello\n', 'there\n'])
906
# We are going to invasively corrupt the text
907
# Make sure the internals of weave are the same
908
self.assertEqual([('{', 0)
916
self.assertEqual(['f572d396fae9206628714fb2ce00f72e94f2258f'
917
, '90f265c6e75f1c8f9ab76dcf85528352c5f215ef'
922
w._weave[4] = 'There\n'
924
self.assertEqual('hello\n', w.get_text('v1'))
925
self.assertRaises(errors.WeaveInvalidChecksum, w.get_text, 'v2')
926
self.assertRaises(errors.WeaveInvalidChecksum, w.get_lines, 'v2')
927
self.assertRaises(errors.WeaveInvalidChecksum, list, w.get_iter('v2'))
928
self.assertRaises(errors.WeaveInvalidChecksum, w.check)
931
w._weave[4] = 'there\n'
932
self.assertEqual('hello\nthere\n', w.get_text('v2'))
934
#Invalid checksum, first digit changed
935
w._sha1s[1] = 'f0f265c6e75f1c8f9ab76dcf85528352c5f215ef'
937
self.assertEqual('hello\n', w.get_text('v1'))
938
self.assertRaises(errors.WeaveInvalidChecksum, w.get_text, 'v2')
939
self.assertRaises(errors.WeaveInvalidChecksum, w.get_lines, 'v2')
940
self.assertRaises(errors.WeaveInvalidChecksum, list, w.get_iter('v2'))
941
self.assertRaises(errors.WeaveInvalidChecksum, w.check)
943
def test_written_detection(self):
944
# Test detection of weave file corruption.
946
# Make sure that we can detect if a weave file has
947
# been corrupted. This doesn't test all forms of corruption,
948
# but it at least helps verify the data you get, is what you want.
949
from cStringIO import StringIO
952
w.add('v1', [], ['hello\n'])
953
w.add('v2', ['v1'], ['hello\n', 'there\n'])
958
# Because we are corrupting, we need to make sure we have the exact text
959
self.assertEquals('# bzr weave file v5\n'
960
'i\n1 f572d396fae9206628714fb2ce00f72e94f2258f\nn v1\n\n'
961
'i 0\n1 90f265c6e75f1c8f9ab76dcf85528352c5f215ef\nn v2\n\n'
962
'w\n{ 0\n. hello\n}\n{ 1\n. there\n}\nW\n',
965
# Change a single letter
966
tmpf = StringIO('# bzr weave file v5\n'
967
'i\n1 f572d396fae9206628714fb2ce00f72e94f2258f\nn v1\n\n'
968
'i 0\n1 90f265c6e75f1c8f9ab76dcf85528352c5f215ef\nn v2\n\n'
969
'w\n{ 0\n. hello\n}\n{ 1\n. There\n}\nW\n')
973
self.assertEqual('hello\n', w.get_text('v1'))
974
self.assertRaises(errors.WeaveInvalidChecksum, w.get_text, 'v2')
975
self.assertRaises(errors.WeaveInvalidChecksum, w.get_lines, 'v2')
976
self.assertRaises(errors.WeaveInvalidChecksum, list, w.get_iter('v2'))
977
self.assertRaises(errors.WeaveInvalidChecksum, w.check)
979
# Change the sha checksum
980
tmpf = StringIO('# bzr weave file v5\n'
981
'i\n1 f572d396fae9206628714fb2ce00f72e94f2258f\nn v1\n\n'
982
'i 0\n1 f0f265c6e75f1c8f9ab76dcf85528352c5f215ef\nn v2\n\n'
983
'w\n{ 0\n. hello\n}\n{ 1\n. there\n}\nW\n')
987
self.assertEqual('hello\n', w.get_text('v1'))
988
self.assertRaises(errors.WeaveInvalidChecksum, w.get_text, 'v2')
989
self.assertRaises(errors.WeaveInvalidChecksum, w.get_lines, 'v2')
990
self.assertRaises(errors.WeaveInvalidChecksum, list, w.get_iter('v2'))
991
self.assertRaises(errors.WeaveInvalidChecksum, w.check)
994
class InstrumentedWeave(Weave):
995
"""Keep track of how many times functions are called."""
997
def __init__(self, weave_name=None):
998
self._extract_count = 0
999
Weave.__init__(self, weave_name=weave_name)
1001
def _extract(self, versions):
1002
self._extract_count += 1
1003
return Weave._extract(self, versions)
1006
class JoinOptimization(TestCase):
1007
"""Test that Weave.join() doesn't extract all texts, only what must be done."""
1009
def test_join(self):
1010
w1 = InstrumentedWeave()
1011
w2 = InstrumentedWeave()
1014
txt1 = ['a\n', 'b\n']
1015
txt2 = ['a\n', 'c\n']
1016
txt3 = ['a\n', 'b\n', 'c\n']
1018
w1.add('txt0', [], txt0) # extract 1a
1019
w2.add('txt0', [], txt0) # extract 1b
1020
w1.add('txt1', [0], txt1)# extract 2a
1021
w2.add('txt2', [0], txt2)# extract 2b
1022
w1.join(w2) # extract 3a to add txt2
1023
w2.join(w1) # extract 3b to add txt1
1025
w1.add('txt3', [1, 2], txt3) # extract 4a
1026
w2.add('txt3', [1, 2], txt3) # extract 4b
1027
# These secretly have inverted parents
1029
# This should not have to do any extractions
1030
w1.join(w2) # NO extract, texts already present with same parents
1031
w2.join(w1) # NO extract, texts already present with same parents
1033
self.assertEqual(4, w1._extract_count)
1034
self.assertEqual(4, w2._extract_count)
1036
def test_double_parent(self):
1037
# It should not be considered illegal to add
1038
# a revision with the same parent twice
1039
w1 = InstrumentedWeave()
1040
w2 = InstrumentedWeave()
1043
txt1 = ['a\n', 'b\n']
1044
txt2 = ['a\n', 'c\n']
1045
txt3 = ['a\n', 'b\n', 'c\n']
1047
w1.add('txt0', [], txt0)
1048
w2.add('txt0', [], txt0)
1049
w1.add('txt1', [0], txt1)
1050
w2.add('txt1', [0,0], txt1)
1051
# Same text, effectively the same, because the
1052
# parent is only repeated
1053
w1.join(w2) # extract 3a to add txt2
1054
w2.join(w1) # extract 3b to add txt1
1057
class MismatchedTexts(TestCase):
1058
"""Test that merging two weaves with different texts fails."""
1060
def test_reweave(self):
1064
w1.add('txt0', [], ['a\n'])
1065
w2.add('txt0', [], ['a\n'])
1066
w1.add('txt1', [0], ['a\n', 'b\n'])
1067
w2.add('txt1', [0], ['a\n', 'c\n'])
1069
self.assertRaises(errors.WeaveTextDiffers, w1.reweave, w2)