73
78
class StoreText(TestBase):
74
79
"""Store and retrieve a simple text."""
81
def test_storing_text(self):
77
idx = k.add('text0', [], TEXT_0)
78
self.assertEqual(k.get(idx), TEXT_0)
83
idx = k.add_lines('text0', [], TEXT_0)
84
self.assertEqual(k.get_lines(idx), TEXT_0)
79
85
self.assertEqual(idx, 0)
83
88
class AnnotateOne(TestBase):
86
k.add('text0', [], TEXT_0)
87
self.assertEqual(k.annotate(0),
91
k.add_lines('text0', [], TEXT_0)
92
self.assertEqual(k.annotate('text0'),
93
[('text0', TEXT_0[0])])
91
96
class StoreTwo(TestBase):
95
idx = k.add('text0', [], TEXT_0)
100
idx = k.add_lines('text0', [], TEXT_0)
96
101
self.assertEqual(idx, 0)
98
idx = k.add('text1', [], TEXT_1)
103
idx = k.add_lines('text1', [], TEXT_1)
99
104
self.assertEqual(idx, 1)
101
self.assertEqual(k.get(0), TEXT_0)
102
self.assertEqual(k.get(1), TEXT_1)
106
class AddWithGivenSha(TestBase):
108
"""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):
112
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,
116
122
class InvalidAdd(TestBase):
117
123
"""Try to use invalid version number during add."""
118
124
def runTest(self):
121
self.assertRaises(IndexError,
127
self.assertRaises(errors.RevisionNotPresent,
129
135
"""Add the same version twice; harmless."""
130
136
def runTest(self):
132
idx = k.add('text0', [], TEXT_0)
133
idx2 = k.add('text0', [], TEXT_0)
138
idx = k.add_lines('text0', [], TEXT_0)
139
idx2 = k.add_lines('text0', [], TEXT_0)
134
140
self.assertEqual(idx, idx2)
138
143
class InvalidRepeatedAdd(TestBase):
139
144
def runTest(self):
141
idx = k.add('text0', [], TEXT_0)
142
self.assertRaises(WeaveError,
146
k.add_lines('basis', [], TEXT_0)
147
idx = k.add_lines('text0', [], TEXT_0)
148
self.assertRaises(errors.RevisionAlreadyPresent,
146
152
['not the same text'])
147
self.assertRaises(WeaveError,
153
self.assertRaises(errors.RevisionAlreadyPresent,
150
[12], # not the right parents
156
['basis'], # not the right parents
155
160
class InsertLines(TestBase):
156
161
"""Store a revision that adds one line to the original.
160
165
def runTest(self):
163
k.add('text0', [], ['line 1'])
164
k.add('text1', [0], ['line 1', 'line 2'])
166
self.assertEqual(k.annotate(0),
169
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),
173
self.assertEqual(k.annotate(1),
177
k.add('text2', [0], ['line 1', 'diverged line'])
179
self.assertEqual(k.annotate(2),
181
(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')])
183
188
text3 = ['line 1', 'middle line', 'line 2']
188
193
# self.log("changes to text3: " + pformat(list(k._delta(set([0, 1]), text3))))
190
195
self.log("k._weave=" + pformat(k._weave))
192
self.assertEqual(k.annotate(3),
197
self.assertEqual(k.annotate('text3'),
198
[('text0', 'line 1'),
199
('text3', 'middle line'),
200
('text1', 'line 2')])
197
202
# now multiple insertions at different places
204
['text0', 'text1', 'text3'],
200
205
['line 1', 'aaa', 'middle line', 'bbb', 'line 2', 'ccc'])
202
self.assertEqual(k.annotate(4),
207
self.assertEqual(k.annotate('text4'),
208
[('text0', 'line 1'),
210
('text3', 'middle line'),
212
216
class DeleteLines(TestBase):
561
576
['header', '', 'line from 1', 'fixup line', 'line from 2'],
564
k.add('text0', [], texts[0])
565
k.add('text1', [0], texts[1])
566
k.add('text2', [0], texts[2])
567
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])
569
584
for i, t in enumerate(texts):
570
self.assertEqual(k.get(i), t)
585
self.assertEqual(k.get_lines(i), t)
572
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'),
580
self.assertEqual(list(k.inclusions([3])),
595
self.assertEqual(list(k.get_ancestry(['merge'])),
596
['text0', 'text1', 'text2', 'merge'])
583
598
self.log('k._weave=' + pformat(k._weave))
618
k.add([], ['aaa', 'bbb'])
619
k.add([0], ['111', 'aaa', 'ccc', 'bbb'])
620
k.add([1], ['aaa', 'ccc', 'bbb', '222'])
626
class AutoMerge(TestBase):
630
texts = [['header', 'aaa', 'bbb'],
631
['header', 'aaa', 'line from 1', 'bbb'],
632
['header', 'aaa', 'bbb', 'line from 2', 'more from 2'],
635
k.add('text0', [], texts[0])
636
k.add('text1', [0], texts[1])
637
k.add('text2', [0], texts[2])
639
self.log('k._weave=' + pformat(k._weave))
641
m = list(k.mash_iter([0, 1, 2]))
647
'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'])
651
637
class Khayyam(TestBase):
652
638
"""Test changes to multi-line texts, and read/write"""
640
def test_multi_line_merge(self):
655
642
"""A Book of Verses underneath the Bough,
656
643
A Jug of Wine, a Loaf of Bread, -- and Thou
840
829
eq(wa.get_lines('b1'),
841
830
['hello\n', 'pale blue\n', 'world\n'])
844
if __name__ == '__main__':
847
sys.exit(unittest.main())
832
def test_join_parent_disagreement(self):
833
#join reconciles differening parents into a union.
836
wa.add_lines('v1', [], ['hello\n'])
837
wb.add_lines('v0', [], [])
838
wb.add_lines('v1', ['v0'], ['hello\n'])
840
self.assertEqual(['v0'], wa.get_parents('v1'))
842
def test_join_text_disagreement(self):
843
"""Cannot join weaves with different texts for a version."""
846
wa.add_lines('v1', [], ['hello\n'])
847
wb.add_lines('v1', [], ['not\n', 'hello\n'])
848
self.assertRaises(WeaveError,
851
def test_join_unordered(self):
852
"""Join weaves where indexes differ.
854
The source weave contains a different version at index 0."""
855
wa = self.weave1.copy()
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'])
861
eq = self.assertEquals
862
eq(sorted(wa.versions()), ['v1', 'v2', 'v3', 'x1',])
863
eq(wa.get_text('x1'), 'line from x1\n')
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])))