~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/plugins/changelog_merge/tests/test_changelog_merge.py

  • Committer: Patch Queue Manager
  • Date: 2016-02-01 19:56:05 UTC
  • mfrom: (6615.1.1 trunk)
  • Revision ID: pqm@pqm.ubuntu.com-20160201195605-o7rl92wf6uyum3fk
(vila) Open trunk again as 2.8b1 (Vincent Ladeuil)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2011 by Canonical Ltd
 
2
#
 
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.
 
7
#
 
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.
 
12
#
 
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
16
 
 
17
from bzrlib import (
 
18
    merge,
 
19
    tests,
 
20
    )
 
21
from bzrlib.tests import test_merge_core
 
22
from bzrlib.plugins.changelog_merge import changelog_merge
 
23
 
 
24
 
 
25
sample_base_entries = [
 
26
    'Base entry B1',
 
27
    'Base entry B2',
 
28
    'Base entry B3',
 
29
    ]
 
30
 
 
31
sample_this_entries = [
 
32
    'This entry T1',
 
33
    'This entry T2',
 
34
    #'Base entry B1 updated',
 
35
    'Base entry B1',
 
36
    'Base entry B2',
 
37
    'Base entry B3',
 
38
    ]
 
39
 
 
40
sample_other_entries = [
 
41
    'Other entry O1',
 
42
    #'Base entry B1',
 
43
    'Base entry B1',
 
44
    'Base entry B2 updated',
 
45
    'Base entry B3',
 
46
    ]
 
47
 
 
48
 
 
49
sample2_base_entries = [
 
50
    'Base entry B1',
 
51
    'Base entry B2',
 
52
    'Base entry B3',
 
53
    ]
 
54
 
 
55
sample2_this_entries = [
 
56
    'This entry T1',
 
57
    'This entry T2',
 
58
    #'Base entry B1 updated',
 
59
    'Base entry B1',
 
60
    'Base entry B2',
 
61
    ]
 
62
 
 
63
sample2_other_entries = [
 
64
    'Other entry O1',
 
65
    #'Base entry B1',
 
66
    'Base entry B1 edit',  # > 80% similar according to difflib
 
67
    'Base entry B2',
 
68
    ]
 
69
 
 
70
 
 
71
class TestMergeCoreLogic(tests.TestCase):
 
72
 
 
73
    def test_new_in_other_floats_to_top(self):
 
74
        """Changes at the top of 'other' float to the top.
 
75
 
 
76
        Given a changelog in THIS containing::
 
77
 
 
78
          NEW-1
 
79
          OLD-1
 
80
 
 
81
        and a changelog in OTHER containing::
 
82
 
 
83
          NEW-2
 
84
          OLD-1
 
85
 
 
86
        it will merge as::
 
87
 
 
88
          NEW-2
 
89
          NEW-1
 
90
          OLD-1
 
91
        """
 
92
        base_entries = ['OLD-1']
 
93
        this_entries = ['NEW-1', 'OLD-1']
 
94
        other_entries = ['NEW-2', 'OLD-1']
 
95
        result_entries = changelog_merge.merge_entries(
 
96
            base_entries, this_entries, other_entries)
 
97
        self.assertEqual(
 
98
            ['NEW-2', 'NEW-1', 'OLD-1'], result_entries)
 
99
 
 
100
    def test_acceptance_bug_723968(self):
 
101
        """Merging a branch that:
 
102
 
 
103
         1. adds a new entry, and
 
104
         2. edits an old entry (e.g. to fix a typo or twiddle formatting)
 
105
 
 
106
        will:
 
107
 
 
108
         1. add the new entry to the top
 
109
         2. keep the edit, without duplicating the edited entry or moving it.
 
110
        """
 
111
        result_entries = changelog_merge.merge_entries(
 
112
            sample_base_entries, sample_this_entries, sample_other_entries)
 
113
        self.assertEqual([
 
114
            'Other entry O1',
 
115
            'This entry T1',
 
116
            'This entry T2',
 
117
            'Base entry B1',
 
118
            'Base entry B2 updated',
 
119
            'Base entry B3',
 
120
            ],
 
121
            list(result_entries))
 
122
 
 
123
    def test_more_complex_conflict(self):
 
124
        """Like test_acceptance_bug_723968, but with a more difficult conflict:
 
125
        the new entry and the edited entry are adjacent.
 
126
        """
 
127
        def guess_edits(new, deleted):
 
128
            #import pdb; pdb.set_trace()
 
129
            return changelog_merge.default_guess_edits(new, deleted,
 
130
                    entry_as_str=lambda x: x)
 
