48
83
class AnnotateOne(TestBase):
86
k.add('text0', [], TEXT_0)
52
87
self.assertEqual(k.annotate(0),
56
91
class StoreTwo(TestBase):
60
idx = k.add([], TEXT_0)
95
idx = k.add('text0', [], TEXT_0)
61
96
self.assertEqual(idx, 0)
63
idx = k.add([], TEXT_1)
98
idx = k.add('text1', [], TEXT_1)
64
99
self.assertEqual(idx, 1)
66
101
self.assertEqual(k.get(0), TEXT_0)
67
102
self.assertEqual(k.get(1), TEXT_1)
73
class Delta1(TestBase):
74
"""Detection of changes prior to inserting new revision."""
106
class AddWithGivenSha(TestBase):
75
107
def runTest(self):
76
from pprint import pformat
81
changes = list(k._delta(set([0]),
85
self.log('raw changes: ' + pformat(changes))
87
# should be one inserted line after line 0q
88
self.assertEquals(changes,
89
[(1, 1, ['new line'])])
91
changes = k._delta(set([0]),
95
self.assertEquals(list(changes),
96
[(0, 0, ['top line'])])
108
"""Add with caller-supplied SHA-1"""
112
k.add('text0', [], [t], sha1=sha_string(t))
100
116
class InvalidAdd(TestBase):
101
117
"""Try to use invalid version number during add."""
102
118
def runTest(self):
105
121
self.assertRaises(IndexError,
128
class RepeatedAdd(TestBase):
129
"""Add the same version twice; harmless."""
132
idx = k.add('text0', [], TEXT_0)
133
idx2 = k.add('text0', [], TEXT_0)
134
self.assertEqual(idx, idx2)
138
class InvalidRepeatedAdd(TestBase):
141
idx = k.add('text0', [], TEXT_0)
142
self.assertRaises(WeaveError,
146
['not the same text'])
147
self.assertRaises(WeaveError,
150
[12], # not the right parents
111
155
class InsertLines(TestBase):
112
156
"""Store a revision that adds one line to the original.
114
158
Look at the annotations to make sure that the first line is matched
115
159
and not stored repeatedly."""
116
160
def runTest(self):
119
k.add([], ['line 1'])
120
k.add([0], ['line 1', 'line 2'])
163
k.add('text0', [], ['line 1'])
164
k.add('text1', [0], ['line 1', 'line 2'])
122
166
self.assertEqual(k.annotate(0),
133
k.add([0], ['line 1', 'diverged line'])
177
k.add('text2', [0], ['line 1', 'diverged line'])
135
179
self.assertEqual(k.annotate(2),
137
181
(2, 'diverged line')])
140
['line 1', 'middle line', 'line 2'])
183
text3 = ['line 1', 'middle line', 'line 2']
188
# self.log("changes to text3: " + pformat(list(k._delta(set([0, 1]), text3))))
190
self.log("k._weave=" + pformat(k._weave))
142
192
self.assertEqual(k.annotate(3),
144
194
(3, 'middle line'),
197
# now multiple insertions at different places
200
['line 1', 'aaa', 'middle line', 'bbb', 'line 2', 'ccc'])
202
self.assertEqual(k.annotate(4),
212
class DeleteLines(TestBase):
213
"""Deletion of lines from existing text.
215
Try various texts all based on a common ancestor."""
219
base_text = ['one', 'two', 'three', 'four']
221
k.add('text0', [], base_text)
223
texts = [['one', 'two', 'three'],
224
['two', 'three', 'four'],
226
['one', 'two', 'three', 'four'],
231
ver = k.add('text%d' % i,
235
self.log('final weave:')
236
self.log('k._weave=' + pformat(k._weave))
238
for i in range(len(texts)):
239
self.assertEqual(k.get(i+1),
245
class SuicideDelete(TestBase):
246
"""Invalid weave which tries to add and delete simultaneously."""
252
k._weave = [('{', 0),
259
################################### SKIPPED
260
# Weave.get doesn't trap this anymore
263
self.assertRaises(WeaveFormatError,
269
class CannedDelete(TestBase):
270
"""Unpack canned weave with deleted lines."""
277
k._weave = [('{', 0),
280
'line to be deleted',
286
self.assertEqual(k.get(0),
288
'line to be deleted',
292
self.assertEqual(k.get(1),
299
class CannedReplacement(TestBase):
300
"""Unpack canned weave with deleted lines."""
304
k._parents = [frozenset(),
307
k._weave = [('{', 0),
310
'line to be deleted',
319
self.assertEqual(k.get(0),
321
'line to be deleted',
325
self.assertEqual(k.get(1),
333
class BadWeave(TestBase):
334
"""Test that we trap an insert which should not occur."""
338
k._parents = [frozenset(),
340
k._weave = ['bad line',
344
' added in version 1',
353
################################### SKIPPED
354
# Weave.get doesn't trap this anymore
358
self.assertRaises(WeaveFormatError,
363
class BadInsert(TestBase):
364
"""Test that we trap an insert which should not occur."""
368
k._parents = [frozenset(),
373
k._weave = [('{', 0),
376
' added in version 1',
384
# this is not currently enforced by get
385
return ##########################################
387
self.assertRaises(WeaveFormatError,
391
self.assertRaises(WeaveFormatError,
396
class InsertNested(TestBase):
397
"""Insertion with nested instructions."""
401
k._parents = [frozenset(),
406
k._weave = [('{', 0),
409
' added in version 1',
418
self.assertEqual(k.get(0),
422
self.assertEqual(k.get(1),
424
' added in version 1',
428
self.assertEqual(k.get(2),
433
self.assertEqual(k.get(3),
435
' added in version 1',
442
class DeleteLines2(TestBase):
443
"""Test recording revisions that delete lines.
445
This relies on the weave having a way to represent lines knocked
446
out by a later revision."""
450
k.add('text0', [], ["line the first",
455
self.assertEqual(len(k.get(0)), 4)
457
k.add('text1', [0], ["line the first",
460
self.assertEqual(k.get(1),
464
self.assertEqual(k.annotate(1),
465
[(0, "line the first"),
149
470
class IncludeVersions(TestBase):
150
471
"""Check texts that are stored across multiple revisions.
152
Here we manually create a knit with particular encoding and make
473
Here we manually create a weave with particular encoding and make
153
474
sure it unpacks properly.
155
476
Text 0 includes nothing; text 1 includes text 0 and adds some
199
529
"alternative second line"])
203
from unittest import TestSuite, TestLoader
208
suite.addTest(tl.loadTestsFromModule(testknit))
210
return int(not testsweet.run_suite(suite)) # for shell 0=true
531
self.assertEqual(list(k.inclusions([2])),
536
class ReplaceLine(TestBase):
540
text0 = ['cheddar', 'stilton', 'gruyere']
541
text1 = ['cheddar', 'blue vein', 'neufchatel', 'chevre']
543
k.add('text0', [], text0)
544
k.add('text1', [0], text1)
546
self.log('k._weave=' + pformat(k._weave))
548
self.assertEqual(k.get(0), text0)
549
self.assertEqual(k.get(1), text1)
553
class Merge(TestBase):
554
"""Storage of versions that merge diverged parents"""
559
['header', '', 'line from 1'],
560
['header', '', 'line from 2', 'more from 2'],
561
['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])
569
for i, t in enumerate(texts):
570
self.assertEqual(k.get(i), t)
572
self.assertEqual(k.annotate(3),
580
self.assertEqual(list(k.inclusions([3])),
583
self.log('k._weave=' + pformat(k._weave))
585
self.check_read_write(k)
588
class Conflicts(TestBase):
589
"""Test detection of conflicting regions during a merge.
591
A base version is inserted, then two descendents try to
592
insert different lines in the same place. These should be
593
reported as a possible conflict and forwarded to the user."""
598
k.add([], ['aaa', 'bbb'])
599
k.add([0], ['aaa', '111', 'bbb'])
600
k.add([1], ['aaa', '222', 'bbb'])
602
merged = k.merge([1, 2])
604
self.assertEquals([[['aaa']],
610
class NonConflict(TestBase):
611
"""Two descendants insert compatible changes.
613
No conflict should be reported."""
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'])
651
class Khayyam(TestBase):
652
"""Test changes to multi-line texts, and read/write"""
655
"""A Book of Verses underneath the Bough,
656
A Jug of Wine, a Loaf of Bread, -- and Thou
657
Beside me singing in the Wilderness --
658
Oh, Wilderness were Paradise enow!""",
660
"""A Book of Verses underneath the Bough,
661
A Jug of Wine, a Loaf of Bread, -- and Thou
662
Beside me singing in the Wilderness --
663
Oh, Wilderness were Paradise now!""",
665
"""A Book of poems underneath the tree,
666
A Jug of Wine, a Loaf of Bread,
668
Beside me singing in the Wilderness --
669
Oh, Wilderness were Paradise now!
673
"""A Book of Verses underneath the Bough,
674
A Jug of Wine, a Loaf of Bread,
676
Beside me singing in the Wilderness --
677
Oh, Wilderness were Paradise now!""",
679
texts = [[l.strip() for l in t.split('\n')] for t in rawtexts]
685
ver = k.add('text%d' % i,
690
self.log("k._weave=" + pformat(k._weave))
692
for i, t in enumerate(texts):
693
self.assertEqual(k.get(i), t)
695
self.check_read_write(k)
699
class MergeCases(TestBase):
700
def doMerge(self, base, a, b, mp):
701
from cStringIO import StringIO
702
from textwrap import dedent
708
w.add('text0', [], map(addcrlf, base))
709
w.add('text1', [0], map(addcrlf, a))
710
w.add('text2', [0], map(addcrlf, b))
712
self.log('weave is:')
715
self.log(tmpf.getvalue())
717
self.log('merge plan:')
718
p = list(w.plan_merge(1, 2))
719
for state, line in p:
721
self.log('%12s | %s' % (state, line[:-1]))
725
mt.writelines(w.weave_merge(p))
727
self.log(mt.getvalue())
729
mp = map(addcrlf, mp)
730
self.assertEqual(mt.readlines(), mp)
733
def testOneInsert(self):
739
def testSeparateInserts(self):
740
self.doMerge(['aaa', 'bbb', 'ccc'],
741
['aaa', 'xxx', 'bbb', 'ccc'],
742
['aaa', 'bbb', 'yyy', 'ccc'],
743
['aaa', 'xxx', 'bbb', 'yyy', 'ccc'])
745
def testSameInsert(self):
746
self.doMerge(['aaa', 'bbb', 'ccc'],
747
['aaa', 'xxx', 'bbb', 'ccc'],
748
['aaa', 'xxx', 'bbb', 'yyy', 'ccc'],
749
['aaa', 'xxx', 'bbb', 'yyy', 'ccc'])
751
def testOverlappedInsert(self):
752
self.doMerge(['aaa', 'bbb'],
753
['aaa', 'xxx', 'yyy', 'bbb'],
754
['aaa', 'xxx', 'bbb'],
755
['aaa', '<<<<', 'xxx', 'yyy', '====', 'xxx', '>>>>', 'bbb'])
757
# really it ought to reduce this to
758
# ['aaa', 'xxx', 'yyy', 'bbb']
761
def testClashReplace(self):
762
self.doMerge(['aaa'],
765
['<<<<', 'xxx', '====', 'yyy', 'zzz', '>>>>'])
767
def testNonClashInsert(self):
768
self.doMerge(['aaa'],
771
['<<<<', 'xxx', 'aaa', '====', 'yyy', 'zzz', '>>>>'])
773
self.doMerge(['aaa'],
779
def testDeleteAndModify(self):
780
"""Clashing delete and modification.
782
If one side modifies a region and the other deletes it then
783
there should be a conflict with one side blank.
786
#######################################
787
# skippd, not working yet
790
self.doMerge(['aaa', 'bbb', 'ccc'],
791
['aaa', 'ddd', 'ccc'],
793
['<<<<', 'aaa', '====', '>>>>', 'ccc'])
796
class JoinWeavesTests(TestBase):
798
super(JoinWeavesTests, self).setUp()
799
self.weave1 = Weave()
800
self.lines1 = ['hello\n']
801
self.lines3 = ['hello\n', 'cruel\n', 'world\n']
802
self.weave1.add('v1', [], self.lines1)
803
self.weave1.add('v2', [0], ['hello\n', 'world\n'])
804
self.weave1.add('v3', [1], self.lines3)
806
def test_join_empty(self):
807
"""Join two empty weaves."""
808
eq = self.assertEqual
812
eq(w1.numversions(), 0)
814
def test_join_empty_to_nonempty(self):
815
"""Join empty weave onto nonempty."""
816
self.weave1.join(Weave())
817
self.assertEqual(len(self.weave1), 3)
819
def test_join_unrelated(self):
820
"""Join two weaves with no history in common."""
822
wb.add('b1', [], ['line from b\n'])
825
eq = self.assertEqual
827
eq(sorted(list(w1.iter_names())),
828
['b1', 'v1', 'v2', 'v3'])
830
def test_join_related(self):
831
wa = self.weave1.copy()
832
wb = self.weave1.copy()
833
wa.add('a1', ['v3'], ['hello\n', 'sweet\n', 'world\n'])
834
wb.add('b1', ['v3'], ['hello\n', 'pale blue\n', 'world\n'])
835
eq = self.assertEquals
840
eq(wa.get_lines('b1'),
841
['hello\n', 'pale blue\n', 'world\n'])
843
def test_join_parent_disagreement(self):
844
"""Cannot join weaves with different parents for a version."""
847
wa.add('v1', [], ['hello\n'])
849
wb.add('v1', ['v0'], ['hello\n'])
850
self.assertRaises(WeaveError,
853
def test_join_text_disagreement(self):
854
"""Cannot join weaves with different texts for a version."""
857
wa.add('v1', [], ['hello\n'])
858
wb.add('v1', [], ['not\n', 'hello\n'])
859
self.assertRaises(WeaveError,
862
def test_join_unordered(self):
863
"""Join weaves where indexes differ.
865
The source weave contains a different version at index 0."""
866
wa = self.weave1.copy()
868
wb.add('x1', [], ['line from x1\n'])
869
wb.add('v1', [], ['hello\n'])
870
wb.add('v2', ['v1'], ['hello\n', 'world\n'])
872
eq = self.assertEquals
873
eq(sorted(wa.iter_names()), ['v1', 'v2', 'v3', 'x1',])
874
eq(wa.get_text('x1'), 'line from x1\n')
876
def test_join_with_ghosts(self):
877
"""Join that inserts parents of an existing revision.
879
This can happen when merging from another branch who
880
knows about revisions the destination does not. In
881
this test the second weave knows of an additional parent of
882
v2. Any revisions which are in common still have to have the
884
return ###############################
885
wa = self.weave1.copy()
887
wb.add('x1', [], ['line from x1\n'])
888
wb.add('v1', [], ['hello\n'])
889
wb.add('v2', ['v1', 'x1'], ['hello\n', 'world\n'])
891
eq = self.assertEquals
892
eq(sorted(wa.iter_names()), ['v1', 'v2', 'v3', 'x1',])
893
eq(wa.get_text('x1'), 'line from x1\n')
213
896
if __name__ == '__main__':
899
sys.exit(unittest.main())