74
78
class StoreText(TestBase):
75
79
"""Store and retrieve a simple text."""
81
def test_storing_text(self):
78
idx = k.add('text0', [], TEXT_0)
79
self.assertEqual(k.get(idx), TEXT_0)
83
idx = k.add_lines('text0', [], TEXT_0)
84
self.assertEqual(k.get_lines(idx), TEXT_0)
80
85
self.assertEqual(idx, 0)
84
88
class AnnotateOne(TestBase):
87
k.add('text0', [], TEXT_0)
88
self.assertEqual(k.annotate(0),
91
k.add_lines('text0', [], TEXT_0)
92
self.assertEqual(k.annotate('text0'),
93
[('text0', TEXT_0[0])])
92
96
class StoreTwo(TestBase):
96
idx = k.add('text0', [], TEXT_0)
100
idx = k.add_lines('text0', [], TEXT_0)
97
101
self.assertEqual(idx, 0)
99
idx = k.add('text1', [], TEXT_1)
103
idx = k.add_lines('text1', [], TEXT_1)
100
104
self.assertEqual(idx, 1)
102
self.assertEqual(k.get(0), TEXT_0)
103
self.assertEqual(k.get(1), TEXT_1)
107
class AddWithGivenSha(TestBase):
109
"""Add with caller-supplied SHA-1"""
106
self.assertEqual(k.get_lines(0), TEXT_0)
107
self.assertEqual(k.get_lines(1), TEXT_1)
110
class GetSha1(TestBase):
111
def test_get_sha1(self):
113
k.add('text0', [], [t], sha1=sha_string(t))
113
k.add_lines('text0', [], 'text0')
114
self.assertEqual('34dc0e430c642a26c3dd1c2beb7a8b4f4445eb79',
116
self.assertRaises(errors.RevisionNotPresent,
118
self.assertRaises(errors.RevisionNotPresent,
117
122
class InvalidAdd(TestBase):
118
123
"""Try to use invalid version number during add."""
119
124
def runTest(self):
122
self.assertRaises(IndexError,
127
self.assertRaises(errors.RevisionNotPresent,
161
165
def runTest(self):
164
k.add('text0', [], ['line 1'])
165
k.add('text1', [0], ['line 1', 'line 2'])
167
self.assertEqual(k.annotate(0),
170
self.assertEqual(k.get(1),
168
k.add_lines('text0', [], ['line 1'])
169
k.add_lines('text1', ['text0'], ['line 1', 'line 2'])
171
self.assertEqual(k.annotate('text0'),
172
[('text0', 'line 1')])
174
self.assertEqual(k.get_lines(1),
174
self.assertEqual(k.annotate(1),
178
k.add('text2', [0], ['line 1', 'diverged line'])
180
self.assertEqual(k.annotate(2),
182
(2, 'diverged line')])
178
self.assertEqual(k.annotate('text1'),
179
[('text0', 'line 1'),
180
('text1', 'line 2')])
182
k.add_lines('text2', ['text0'], ['line 1', 'diverged line'])
184
self.assertEqual(k.annotate('text2'),
185
[('text0', 'line 1'),
186
('text2', 'diverged line')])
184
188
text3 = ['line 1', 'middle line', 'line 2']
189
193
# self.log("changes to text3: " + pformat(list(k._delta(set([0, 1]), text3))))
191
195
self.log("k._weave=" + pformat(k._weave))
193
self.assertEqual(k.annotate(3),
197
self.assertEqual(k.annotate('text3'),
198
[('text0', 'line 1'),
199
('text3', 'middle line'),
200
('text1', 'line 2')])
198
202
# now multiple insertions at different places
204
['text0', 'text1', 'text3'],
201
205
['line 1', 'aaa', 'middle line', 'bbb', 'line 2', 'ccc'])
203
self.assertEqual(k.annotate(4),
207
self.assertEqual(k.annotate('text4'),
208
[('text0', 'line 1'),
210
('text3', 'middle line'),
213
216
class DeleteLines(TestBase):
562
576
['header', '', 'line from 1', 'fixup line', 'line from 2'],
565
k.add('text0', [], texts[0])
566
k.add('text1', [0], texts[1])
567
k.add('text2', [0], texts[2])
568
k.add('merge', [0, 1, 2], texts[3])
579
k.add_lines('text0', [], texts[0])
580
k.add_lines('text1', ['text0'], texts[1])
581
k.add_lines('text2', ['text0'], texts[2])
582
k.add_lines('merge', ['text0', 'text1', 'text2'], texts[3])
570
584
for i, t in enumerate(texts):
571
self.assertEqual(k.get(i), t)
585
self.assertEqual(k.get_lines(i), t)
573
self.assertEqual(k.annotate(3),
587
self.assertEqual(k.annotate('merge'),
588
[('text0', 'header'),
590
('text1', 'line from 1'),
591
('merge', 'fixup line'),
592
('text2', 'line from 2'),
581
self.assertEqual(list(k.inclusions([3])),
595
self.assertEqual(list(k.get_ancestry(['merge'])),
596
['text0', 'text1', 'text2', 'merge'])
584
598
self.log('k._weave=' + pformat(k._weave))
619
k.add([], ['aaa', 'bbb'])
620
k.add([0], ['111', 'aaa', 'ccc', 'bbb'])
621
k.add([1], ['aaa', 'ccc', 'bbb', '222'])
627
class AutoMerge(TestBase):
631
texts = [['header', 'aaa', 'bbb'],
632
['header', 'aaa', 'line from 1', 'bbb'],
633
['header', 'aaa', 'bbb', 'line from 2', 'more from 2'],
636
k.add('text0', [], texts[0])
637
k.add('text1', [0], texts[1])
638
k.add('text2', [0], texts[2])
640
self.log('k._weave=' + pformat(k._weave))
642
m = list(k.mash_iter([0, 1, 2]))
648
'line from 2', 'more from 2'])
632
k.add_lines([], ['aaa', 'bbb'])
633
k.add_lines([0], ['111', 'aaa', 'ccc', 'bbb'])
634
k.add_lines([1], ['aaa', 'ccc', 'bbb', '222'])
652
637
class Khayyam(TestBase):
653
638
"""Test changes to multi-line texts, and read/write"""
640
def test_multi_line_merge(self):
656
642
"""A Book of Verses underneath the Bough,
657
643
A Jug of Wine, a Loaf of Bread, -- and Thou
866
854
The source weave contains a different version at index 0."""
867
855
wa = self.weave1.copy()
869
wb.add('x1', [], ['line from x1\n'])
870
wb.add('v1', [], ['hello\n'])
871
wb.add('v2', ['v1'], ['hello\n', 'world\n'])
857
wb.add_lines('x1', [], ['line from x1\n'])
858
wb.add_lines('v1', [], ['hello\n'])
859
wb.add_lines('v2', ['v1'], ['hello\n', 'world\n'])
873
861
eq = self.assertEquals
874
eq(sorted(wa.iter_names()), ['v1', 'v2', 'v3', 'x1',])
862
eq(sorted(wa.versions()), ['v1', 'v2', 'v3', 'x1',])
875
863
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())
865
def test_written_detection(self):
866
# Test detection of weave file corruption.
868
# Make sure that we can detect if a weave file has
869
# been corrupted. This doesn't test all forms of corruption,
870
# but it at least helps verify the data you get, is what you want.
871
from cStringIO import StringIO
874
w.add_lines('v1', [], ['hello\n'])
875
w.add_lines('v2', ['v1'], ['hello\n', 'there\n'])
880
# Because we are corrupting, we need to make sure we have the exact text
881
self.assertEquals('# bzr weave file v5\n'
882
'i\n1 f572d396fae9206628714fb2ce00f72e94f2258f\nn v1\n\n'
883
'i 0\n1 90f265c6e75f1c8f9ab76dcf85528352c5f215ef\nn v2\n\n'
884
'w\n{ 0\n. hello\n}\n{ 1\n. there\n}\nW\n',
887
# Change a single letter
888
tmpf = StringIO('# bzr weave file v5\n'
889
'i\n1 f572d396fae9206628714fb2ce00f72e94f2258f\nn v1\n\n'
890
'i 0\n1 90f265c6e75f1c8f9ab76dcf85528352c5f215ef\nn v2\n\n'
891
'w\n{ 0\n. hello\n}\n{ 1\n. There\n}\nW\n')
895
self.assertEqual('hello\n', w.get_text('v1'))
896
self.assertRaises(errors.WeaveInvalidChecksum, w.get_text, 'v2')
897
self.assertRaises(errors.WeaveInvalidChecksum, w.get_lines, 'v2')
898
self.assertRaises(errors.WeaveInvalidChecksum, w.check)
900
# Change the sha checksum
901
tmpf = StringIO('# bzr weave file v5\n'
902
'i\n1 f572d396fae9206628714fb2ce00f72e94f2258f\nn v1\n\n'
903
'i 0\n1 f0f265c6e75f1c8f9ab76dcf85528352c5f215ef\nn v2\n\n'
904
'w\n{ 0\n. hello\n}\n{ 1\n. there\n}\nW\n')
908
self.assertEqual('hello\n', w.get_text('v1'))
909
self.assertRaises(errors.WeaveInvalidChecksum, w.get_text, 'v2')
910
self.assertRaises(errors.WeaveInvalidChecksum, w.get_lines, 'v2')
911
self.assertRaises(errors.WeaveInvalidChecksum, w.check)
914
class InstrumentedWeave(Weave):
915
"""Keep track of how many times functions are called."""
917
def __init__(self, weave_name=None):
918
self._extract_count = 0
919
Weave.__init__(self, weave_name=weave_name)
921
def _extract(self, versions):
922
self._extract_count += 1
923
return Weave._extract(self, versions)
926
class JoinOptimization(TestCase):
927
"""Test that Weave.join() doesn't extract all texts, only what must be done."""
930
w1 = InstrumentedWeave()
931
w2 = InstrumentedWeave()
934
txt1 = ['a\n', 'b\n']
935
txt2 = ['a\n', 'c\n']
936
txt3 = ['a\n', 'b\n', 'c\n']
938
w1.add_lines('txt0', [], txt0) # extract 1a
939
w2.add_lines('txt0', [], txt0) # extract 1b
940
w1.add_lines('txt1', ['txt0'], txt1)# extract 2a
941
w2.add_lines('txt2', ['txt0'], txt2)# extract 2b
942
w1.join(w2) # extract 3a to add txt2
943
w2.join(w1) # extract 3b to add txt1
945
w1.add_lines('txt3', ['txt1', 'txt2'], txt3) # extract 4a
946
w2.add_lines('txt3', ['txt2', 'txt1'], txt3) # extract 4b
947
# These secretly have inverted parents
949
# This should not have to do any extractions
950
w1.join(w2) # NO extract, texts already present with same parents
951
w2.join(w1) # NO extract, texts already present with same parents
953
self.assertEqual(4, w1._extract_count)
954
self.assertEqual(4, w2._extract_count)
956
def test_double_parent(self):
957
# It should not be considered illegal to add
958
# a revision with the same parent twice
959
w1 = InstrumentedWeave()
960
w2 = InstrumentedWeave()
963
txt1 = ['a\n', 'b\n']
964
txt2 = ['a\n', 'c\n']
965
txt3 = ['a\n', 'b\n', 'c\n']
967
w1.add_lines('txt0', [], txt0)
968
w2.add_lines('txt0', [], txt0)
969
w1.add_lines('txt1', ['txt0'], txt1)
970
w2.add_lines('txt1', ['txt0', 'txt0'], txt1)
971
# Same text, effectively the same, because the
972
# parent is only repeated
973
w1.join(w2) # extract 3a to add txt2
974
w2.join(w1) # extract 3b to add txt1
977
class TestNeedsRweave(TestCase):
978
"""Internal corner cases for when reweave is needed."""
980
def test_compatible_parents(self):
982
my_parents = set([1, 2, 3])
984
self.assertTrue(w1._compatible_parents(my_parents, set([3])))
986
self.assertTrue(w1._compatible_parents(my_parents, set(my_parents)))
987
# same empty corner case
988
self.assertTrue(w1._compatible_parents(set(), set()))
989
# other cannot contain stuff my_parents does not
990
self.assertFalse(w1._compatible_parents(set(), set([1])))
991
self.assertFalse(w1._compatible_parents(my_parents, set([1, 2, 3, 4])))
992
self.assertFalse(w1._compatible_parents(my_parents, set([4])))