131
        result_entries = changelog_merge.merge_entries(
 
132
            sample2_base_entries, sample2_this_entries, sample2_other_entries,
 
133
            guess_edits=guess_edits)
 
134
        self.assertEqual([
 
135
            'Other entry O1',
 
136
            'This entry T1',
 
137
            'This entry T2',
 
138
            'Base entry B1 edit',
 
139
            'Base entry B2',
 
140
            ],
 
141
            list(result_entries))
 
142
 
 
143
    def test_too_hard(self):
 
144
        """A conflict this plugin cannot resolve raises EntryConflict.
 
145
        """
 
146
        # An entry edited in other but deleted in this is a conflict we can't
 
147
        # resolve.  (Ideally perhaps we'd generate a nice conflict file, but
 
148
        # for now we just give up.)
 
149
        self.assertRaises(changelog_merge.EntryConflict,
 
150
            changelog_merge.merge_entries,
 
151
            sample2_base_entries, [], sample2_other_entries)
 
152
 
 
153
    def test_default_guess_edits(self):
 
154
        """default_guess_edits matches a new entry only once.
 
155
        
 
156
        (Even when that entry is the best match for multiple old entries.)
 
157
        """
 
158
        new_in_other = [('AAAAA',), ('BBBBB',)]
 
159
        deleted_in_other = [('DDDDD',), ('BBBBBx',), ('BBBBBxx',)]
 
160
        # BBBBB is the best match for both BBBBBx and BBBBBxx
 
161
        result = changelog_merge.default_guess_edits(
 
162
            new_in_other, deleted_in_other)
 
163
        self.assertEqual(
 
164
            ([('AAAAA',)], # new
 
165
             [('DDDDD',), ('BBBBBxx',)], # deleted
 
166
             [(('BBBBBx',), ('BBBBB',))]), # edits
 
167
            result)
 
168
 
 
169
 
 
170
class TestChangeLogMerger(tests.TestCaseWithTransport):
 
171
    """Tests for ChangeLogMerger class.
 
172
    
 
173
    Most tests should be unit tests for merge_entries (and its helpers).
 
174
    This class is just to cover the handful of lines of code in ChangeLogMerger
 
175
    itself.
 
176
    """
 
177
 
 
178
    def make_builder(self):
 
179
        builder = test_merge_core.MergeBuilder(self.test_base_dir)
 
180
        self.addCleanup(builder.cleanup)
 
181
        return builder
 
182
 
 
183
    def make_changelog_merger(self, base_text, this_text, other_text):
 
184
        builder = self.make_builder()
 
185
        builder.add_file('clog-id', builder.tree_root, 'ChangeLog',
 
186
            base_text, True)
 
187
        builder.change_contents('clog-id', other=other_text, this=this_text)
 
188
        merger = builder.make_merger(merge.Merge3Merger, ['clog-id'])
 
189
        # The following can't use config stacks until the plugin itself does
 
190
        # ('this_branch' is already write locked at this point and as such
 
191
        # won't write the new value to disk where get_user_option can get it).
 
192
        merger.this_branch.get_config().set_user_option(
 
193
            'changelog_merge_files', 'ChangeLog')
 
194
        merge_hook_params = merge.MergeFileHookParams(merger, 'clog-id', None,
 
195
            'file', 'file', 'conflict')
 
196
        changelog_merger = changelog_merge.ChangeLogMerger(merger)
 
197
        return changelog_merger, merge_hook_params
 
198
 
 
199
    def test_merge_text_returns_not_applicable(self):
 
200
        """A conflict this plugin cannot resolve returns (not_applicable, None).
 
201
        """
 
202
        # Build same example as TestMergeCoreLogic.test_too_hard: edit an entry
 
203
        # in other but delete it in this.
 
204
        def entries_as_str(entries):
 
205
            return ''.join(entry + '\n' for entry in entries)
 
206
        changelog_merger, merge_hook_params = self.make_changelog_merger(
 
207
            entries_as_str(sample2_base_entries),
 
208
            '',
 
209
            entries_as_str(sample2_other_entries))
 
210
        self.assertEqual(
 
211
            ('not_applicable', None),
 
212
            changelog_merger.merge_contents(merge_hook_params))
 
213
 
 
214
    def test_merge_text_returns_success(self):
 
215
        """A successful merge returns ('success', lines)."""
 
216
        changelog_merger, merge_hook_params = self.make_changelog_merger(
 
217
            '', 'this text\n', 'other text\n')
 
218
        status, lines = changelog_merger.merge_contents(merge_hook_params)
 
219
        self.assertEqual(
 
220
            ('success', ['other text\n', 'this text\n']),
 
221
            (status, list(lines)))
 
222