1
# Copyright (C) 2011 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21
from bzrlib.tests import test_merge_core
22
from bzrlib.plugins.changelog_merge import changelog_merge
25
sample_base_entries = [
31
sample_this_entries = [
34
#'Base entry B1 updated',
40
sample_other_entries = [
44
'Base entry B2 updated',
49
sample2_base_entries = [
55
sample2_this_entries = [
58
#'Base entry B1 updated',
63
sample2_other_entries = [
66
'Base entry B1 edit', # > 80% similar according to difflib
71
class TestMergeCoreLogic(tests.TestCase):
73
def test_new_in_other_floats_to_top(self):
74
"""Changes at the top of 'other' float to the top.
76
Given a changelog in THIS containing::
81
and a changelog in OTHER containing::
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)
98
['NEW-2', 'NEW-1', 'OLD-1'], result_entries)
100
def test_acceptance_bug_723968(self):
101
"""Merging a branch that:
103
1. adds a new entry, and
104
2. edits an old entry (e.g. to fix a typo or twiddle formatting)
108
1. add the new entry to the top
109
2. keep the edit, without duplicating the edited entry or moving it.
111
result_entries = changelog_merge.merge_entries(
112
sample_base_entries, sample_this_entries, sample_other_entries)
118
'Base entry B2 updated',
121
list(result_entries))
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.
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)
138
'Base entry B1 edit',
141
list(result_entries))
143
def test_too_hard(self):
144
"""A conflict this plugin cannot resolve raises EntryConflict.
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)
153
def test_default_guess_edits(self):
154
"""default_guess_edits matches a new entry only once.
156
(Even when that entry is the best match for multiple old entries.)
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)
165
[('DDDDD',), ('BBBBBxx',)], # deleted
166
[(('BBBBBx',), ('BBBBB',))]), # edits
170
class TestChangeLogMerger(tests.TestCaseWithTransport):
171
"""Tests for ChangeLogMerger class.
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
178
def make_builder(self):
179
builder = test_merge_core.MergeBuilder(self.test_base_dir)
180
self.addCleanup(builder.cleanup)
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',
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
199
def test_merge_text_returns_not_applicable(self):
200
"""A conflict this plugin cannot resolve returns (not_applicable, None).
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),
209
entries_as_str(sample2_other_entries))
211
('not_applicable', None),
212
changelog_merger.merge_contents(merge_hook_params))
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)
220
('success', ['other text\n', 'this text\n']),
221
(status, list(lines)))