1
# Copyright (C) 2004, 2005 by Canonical Ltd
 
 
3
# This program is free software; you can redistribute it and/or modify
 
 
4
# it under the terms of the GNU General Public License as published by
 
 
5
# the Free Software Foundation; either version 2 of the License, or
 
 
6
# (at your option) any later version.
 
 
8
# This program is distributed in the hope that it will be useful,
 
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 
11
# GNU General Public License for more details.
 
 
13
# You should have received a copy of the GNU General Public License
 
 
14
# along with this program; if not, write to the Free Software
 
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 
18
from bzrlib.selftest import TestCaseInTempDir, TestCase
 
 
19
from bzrlib.merge3 import Merge3
 
 
20
from bzrlib.errors import CantReprocessAndShowBase
 
 
23
    from cStringIO import StringIO
 
 
24
    return StringIO(t).readlines()
 
 
26
############################################################
 
 
27
# test case data from the gnu diffutils manual
 
 
29
TZU = split_lines("""     The Nameless is the origin of Heaven and Earth;
 
 
30
     The named is the mother of all things.
 
 
32
     Therefore let there always be non-being,
 
 
33
       so we may see their subtlety,
 
 
34
     And let there always be being,
 
 
35
       so we may see their outcome.
 
 
37
     But after they are produced,
 
 
38
       they have different names.
 
 
39
     They both may be called deep and profound.
 
 
40
     Deeper and more profound,
 
 
41
     The door of all subtleties!
 
 
44
LAO = split_lines("""     The Way that can be told of is not the eternal Way;
 
 
45
     The name that can be named is not the eternal name.
 
 
46
     The Nameless is the origin of Heaven and Earth;
 
 
47
     The Named is the mother of all things.
 
 
48
     Therefore let there always be non-being,
 
 
49
       so we may see their subtlety,
 
 
50
     And let there always be being,
 
 
51
       so we may see their outcome.
 
 
53
     But after they are produced,
 
 
54
       they have different names.
 
 
58
TAO = split_lines("""     The Way that can be told of is not the eternal Way;
 
 
59
     The name that can be named is not the eternal name.
 
 
60
     The Nameless is the origin of Heaven and Earth;
 
 
61
     The named is the mother of all things.
 
 
63
     Therefore let there always be non-being,
 
 
64
       so we may see their subtlety,
 
 
65
     And let there always be being,
 
 
66
       so we may see their result.
 
 
68
     But after they are produced,
 
 
69
       they have different names.
 
 
71
       -- The Way of Lao-Tzu, tr. Wing-tsit Chan
 
 
75
MERGED_RESULT = split_lines("""     The Way that can be told of is not the eternal Way;
 
 
76
     The name that can be named is not the eternal name.
 
 
77
     The Nameless is the origin of Heaven and Earth;
 
 
78
     The Named is the mother of all things.
 
 
79
     Therefore let there always be non-being,
 
 
80
       so we may see their subtlety,
 
 
81
     And let there always be being,
 
 
82
       so we may see their result.
 
 
84
     But after they are produced,
 
 
85
       they have different names.
 
 
89
       -- The Way of Lao-Tzu, tr. Wing-tsit Chan
 
 
94
class TestMerge3(TestCase):
 
 
96
    def test_no_changes(self):
 
 
97
        """No conflicts because nothing changed"""
 
 
98
        m3 = Merge3(['aaa', 'bbb'],
 
 
102
        self.assertEquals(m3.find_unconflicted(),
 
 
105
        self.assertEquals(list(m3.find_sync_regions()),
 
 
111
        self.assertEquals(list(m3.merge_regions()),
 
 
112
                          [('unchanged', 0, 2)])
 
 
114
        self.assertEquals(list(m3.merge_groups()),
 
 
115
                          [('unchanged', ['aaa', 'bbb'])])
 
 
117
    def test_front_insert(self):
 
 
119
                    ['aaa', 'bbb', 'zz'],
 
 
122
        # todo: should use a sentinal at end as from get_matching_blocks
 
 
123
        # to match without zz
 
 
124
        self.assertEquals(list(m3.find_sync_regions()),
 
 
128
        self.assertEquals(list(m3.merge_regions()),
 
 
130
                           ('unchanged', 0, 1)])
 
 
132
        self.assertEquals(list(m3.merge_groups()),
 
 
133
                          [('a', ['aaa', 'bbb']),
 
 
134
                           ('unchanged', ['zz'])])
 
 
136
    def test_null_insert(self):
 
 
140
        # todo: should use a sentinal at end as from get_matching_blocks
 
 
141
        # to match without zz
 
 
142
        self.assertEquals(list(m3.find_sync_regions()),
 
 
145
        self.assertEquals(list(m3.merge_regions()),
 
 
148
        self.assertEquals(list(m3.merge_lines()),
 
 
151
    def test_no_conflicts(self):
 
 
152
        """No conflicts because only one side changed"""
 
 
153
        m3 = Merge3(['aaa', 'bbb'],
 
 
154
                    ['aaa', '111', 'bbb'],
 
 
157
        self.assertEquals(m3.find_unconflicted(),
 
 
160
        self.assertEquals(list(m3.find_sync_regions()),
 
 
165
        self.assertEquals(list(m3.merge_regions()),
 
 
166
                          [('unchanged', 0, 1),
 
 
168
                           ('unchanged', 1, 2),])
 
 
170
    def test_append_a(self):
 
 
171
        m3 = Merge3(['aaa\n', 'bbb\n'],
 
 
172
                    ['aaa\n', 'bbb\n', '222\n'],
 
 
175
        self.assertEquals(''.join(m3.merge_lines()),
 
 
178
    def test_append_b(self):
 
 
179
        m3 = Merge3(['aaa\n', 'bbb\n'],
 
 
181
                    ['aaa\n', 'bbb\n', '222\n'])
 
 
183
        self.assertEquals(''.join(m3.merge_lines()),
 
 
186
    def test_append_agreement(self):
 
 
187
        m3 = Merge3(['aaa\n', 'bbb\n'],
 
 
188
                    ['aaa\n', 'bbb\n', '222\n'],
 
 
189
                    ['aaa\n', 'bbb\n', '222\n'])
 
 
191
        self.assertEquals(''.join(m3.merge_lines()),
 
 
194
    def test_append_clash(self):
 
 
195
        m3 = Merge3(['aaa\n', 'bbb\n'],
 
 
196
                    ['aaa\n', 'bbb\n', '222\n'],
 
 
197
                    ['aaa\n', 'bbb\n', '333\n'])
 
 
199
        ml = m3.merge_lines(name_a='a',
 
 
204
        self.assertEquals(''.join(ml),
 
 
215
    def test_insert_agreement(self):
 
 
216
        m3 = Merge3(['aaa\n', 'bbb\n'],
 
 
217
                    ['aaa\n', '222\n', 'bbb\n'],
 
 
218
                    ['aaa\n', '222\n', 'bbb\n'])
 
 
220
        ml = m3.merge_lines(name_a='a',
 
 
225
        self.assertEquals(''.join(ml), 'aaa\n222\nbbb\n')
 
 
228
    def test_insert_clash(self):
 
 
229
        """Both try to insert lines in the same place."""
 
 
230
        m3 = Merge3(['aaa\n', 'bbb\n'],
 
 
231
                    ['aaa\n', '111\n', 'bbb\n'],
 
 
232
                    ['aaa\n', '222\n', 'bbb\n'])
 
 
234
        self.assertEquals(m3.find_unconflicted(),
 
 
237
        self.assertEquals(list(m3.find_sync_regions()),
 
 
242
        self.assertEquals(list(m3.merge_regions()),
 
 
244
                           ('conflict', 1,1, 1,2, 1,2),
 
 
247
        self.assertEquals(list(m3.merge_groups()),
 
 
248
                          [('unchanged', ['aaa\n']),
 
 
249
                           ('conflict', [], ['111\n'], ['222\n']),
 
 
250
                           ('unchanged', ['bbb\n']),
 
 
253
        ml = m3.merge_lines(name_a='a',
 
 
258
        self.assertEquals(''.join(ml),
 
 
268
    def test_replace_clash(self):
 
 
269
        """Both try to insert lines in the same place."""
 
 
270
        m3 = Merge3(['aaa', '000', 'bbb'],
 
 
271
                    ['aaa', '111', 'bbb'],
 
 
272
                    ['aaa', '222', 'bbb'])
 
 
274
        self.assertEquals(m3.find_unconflicted(),
 
 
277
        self.assertEquals(list(m3.find_sync_regions()),
 
 
282
    def test_replace_multi(self):
 
 
283
        """Replacement with regions of different size."""
 
 
284
        m3 = Merge3(['aaa', '000', '000', 'bbb'],
 
 
285
                    ['aaa', '111', '111', '111', 'bbb'],
 
 
286
                    ['aaa', '222', '222', '222', '222', 'bbb'])
 
 
288
        self.assertEquals(m3.find_unconflicted(),
 
 
292
        self.assertEquals(list(m3.find_sync_regions()),
 
 
297
    def test_merge_poem(self):
 
 
298
        """Test case from diff3 manual"""
 
 
299
        m3 = Merge3(TZU, LAO, TAO)
 
 
300
        ml = list(m3.merge_lines('LAO', 'TAO'))
 
 
301
        self.log('merge result:')
 
 
302
        self.log(''.join(ml))
 
 
303
        self.assertEquals(ml, MERGED_RESULT)
 
 
305
    def test_minimal_conflicts(self):
 
 
307
        base_text = ("a\n" * 20).splitlines(True)
 
 
308
        this_text = ("a\n"*10+"b\n" * 10).splitlines(True)
 
 
309
        other_text = ("a\n"*10+"c\n"+"b\n" * 8 + "c\n").splitlines(True)
 
 
310
        m3 = Merge3(base_text, other_text, this_text)
 
 
311
        m_lines = m3.merge_lines('OTHER', 'THIS', reprocess=True)
 
 
312
        merged_text = "".join(list(m_lines))
 
 
313
        optimal_text = "a\n" * 10 + "<<<<<<< OTHER\nc\n=======\n>>>>>>> THIS"\
 
 
314
            + "\n" + 8* "b\n" + "<<<<<<< OTHER\nc\n=======\nb\nb\n>>>>>>>"\
 
 
316
        self.assertEqualDiff(merged_text, optimal_text)
 
 
318
    def test_reprocess_and_base(self):
 
 
319
        """Reprocessing and showing base breaks correctly"""
 
 
320
        base_text = ("a\n" * 20).splitlines(True)
 
 
321
        this_text = ("a\n"*10+"b\n" * 10).splitlines(True)
 
 
322
        other_text = ("a\n"*10+"c\n"+"b\n" * 8 + "c\n").splitlines(True)
 
 
323
        m3 = Merge3(base_text, other_text, this_text)
 
 
324
        m_lines = m3.merge_lines('OTHER', 'THIS', reprocess=True, 
 
 
325
                                 base_marker='|||||||')
 
 
326
        self.assertRaises(CantReprocessAndShowBase, list, m_lines)