~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_versionedfile.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2006-04-19 08:36:50 UTC
  • mfrom: (1664.2.14 bzr.knitplanmerge)
  • Revision ID: pqm@pqm.ubuntu.com-20060419083650-f26d296f90d75d88
Implement plan_merge for knits

Show diffs side-by-side

added added

removed removed

Lines of Context:
18
18
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19
19
 
20
20
 
 
21
from StringIO import StringIO
 
22
 
21
23
import bzrlib
22
24
import bzrlib.errors as errors
23
25
from bzrlib.errors import (
34
36
from bzrlib.transport.memory import MemoryTransport
35
37
import bzrlib.versionedfile as versionedfile
36
38
from bzrlib.weave import WeaveFile
37
 
from bzrlib.weavefile import read_weave
 
39
from bzrlib.weavefile import read_weave, write_weave
38
40
 
39
41
 
40
42
class VersionedFileTestMixIn(object):
815
817
 
816
818
    def get_factory(self):
817
819
        return KnitVersionedFile
 
820
 
 
821
 
 
822
class MergeCasesMixin(object):
 
823
 
 
824
    def doMerge(self, base, a, b, mp):
 
825
        from cStringIO import StringIO
 
826
        from textwrap import dedent
 
827
 
 
828
        def addcrlf(x):
 
829
            return x + '\n'
 
830
        
 
831
        w = self.get_file()
 
832
        w.add_lines('text0', [], map(addcrlf, base))
 
833
        w.add_lines('text1', ['text0'], map(addcrlf, a))
 
834
        w.add_lines('text2', ['text0'], map(addcrlf, b))
 
835
 
 
836
        self.log_contents(w)
 
837
 
 
838
        self.log('merge plan:')
 
839
        p = list(w.plan_merge('text1', 'text2'))
 
840
        for state, line in p:
 
841
            if line:
 
842
                self.log('%12s | %s' % (state, line[:-1]))
 
843
 
 
844
        self.log('merge:')
 
845
        mt = StringIO()
 
846
        mt.writelines(w.weave_merge(p))
 
847
        mt.seek(0)
 
848
        self.log(mt.getvalue())
 
849
 
 
850
        mp = map(addcrlf, mp)
 
851
        self.assertEqual(mt.readlines(), mp)
 
852
        
 
853
        
 
854
    def testOneInsert(self):
 
855
        self.doMerge([],
 
856
                     ['aa'],
 
857
                     [],
 
858
                     ['aa'])
 
859
 
 
860
    def testSeparateInserts(self):
 
861
        self.doMerge(['aaa', 'bbb', 'ccc'],
 
862
                     ['aaa', 'xxx', 'bbb', 'ccc'],
 
863
                     ['aaa', 'bbb', 'yyy', 'ccc'],
 
864
                     ['aaa', 'xxx', 'bbb', 'yyy', 'ccc'])
 
865
 
 
866
    def testSameInsert(self):
 
867
        self.doMerge(['aaa', 'bbb', 'ccc'],
 
868
                     ['aaa', 'xxx', 'bbb', 'ccc'],
 
869
                     ['aaa', 'xxx', 'bbb', 'yyy', 'ccc'],
 
870
                     ['aaa', 'xxx', 'bbb', 'yyy', 'ccc'])
 
871
    overlappedInsertExpected = ['aaa', 'xxx', 'yyy', 'bbb']
 
872
    def testOverlappedInsert(self):
 
873
        self.doMerge(['aaa', 'bbb'],
 
874
                     ['aaa', 'xxx', 'yyy', 'bbb'],
 
875
                     ['aaa', 'xxx', 'bbb'], self.overlappedInsertExpected)
 
876
 
 
877
        # really it ought to reduce this to 
 
878
        # ['aaa', 'xxx', 'yyy', 'bbb']
 
879
 
 
880
 
 
881
    def testClashReplace(self):
 
882
        self.doMerge(['aaa'],
 
883
                     ['xxx'],
 
884
                     ['yyy', 'zzz'],
 
885
                     ['<<<<<<< ', 'xxx', '=======', 'yyy', 'zzz', 
 
886
                      '>>>>>>> '])
 
887
 
 
888
    def testNonClashInsert1(self):
 
889
        self.doMerge(['aaa'],
 
890
                     ['xxx', 'aaa'],
 
891
                     ['yyy', 'zzz'],
 
892
                     ['<<<<<<< ', 'xxx', 'aaa', '=======', 'yyy', 'zzz', 
 
893
                      '>>>>>>> '])
 
894
 
 
895
    def testNonClashInsert2(self):
 
896
        self.doMerge(['aaa'],
 
897
                     ['aaa'],
 
898
                     ['yyy', 'zzz'],
 
899
                     ['yyy', 'zzz'])
 
900
 
 
901
 
 
902
    def testDeleteAndModify(self):
 
903
        """Clashing delete and modification.
 
904
 
 
905
        If one side modifies a region and the other deletes it then
 
906
        there should be a conflict with one side blank.
 
907
        """
 
908
 
 
909
        #######################################
 
910
        # skippd, not working yet
 
911
        return
 
912
        
 
913
        self.doMerge(['aaa', 'bbb', 'ccc'],
 
914
                     ['aaa', 'ddd', 'ccc'],
 
915
                     ['aaa', 'ccc'],
 
916
                     ['<<<<<<<< ', 'aaa', '=======', '>>>>>>> ', 'ccc'])
 
917
 
 
918
    def _test_merge_from_strings(self, base, a, b, expected):
 
919
        w = self.get_file()
 
920
        w.add_lines('text0', [], base.splitlines(True))
 
921
        w.add_lines('text1', ['text0'], a.splitlines(True))
 
922
        w.add_lines('text2', ['text0'], b.splitlines(True))
 
923
        self.log('merge plan:')
 
924
        p = list(w.plan_merge('text1', 'text2'))
 
925
        for state, line in p:
 
926
            if line:
 
927
                self.log('%12s | %s' % (state, line[:-1]))
 
928
        self.log('merge result:')
 
929
        result_text = ''.join(w.weave_merge(p))
 
930
        self.log(result_text)
 
931
        self.assertEqualDiff(result_text, expected)
 
932
 
 
933
    def test_weave_merge_conflicts(self):
 
934
        # does weave merge properly handle plans that end with unchanged?
 
935
        result = ''.join(self.get_file().weave_merge([('new-a', 'hello\n')]))
 
936
        self.assertEqual(result, 'hello\n')
 
937
 
 
938
    def test_deletion_extended(self):
 
939
        """One side deletes, the other deletes more.
 
940
        """
 
941
        base = """\
 
942
            line 1
 
943
            line 2
 
944
            line 3
 
945
            """
 
946
        a = """\
 
947
            line 1
 
948
            line 2
 
949
            """
 
950
        b = """\
 
951
            line 1
 
952
            """
 
953
        result = """\
 
954
            line 1
 
955
            """
 
956
        self._test_merge_from_strings(base, a, b, result)
 
957
 
 
958
    def test_deletion_overlap(self):
 
959
        """Delete overlapping regions with no other conflict.
 
960
 
 
961
        Arguably it'd be better to treat these as agreement, rather than 
 
962
        conflict, but for now conflict is safer.
 
963
        """
 
964
        base = """\
 
965
            start context
 
966
            int a() {}
 
967
            int b() {}
 
968
            int c() {}
 
969
            end context
 
970
            """
 
971
        a = """\
 
972
            start context
 
973
            int a() {}
 
974
            end context
 
975
            """
 
976
        b = """\
 
977
            start context
 
978
            int c() {}
 
979
            end context
 
980
            """
 
981
        result = """\
 
982
            start context
 
983
<<<<<<< 
 
984
            int a() {}
 
985
=======
 
986
            int c() {}
 
987
>>>>>>> 
 
988
            end context
 
989
            """
 
990
        self._test_merge_from_strings(base, a, b, result)
 
991
 
 
992
    def test_agreement_deletion(self):
 
993
        """Agree to delete some lines, without conflicts."""
 
994
        base = """\
 
995
            start context
 
996
            base line 1
 
997
            base line 2
 
998
            end context
 
999
            """
 
1000
        a = """\
 
1001
            start context
 
1002
            base line 1
 
1003
            end context
 
1004
            """
 
1005
        b = """\
 
1006
            start context
 
1007
            base line 1
 
1008
            end context
 
1009
            """
 
1010
        result = """\
 
1011
            start context
 
1012
            base line 1
 
1013
            end context
 
1014
            """
 
1015
        self._test_merge_from_strings(base, a, b, result)
 
1016
 
 
1017
    def test_sync_on_deletion(self):
 
1018
        """Specific case of merge where we can synchronize incorrectly.
 
1019
        
 
1020
        A previous version of the weave merge concluded that the two versions
 
1021
        agreed on deleting line 2, and this could be a synchronization point.
 
1022
        Line 1 was then considered in isolation, and thought to be deleted on 
 
1023
        both sides.
 
1024
 
 
1025
        It's better to consider the whole thing as a disagreement region.
 
1026
        """
 
1027
        base = """\
 
1028
            start context
 
1029
            base line 1
 
1030
            base line 2
 
1031
            end context
 
1032
            """
 
1033
        a = """\
 
1034
            start context
 
1035
            base line 1
 
1036
            a's replacement line 2
 
1037
            end context
 
1038
            """
 
1039
        b = """\
 
1040
            start context
 
1041
            b replaces
 
1042
            both lines
 
1043
            end context
 
1044
            """
 
1045
        result = """\
 
1046
            start context
 
1047
<<<<<<< 
 
1048
            base line 1
 
1049
            a's replacement line 2
 
1050
=======
 
1051
            b replaces
 
1052
            both lines
 
1053
>>>>>>> 
 
1054
            end context
 
1055
            """
 
1056
        self._test_merge_from_strings(base, a, b, result)
 
1057
 
 
1058
 
 
1059
class TestKnitMerge(TestCaseWithTransport, MergeCasesMixin):
 
1060
 
 
1061
    def get_file(self, name='foo'):
 
1062
        return KnitVersionedFile(name, get_transport(self.get_url('.')),
 
1063
                                 delta=True, create=True)
 
1064
 
 
1065
    def log_contents(self, w):
 
1066
        pass
 
1067
 
 
1068
 
 
1069
class TestWeaveMerge(TestCaseWithTransport, MergeCasesMixin):
 
1070
 
 
1071
    def get_file(self, name='foo'):
 
1072
        return WeaveFile(name, get_transport(self.get_url('.')), create=True)
 
1073
 
 
1074
    def log_contents(self, w):
 
1075
        self.log('weave is:')
 
1076
        tmpf = StringIO()
 
1077
        write_weave(w, tmpf)
 
1078
        self.log(tmpf.getvalue())
 
1079
 
 
1080
    overlappedInsertExpected = ['aaa', '<<<<<<< ', 'xxx', 'yyy', '=======', 
 
1081
                                'xxx', '>>>>>>> ', 'bbb']