~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_weave.py

(vila) Calling super() instead of mentioning the base class in setUp avoid
 mistakes. (Vincent Ladeuil)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#! /usr/bin/python2.4
2
 
 
3
 
# Copyright (C) 2005 by Canonical Ltd
4
 
 
 
1
# Copyright (C) 2005-2009, 2011 Canonical Ltd
 
2
#
5
3
# This program is free software; you can redistribute it and/or modify
6
4
# it under the terms of the GNU General Public License as published by
7
5
# the Free Software Foundation; either version 2 of the License, or
8
6
# (at your option) any later version.
9
 
 
 
7
#
10
8
# This program is distributed in the hope that it will be useful,
11
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
11
# GNU General Public License for more details.
14
 
 
 
12
#
15
13
# You should have received a copy of the GNU General Public License
16
14
# along with this program; if not, write to the Free Software
17
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
16
 
19
17
 
20
18
# TODO: tests regarding version names
21
 
# TODO: rbc 20050108 test that join does not leave an inconsistent weave 
 
19
# TODO: rbc 20050108 test that join does not leave an inconsistent weave
22
20
#       if it fails.
23
21
 
24
22
"""test suite for weave algorithm"""
25
23
 
26
24
from pprint import pformat
27
25
 
28
 
import bzrlib.errors as errors
29
 
from bzrlib.weave import Weave, WeaveFormatError, WeaveError, reweave
 
26
from bzrlib import (
 
27
    errors,
 
28
    )
 
29
from bzrlib.osutils import sha_string
 
30
from bzrlib.tests import TestCase, TestCaseInTempDir
 
31
from bzrlib.weave import Weave, WeaveFormatError
30
32
from bzrlib.weavefile import write_weave, read_weave
31
 
from bzrlib.tests import TestCase
32
 
from bzrlib.osutils import sha_string
33
33
 
34
34
 
35
35
# texts for use in testing
39
39
 
40
40
 
41
41
class TestBase(TestCase):
 
42
 
42
43
    def check_read_write(self, k):
43
44
        """Check the weave k can be written & re-read."""
44
45
        from tempfile import TemporaryFile
63
64
 
64
65
class WeaveContains(TestBase):
65
66
    """Weave __contains__ operator"""
 
67
 
66
68
    def runTest(self):
67
 
        k = Weave()
 
69
        k = Weave(get_scope=lambda:None)
68
70
        self.assertFalse('foo' in k)
69
71
        k.add_lines('foo', [], TEXT_1)
70
72
        self.assertTrue('foo' in k)
71
73
 
72
74
 
73
75
class Easy(TestBase):
 
76
 
74
77
    def runTest(self):
75
78
        k = Weave()
76
79
 
77
80
 
78
 
class StoreText(TestBase):
79
 
    """Store and retrieve a simple text."""
80
 
 
81
 
    def test_storing_text(self):
82
 
        k = Weave()
83
 
        idx = k.add_lines('text0', [], TEXT_0)
84
 
        self.assertEqual(k.get_lines(idx), TEXT_0)
85
 
        self.assertEqual(idx, 0)
86
 
 
87
 
 
88
81
class AnnotateOne(TestBase):
 
82
 
89
83
    def runTest(self):
90
84
        k = Weave()
91
85
        k.add_lines('text0', [], TEXT_0)
93
87
                         [('text0', TEXT_0[0])])
94
88
 
95
89
 
96
 
class StoreTwo(TestBase):
97
 
    def runTest(self):
98
 
        k = Weave()
99
 
 
100
 
        idx = k.add_lines('text0', [], TEXT_0)
101
 
        self.assertEqual(idx, 0)
102
 
 
103
 
        idx = k.add_lines('text1', [], TEXT_1)
104
 
        self.assertEqual(idx, 1)
105
 
 
106
 
        self.assertEqual(k.get_lines(0), TEXT_0)
107
 
        self.assertEqual(k.get_lines(1), TEXT_1)
108
 
 
109
 
 
110
 
class GetSha1(TestBase):
111
 
    def test_get_sha1(self):
112
 
        k = Weave()
113
 
        k.add_lines('text0', [], 'text0')
114
 
        self.assertEqual('34dc0e430c642a26c3dd1c2beb7a8b4f4445eb79',
115
 
                         k.get_sha1('text0'))
116
 
        self.assertRaises(errors.RevisionNotPresent,
117
 
                          k.get_sha1, 0)
