83
78
class StoreText(TestBase):
84
79
"""Store and retrieve a simple text."""
81
def test_storing_text(self):
87
idx = k.add('text0', [], TEXT_0)
88
self.assertEqual(k.get(idx), TEXT_0)
83
idx = k.add_lines('text0', [], TEXT_0)
84
self.assertEqual(k.get_lines(idx), TEXT_0)
89
85
self.assertEqual(idx, 0)
93
88
class AnnotateOne(TestBase):
96
k.add('text0', [], TEXT_0)
97
self.assertEqual(k.annotate(0),
91
k.add_lines('text0', [], TEXT_0)
92
self.assertEqual(k.annotate('text0'),
93
[('text0', TEXT_0[0])])
101
96
class StoreTwo(TestBase):
102
97
def runTest(self):
105
idx = k.add('text0', [], TEXT_0)
100
idx = k.add_lines('text0', [], TEXT_0)
106
101
self.assertEqual(idx, 0)
108
idx = k.add('text1', [], TEXT_1)
103
idx = k.add_lines('text1', [], TEXT_1)
109
104
self.assertEqual(idx, 1)
111
self.assertEqual(k.get(0), TEXT_0)
112
self.assertEqual(k.get(1), TEXT_1)
114
k.dump(self.TEST_LOG)
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_lines('text0', [], 'text0')
114
self.assertEqual('34dc0e430c642a26c3dd1c2beb7a8b4f4445eb79',
116
self.assertRaises(errors.RevisionNotPresent,
118
self.assertRaises(errors.RevisionNotPresent,
118
122
class InvalidAdd(TestBase):
119
123
"""Try to use invalid version number during add."""
120
124
def runTest(self):
123
self.assertRaises(IndexError,
127
self.assertRaises(errors.RevisionNotPresent,
134
class RepeatedAdd(TestBase):
135
"""Add the same version twice; harmless."""
138
idx = k.add_lines('text0', [], TEXT_0)
139
idx2 = k.add_lines('text0', [], TEXT_0)
140
self.assertEqual(idx, idx2)
143
class InvalidRepeatedAdd(TestBase):
146
k.add_lines('basis', [], TEXT_0)
147
idx = k.add_lines('text0', [], TEXT_0)
148
self.assertRaises(errors.RevisionAlreadyPresent,
152
['not the same text'])
153
self.assertRaises(errors.RevisionAlreadyPresent,
156
['basis'], # not the right parents
130
160
class InsertLines(TestBase):
131
161
"""Store a revision that adds one line to the original.
135
165
def runTest(self):
138
k.add('text0', [], ['line 1'])
139
k.add('text1', [0], ['line 1', 'line 2'])
141
self.assertEqual(k.annotate(0),
144
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),
148
self.assertEqual(k.annotate(1),
152
k.add('text2', [0], ['line 1', 'diverged line'])
154
self.assertEqual(k.annotate(2),
156
(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')])
158
188
text3 = ['line 1', 'middle line', 'line 2']
163
193
# self.log("changes to text3: " + pformat(list(k._delta(set([0, 1]), text3))))
165
195
self.log("k._weave=" + pformat(k._weave))
167
self.assertEqual(k.annotate(3),
197
self.assertEqual(k.annotate('text3'),
198
[('text0', 'line 1'),
199
('text3', 'middle line'),
200
('text1', 'line 2')])
172
202
# now multiple insertions at different places
204
['text0', 'text1', 'text3'],
175
205
['line 1', 'aaa', 'middle line', 'bbb', 'line 2', 'ccc'])
177
self.assertEqual(k.annotate(4),
207
self.assertEqual(k.annotate('text4'),
208
[('text0', 'line 1'),
210
('text3', 'middle line'),
187
216
class DeleteLines(TestBase):
538
576
['header', '', 'line from 1', 'fixup line', 'line from 2'],
541
k.add('text0', [], texts[0])
542
k.add('text1', [0], texts[1])
543
k.add('text2', [0], texts[2])
544
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])
546
584
for i, t in enumerate(texts):
547
self.assertEqual(k.get(i), t)
585
self.assertEqual(k.get_lines(i), t)
549
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'),
557
self.assertEqual(list(k.inclusions([3])),
595
self.assertEqual(list(k.get_ancestry(['merge'])),
596
['text0', 'text1', 'text2', 'merge'])
560
598
self.log('k._weave=' + pformat(k._weave))
595
k.add([], ['aaa', 'bbb'])
596
k.add([0], ['111', 'aaa', 'ccc', 'bbb'])
597
k.add([1], ['aaa', 'ccc', 'bbb', '222'])
603
class AutoMerge(TestBase):
607
texts = [['header', 'aaa', 'bbb'],
608
['header', 'aaa', 'line from 1', 'bbb'],
609
['header', 'aaa', 'bbb', 'line from 2', 'more from 2'],
612
k.add('text0', [], texts[0])
613
k.add('text1', [0], texts[1])
614
k.add('text2', [0], texts[2])
616
self.log('k._weave=' + pformat(k._weave))
618
m = list(k.mash_iter([0, 1, 2]))
624
'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'])
628
637
class Khayyam(TestBase):
629
638
"""Test changes to multi-line texts, and read/write"""
640
def test_multi_line_merge(self):
632
642
"""A Book of Verses underneath the Bough,
633
643
A Jug of Wine, a Loaf of Bread, -- and Thou
767
779
self.doMerge(['aaa', 'bbb', 'ccc'],
768
780
['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())
782
['<<<<<<<< ', 'aaa', '=======', '>>>>>>> ', 'ccc'])
785
class JoinWeavesTests(TestBase):
787
super(JoinWeavesTests, self).setUp()
788
self.weave1 = Weave()
789
self.lines1 = ['hello\n']
790
self.lines3 = ['hello\n', 'cruel\n', 'world\n']
791
self.weave1.add_lines('v1', [], self.lines1)
792
self.weave1.add_lines('v2', ['v1'], ['hello\n', 'world\n'])
793
self.weave1.add_lines('v3', ['v2'], self.lines3)
795
def test_join_empty(self):
796
"""Join two empty weaves."""
797
eq = self.assertEqual
803
def test_join_empty_to_nonempty(self):
804
"""Join empty weave onto nonempty."""
805
self.weave1.join(Weave())
806
self.assertEqual(len(self.weave1), 3)
808
def test_join_unrelated(self):
809
"""Join two weaves with no history in common."""
811
wb.add_lines('b1', [], ['line from b\n'])
814
eq = self.assertEqual
816
eq(sorted(w1.versions()),
817
['b1', 'v1', 'v2', 'v3'])
819
def test_join_related(self):
820
wa = self.weave1.copy()
821
wb = self.weave1.copy()
822
wa.add_lines('a1', ['v3'], ['hello\n', 'sweet\n', 'world\n'])
823
wb.add_lines('b1', ['v3'], ['hello\n', 'pale blue\n', 'world\n'])
824
eq = self.assertEquals
829
eq(wa.get_lines('b1'),
830
['hello\n', 'pale blue\n', 'world\n'])
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])))