874
889
eq(sorted(wa.iter_names()), ['v1', 'v2', 'v3', 'x1',])
875
890
eq(wa.get_text('x1'), 'line from x1\n')
877
def test_reweave_with_empty(self):
879
wr = reweave(self.weave1, wb)
880
eq = self.assertEquals
881
eq(sorted(wr.iter_names()), ['v1', 'v2', 'v3'])
882
eq(wr.get_lines('v3'), ['hello\n', 'cruel\n', 'world\n'])
883
self.weave1.reweave(wb)
884
self.assertEquals(wr, self.weave1)
886
def test_join_with_ghosts_raises_parent_mismatch(self):
887
wa = self.weave1.copy()
889
wb.add('x1', [], ['line from x1\n'])
890
wb.add('v1', [], ['hello\n'])
891
wb.add('v2', ['v1', 'x1'], ['hello\n', 'world\n'])
892
self.assertRaises(errors.WeaveParentMismatch, wa.join, wb)
894
def test_reweave_with_ghosts(self):
895
"""Join that inserts parents of an existing revision.
897
This can happen when merging from another branch who
898
knows about revisions the destination does not. In
899
this test the second weave knows of an additional parent of
900
v2. Any revisions which are in common still have to have the
902
wa = self.weave1.copy()
904
wb.add('x1', [], ['line from x1\n'])
905
wb.add('v1', [], ['hello\n'])
906
wb.add('v2', ['v1', 'x1'], ['hello\n', 'world\n'])
908
eq = self.assertEquals
909
eq(sorted(wc.iter_names()), ['v1', 'v2', 'v3', 'x1',])
910
eq(wc.get_text('x1'), 'line from x1\n')
911
eq(wc.get_lines('v2'), ['hello\n', 'world\n'])
912
eq(wc.parent_names('v2'), ['v1', 'x1'])
913
self.weave1.reweave(wb)
914
self.assertEquals(wc, self.weave1)
917
if __name__ == '__main__':
920
sys.exit(unittest.main())
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)