118
 
        self.assertRaises(errors.RevisionNotPresent,
119
 
                          k.get_sha1, 'text1')
120
 
                        
121
 
 
122
90
class InvalidAdd(TestBase):
123
91
    """Try to use invalid version number during add."""
 
92
 
124
93
    def runTest(self):
125
94
        k = Weave()
126
95
 
133
102
 
134
103
class RepeatedAdd(TestBase):
135
104
    """Add the same version twice; harmless."""
136
 
    def runTest(self):
 
105
 
 
106
    def test_duplicate_add(self):
137
107
        k = Weave()
138
108
        idx = k.add_lines('text0', [], TEXT_0)
139
109
        idx2 = k.add_lines('text0', [], TEXT_0)
141
111
 
142
112
 
143
113
class InvalidRepeatedAdd(TestBase):
 
114
 
144
115
    def runTest(self):
145
116
        k = Weave()
146
117
        k.add_lines('basis', [], TEXT_0)
155
126
                          'text0',
156
127
                          ['basis'],         # not the right parents
157
128
                          TEXT_0)
158
 
        
 
129
 
159
130
 
160
131
class InsertLines(TestBase):
161
132
    """Store a revision that adds one line to the original.
204
175
              ['text0', 'text1', 'text3'],
205
176
              ['line 1', 'aaa', 'middle line', 'bbb', 'line 2', 'ccc'])
206
177
 
207
 
        self.assertEqual(k.annotate('text4'), 
 
178
        self.assertEqual(k.annotate('text4'),
208
179
                         [('text0', 'line 1'),
209
180
                          ('text4', 'aaa'),
210
181
                          ('text3', 'middle line'),
223
194
        base_text = ['one', 'two', 'three', 'four']
224
195
 
225
196
        k.add_lines('text0', [], base_text)
226
 
        
 
197
 
227
198
        texts = [['one', 'two', 'three'],
228
199
                 ['two', 'three', 'four'],
229
200
                 ['one', 'four'],
260
231
                ]
261
232
        ################################### SKIPPED
262
233
        # Weave.get doesn't trap this anymore
263
 
        return 
 
234
        return
264
235
 
265
236
        self.assertRaises(WeaveFormatError,
266
237
                          k.get_lines,
267
 
                          0)        
 
238
                          0)
268
239
 
269
240
 
270
241
class CannedDelete(TestBase):
312
283
                'line to be deleted',
313
284
                (']', 1),
314
285
                ('{', 1),
315
 
                'replacement line',                
 
286
                'replacement line',
316
287
                ('}', 1),
317
288
                'last line',
318
289
                ('}', 0),
355
326
 
356
327
        ################################### SKIPPED
357
328
        # Weave.get doesn't trap this anymore
358
 
        return 
 
329
        return
359
330
 
360
331
 
361
332
        self.assertRaises(WeaveFormatError,
433
404
                          '  added in version 1',
434
405
                          '  also from v1',
435
406
                          '}'])
436
 
                       
 
407
 
437
408
        self.assertEqual(k.get_lines(2),
438
409
                         ['foo {',
439
410
                          '  added in v2',
445
416
                          '  added in v2',
446
417
                          '  also from v1',
447
418
                          '}'])
448
 
                         
 
419
 
449
420
 
450
421
class DeleteLines2(TestBase):
451
422
    """Test recording revisions that delete lines.
527
498
                ('}', 1),
528
499
                ('{', 2),
529
500
                "alternative second line",
530
 
                ('}', 2),                
 
501
                ('}', 2),
531
502
                ]
532
503
 
533
504
        k._sha1s = [sha_string('first line')
555
526
 
556
527
        text0 = ['cheddar', 'stilton', 'gruyere']
557
528
        text1 = ['cheddar', 'blue vein', 'neufchatel', 'chevre']
558
 
        
 
529
 
559
530
        k.add_lines('text0', [], text0)
560
531
        k.add_lines('text1', ['text0'], text1)
561
532
 
567
538
 
568
539
class Merge(TestBase):
569
540
    """Storage of versions that merge diverged parents"""
 
541
 
570
542
    def runTest(self):
571
543
        k = Weave()
572
544
 
643
615
            A Jug of Wine, a Loaf of Bread, -- and Thou
644
616
            Beside me singing in the Wilderness --
645
617
            Oh, Wilderness were Paradise enow!""",
646
 
            
 
618
 
647
619
            """A Book of Verses underneath the Bough,
648
620
            A Jug of Wine, a Loaf of Bread, -- and Thou
649
621
            Beside me singing in the Wilderness --
682
654
        self.check_read_write(k)
683
655
 
684
656
 
685
 
class MergeCases(TestBase):
686
 
    def doMerge(self, base, a, b, mp):
687
 
        from cStringIO import StringIO
688
 
        from textwrap import dedent
689
 
 
690
 
        def addcrlf(x):
691
 
            return x + '\n'
692
 
        
693
 
        w = Weave()
694
 
        w.add_lines('text0', [], map(addcrlf, base))
695
 
        w.add_lines('text1', ['text0'], map(addcrlf, a))
696
 
        w.add_lines('text2', ['text0'], map(addcrlf, b))
697
 
 
698
 
        self.log('weave is:')
699
 
        tmpf = StringIO()
700
 
        write_weave(w, tmpf)
701
 
        self.log(tmpf.getvalue())
702
 
 
703
 
        self.log('merge plan:')
704
 
        p = list(w.plan_merge('text1', 'text2'))
705
 
        for state, line in p:
706
 
            if line:
707
 
                self.log('%12s | %s' % (state, line[:-1]))
708
 
 
709
 
        self.log('merge:')
710
 
        mt = StringIO()
711
 
        mt.writelines(w.weave_merge(p))
712
 
        mt.seek(0)
713
 
        self.log(mt.getvalue())
714
 
 
715
 
        mp = map(addcrlf, mp)
716
 
        self.assertEqual(mt.readlines(), mp)
717
 
        
718
 
        
719
 
    def testOneInsert(self):
720
 
        self.doMerge([],
721
 
                     ['aa'],
722
 
                     [],
723
 
                     ['aa'])
724
 
 
725
 
    def testSeparateInserts(self):
726
 
        self.doMerge(['aaa', 'bbb', 'ccc'],
727
 
                     ['aaa', 'xxx', 'bbb', 'ccc'],
728
 
                     ['aaa', 'bbb', 'yyy', 'ccc'],
729
 
                     ['aaa', 'xxx', 'bbb', 'yyy', 'ccc'])
730
 
 
731
 
    def testSameInsert(self):
732
 
        self.doMerge(['aaa', 'bbb', 'ccc'],
733
 
                     ['aaa', 'xxx', 'bbb', 'ccc'],
734
 
                     ['aaa', 'xxx', 'bbb', 'yyy', 'ccc'],
735
 
                     ['aaa', 'xxx', 'bbb', 'yyy', 'ccc'])
736
 
 
737
 
    def testOverlappedInsert(self):
738
 
        self.doMerge(['aaa', 'bbb'],
739
 
                     ['aaa', 'xxx', 'yyy', 'bbb'],
740
 
                     ['aaa', 'xxx', 'bbb'],
741
 
                     ['aaa', '<<<<<<< ', 'xxx', 'yyy', '=======', 'xxx', 
742
 
                      '>>>>>>> ', 'bbb'])
743
 
 
744
 
        # really it ought to reduce this to 
745
 
        # ['aaa', 'xxx', 'yyy', 'bbb']
746
 
 
747
 
 
748
 
    def testClashReplace(self):
749
 
        self.doMerge(['aaa'],
750
 
                     ['xxx'],
751
 
                     ['yyy', 'zzz'],
752
 
                     ['<<<<<<< ', 'xxx', '=======', 'yyy', 'zzz', 
753
 
                      '>>>>>>> '])
754
 
 
755
 
    def testNonClashInsert(self):
756
 
        self.doMerge(['aaa'],
757
 
                     ['xxx', 'aaa'],
758
 
                     ['yyy', 'zzz'],
759
 
                     ['<<<<<<< ', 'xxx', 'aaa', '=======', 'yyy', 'zzz', 
760
 
                      '>>>>>>> '])
761
 
 
762
 
        self.doMerge(['aaa'],
763
 
                     ['aaa'],
764
 
                     ['yyy', 'zzz'],
765
 
                     ['yyy', 'zzz'])
766
 
 
767
 
 
768
 
    def testDeleteAndModify(self):
769
 
        """Clashing delete and modification.
770
 
 
771
 
        If one side modifies a region and the other deletes it then
772
 
        there should be a conflict with one side blank.
773
 
        """
774
 
 
775
 
        #######################################
776
 
        # skippd, not working yet
777
 
        return
778
 
        
779
 
        self.doMerge(['aaa', 'bbb', 'ccc'],
780
 
                     ['aaa', 'ddd', 'ccc'],
781
 
                     ['aaa', 'ccc'],
782
 
                     ['<<<<<<<< ', 'aaa', '=======', '>>>>>>> ', 'ccc'])
783
 
 
784
 
    def _test_merge_from_strings(self, base, a, b, expected):
785
 
        w = Weave()
786
 
        w.add_lines('text0', [], base.splitlines(True))
787
 
        w.add_lines('text1', ['text0'], a.splitlines(True))
788
 
        w.add_lines('text2', ['text0'], b.splitlines(True))
789
 
        self.log('merge plan:')
790
 
        p = list(w.plan_merge('text1', 'text2'))
791
 
        for state, line in p:
792
 
            if line:
793
 
                self.log('%12s | %s' % (state, line[:-1]))
794
 
        self.log('merge result:')
795
 
        result_text = ''.join(w.weave_merge(p))
796
 
        self.log(result_text)
797
 
        self.assertEqualDiff(result_text, expected)
798
 
 
799
 
    def test_deletion_extended(self):
800
 
        """One side deletes, the other deletes more.
801
 
        """
802
 
        base = """\
803
 
            line 1
804
 
            line 2
805
 
            line 3
806
 
            """
807
 
        a = """\
808
 
            line 1
809
 
            line 2
810
 
            """
811
 
        b = """\
812
 
            line 1
813
 
            """
814
 
        result = """\
815
 
            line 1
816
 
            """
817
 
        self._test_merge_from_strings(base, a, b, result)
818
 
 
819
 
    def test_deletion_overlap(self):
820
 
        """Delete overlapping regions with no other conflict.
821
 
 
822
 
        Arguably it'd be better to treat these as agreement, rather than 
823
 
        conflict, but for now conflict is safer.
824
 
        """
825
 
        base = """\
826
 
            start context
827
 
            int a() {}
828
 
            int b() {}
829
 
            int c() {}
830
 
            end context
831
 
            """
832
 
        a = """\
833
 
            start context
834
 
            int a() {}
835
 
            end context
836
 
            """
837
 
        b = """\
838
 
            start context
839
 
            int c() {}
840
 
            end context
841
 
            """
842
 
        result = """\
843
 
            start context
844
 
<<<<<<< 
845
 
            int a() {}
846
 
=======
847
 
            int c() {}
848
 
>>>>>>> 
849
 
            end context
850
 
            """
851
 
        self._test_merge_from_strings(base, a, b, result)
852
 
 
853
 
    def test_agreement_deletion(self):
854
 
        """Agree to delete some lines, without conflicts."""
855
 
        base = """\
856
 
            start context
857
 
            base line 1
858
 
            base line 2
859
 
            end context
860
 
            """
861
 
        a = """\
862
 
            start context
863
 
            base line 1
864
 
            end context
865
 
            """
866
 
        b = """\
867
 
            start context
868
 
            base line 1
869
 
            end context
870
 
            """
871
 
        result = """\
872
 
            start context
873
 
            base line 1
874
 
            end context
875
 
            """
876
 
        self._test_merge_from_strings(base, a, b, result)
877
 
 
878
 
    def test_sync_on_deletion(self):
879
 
        """Specific case of merge where we can synchronize incorrectly.
880
 
        
881
 
        A previous version of the weave merge concluded that the two versions
882
 
        agreed on deleting line 2, and this could be a synchronization point.
883
 
        Line 1 was then considered in isolation, and thought to be deleted on 
884
 
        both sides.
885
 
 
886
 
        It's better to consider the whole thing as a disagreement region.
887
 
        """
888
 
        base = """\
889
 
            start context
890
 
            base line 1
891
 
            base line 2
892
 
            end context
893
 
            """
894
 
        a = """\
895
 
            start context
896
 
            base line 1
897
 
            a's replacement line 2
898
 
            end context
899
 
            """
900
 
        b = """\
901
 
            start context
902
 
            b replaces
903
 
            both lines
904
 
            end context
905
 
            """
906
 
        result = """\
907
 
            start context
908
 
<<<<<<< 
909
 
            base line 1
910
 
            a's replacement line 2
911
 
=======
912
 
            b replaces
913
 
            both lines
914
 
>>>>>>> 
915
 
            end context
916
 
            """
917
 
        self._test_merge_from_strings(base, a, b, result)
918
 
 
919
 
 
920
657
class JoinWeavesTests(TestBase):
 
658
 
921
659
    def setUp(self):
922
660
        super(JoinWeavesTests, self).setUp()
923
661
        self.weave1 = Weave()
926
664
        self.weave1.add_lines('v1', [], self.lines1)
927
665
        self.weave1.add_lines('v2', ['v1'], ['hello\n', 'world\n'])
928
666
        self.weave1.add_lines('v3', ['v2'], self.lines3)
929
 
        
930
 
    def test_join_empty(self):
931
 
        """Join two empty weaves."""
932
 
        eq = self.assertEqual
933
 
        w1 = Weave()
934
 
        w2 = Weave()
935
 
        w1.join(w2)
936
 
        eq(len(w1), 0)
937
 
        
938
 
    def test_join_empty_to_nonempty(self):
939
 
        """Join empty weave onto nonempty."""
940
 
        self.weave1.join(Weave())
941
 
        self.assertEqual(len(self.weave1), 3)
942
 
 
943
 
    def test_join_unrelated(self):
944
 
        """Join two weaves with no history in common."""
945
 
        wb = Weave()
946
 
        wb.add_lines('b1', [], ['line from b\n'])
947
 
        w1 = self.weave1
948
 
        w1.join(wb)
949
 
        eq = self.assertEqual
950
 
        eq(len(w1), 4)
951
 
        eq(sorted(w1.versions()),
952
 
           ['b1', 'v1', 'v2', 'v3'])
953
 
 
954
 
    def test_join_related(self):
955
 
        wa = self.weave1.copy()
956
 
        wb = self.weave1.copy()
957
 
        wa.add_lines('a1', ['v3'], ['hello\n', 'sweet\n', 'world\n'])
958
 
        wb.add_lines('b1', ['v3'], ['hello\n', 'pale blue\n', 'world\n'])
959
 
        eq = self.assertEquals
960
 
        eq(len(wa), 4)
961
 
        eq(len(wb), 4)
962
 
        wa.join(wb)
963
 
        eq(len(wa), 5)
964
 
        eq(wa.get_lines('b1'),
965
 
           ['hello\n', 'pale blue\n', 'world\n'])
966
 
 
967
 
    def test_join_parent_disagreement(self):
968
 
        #join reconciles differening parents into a union.
969
 
        wa = Weave()
970
 
        wb = Weave()
971
 
        wa.add_lines('v1', [], ['hello\n'])
972
 
        wb.add_lines('v0', [], [])
973
 
        wb.add_lines('v1', ['v0'], ['hello\n'])
974
 
        wa.join(wb)
975
 
        self.assertEqual(['v0'], wa.get_parents('v1'))
976
 
 
977
 
    def test_join_text_disagreement(self):
978
 
        """Cannot join weaves with different texts for a version."""
979
 
        wa = Weave()
980
 
        wb = Weave()
981
 
        wa.add_lines('v1', [], ['hello\n'])
982
 
        wb.add_lines('v1', [], ['not\n', 'hello\n'])
983
 
        self.assertRaises(WeaveError,
984
 
                          wa.join, wb)
985
 
 
986
 
    def test_join_unordered(self):
987
 
        """Join weaves where indexes differ.
988
 
        
989
 
        The source weave contains a different version at index 0."""
990
 
        wa = self.weave1.copy()
991
 
        wb = Weave()
992
 
        wb.add_lines('x1', [], ['line from x1\n'])
993
 
        wb.add_lines('v1', [], ['hello\n'])
994
 
        wb.add_lines('v2', ['v1'], ['hello\n', 'world\n'])
995
 
        wa.join(wb)
996
 
        eq = self.assertEquals
997
 
        eq(sorted(wa.versions()), ['v1', 'v2', 'v3', 'x1',])
998
 
        eq(wa.get_text('x1'), 'line from x1\n')
999
667
 
1000
668
    def test_written_detection(self):
1001
669
        # Test detection of weave file corruption.
1046
714
        self.assertRaises(errors.WeaveInvalidChecksum, w.check)
1047
715
 
1048
716
 
 
717
class TestWeave(TestCase):
 
718
 
 
719
    def test_allow_reserved_false(self):
 
720
        w = Weave('name', allow_reserved=False)
 
721
        # Add lines is checked at the WeaveFile level, not at the Weave level
 
722
        w.add_lines('name:', [], TEXT_1)
 
723
        # But get_lines is checked at this level
 
724
        self.assertRaises(errors.ReservedId, w.get_lines, 'name:')
 
725
 
 
726
    def test_allow_reserved_true(self):
 
727
        w = Weave('name', allow_reserved=True)
 
728
        w.add_lines('name:', [], TEXT_1)
 
729
        self.assertEqual(TEXT_1, w.get_lines('name:'))
 
730
 
 
731
 
1049
732
class InstrumentedWeave(Weave):
1050
733
    """Keep track of how many times functions are called."""
1051
 
    
 
734
 
1052
735
    def __init__(self, weave_name=None):
1053
736
        self._extract_count = 0
1054
737
        Weave.__init__(self, weave_name=weave_name)
1058
741
        return Weave._extract(self, versions)
1059
742
 
1060
743
 
1061
 
class JoinOptimization(TestCase):
1062
 
    """Test that Weave.join() doesn't extract all texts, only what must be done."""
1063
 
 
1064
 
    def test_join(self):
1065
 
        w1 = InstrumentedWeave()
1066
 
        w2 = InstrumentedWeave()
1067
 
 
1068
 
        txt0 = ['a\n']
1069
 
        txt1 = ['a\n', 'b\n']
1070
 
        txt2 = ['a\n', 'c\n']
1071
 
        txt3 = ['a\n', 'b\n', 'c\n']
1072
 
 
1073
 
        w1.add_lines('txt0', [], txt0) # extract 1a
1074
 
        w2.add_lines('txt0', [], txt0) # extract 1b
1075
 
        w1.add_lines('txt1', ['txt0'], txt1)# extract 2a
1076
 
        w2.add_lines('txt2', ['txt0'], txt2)# extract 2b
1077
 
        w1.join(w2) # extract 3a to add txt2 
1078
 
        w2.join(w1) # extract 3b to add txt1 
1079
 
 
1080
 
        w1.add_lines('txt3', ['txt1', 'txt2'], txt3) # extract 4a 
1081
 
        w2.add_lines('txt3', ['txt2', 'txt1'], txt3) # extract 4b
1082
 
        # These secretly have inverted parents
1083
 
 
1084
 
        # This should not have to do any extractions
1085
 
        w1.join(w2) # NO extract, texts already present with same parents
1086
 
        w2.join(w1) # NO extract, texts already present with same parents
1087
 
 
1088
 
        self.assertEqual(4, w1._extract_count)
1089
 
        self.assertEqual(4, w2._extract_count)
1090
 
 
1091
 
    def test_double_parent(self):
1092
 
        # It should not be considered illegal to add
1093
 
        # a revision with the same parent twice
1094
 
        w1 = InstrumentedWeave()
1095
 
        w2 = InstrumentedWeave()
1096
 
 
1097
 
        txt0 = ['a\n']
1098
 
        txt1 = ['a\n', 'b\n']
1099
 
        txt2 = ['a\n', 'c\n']
1100
 
        txt3 = ['a\n', 'b\n', 'c\n']
1101
 
 
1102
 
        w1.add_lines('txt0', [], txt0)
1103
 
        w2.add_lines('txt0', [], txt0)
1104
 
        w1.add_lines('txt1', ['txt0'], txt1)
1105
 
        w2.add_lines('txt1', ['txt0', 'txt0'], txt1)
1106
 
        # Same text, effectively the same, because the
1107
 
        # parent is only repeated
1108
 
        w1.join(w2) # extract 3a to add txt2 
1109
 
        w2.join(w1) # extract 3b to add txt1 
1110
 
 
1111
 
 
1112
744
class TestNeedsReweave(TestCase):
1113
745
    """Internal corner cases for when reweave is needed."""
1114
746
 
1125
757
        self.assertFalse(w1._compatible_parents(set(), set([1])))
1126
758
        self.assertFalse(w1._compatible_parents(my_parents, set([1, 2, 3, 4])))
1127
759
        self.assertFalse(w1._compatible_parents(my_parents, set([4])))
 
760
 
 
761
 
 
762
class TestWeaveFile(TestCaseInTempDir):
 
763
 
 
764
    def test_empty_file(self):
 
765
        f = open('empty.weave', 'wb+')
 
766
        try:
 
767
            self.assertRaises(errors.WeaveFormatError,
 
768
                              read_weave, f)
 
769
        finally:
 
770
            f.close()