/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
0.39.1 by Andrew Bennetts
Prototype of fancier logic, and start of some tests. Simple case of bug 723968 passes, harder case does not. Probably regresses some old behaviour.
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
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
17
from breezy import (
5851.1.1 by Andrew Bennetts
Fix 'not_applicable' return in ChangeLogMerger.merge_text. Also add more tests and fix another bug they reveal.
18
    merge,
0.39.1 by Andrew Bennetts
Prototype of fancier logic, and start of some tests. Simple case of bug 723968 passes, harder case does not. Probably regresses some old behaviour.
19
    tests,
20
    )
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
21
from breezy.tests import test_merge_core
22
from breezy.plugins.changelog_merge import changelog_merge
0.39.1 by Andrew Bennetts
Prototype of fancier logic, and start of some tests. Simple case of bug 723968 passes, harder case does not. Probably regresses some old behaviour.
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',
0.39.3 by Andrew Bennetts
Tighten the fuzzy matching cutoff to 80%.
66
    'Base entry B1 edit',  # > 80% similar according to difflib
0.39.1 by Andrew Bennetts
Prototype of fancier logic, and start of some tests. Simple case of bug 723968 passes, harder case does not. Probably regresses some old behaviour.
67
    'Base entry B2',
68
    ]
69
70
71
class TestMergeCoreLogic(tests.TestCase):
72
0.39.2 by Andrew Bennetts
Implement fancy logic to try satisfy the old and new use cases, including another test.
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
0.39.1 by Andrew Bennetts
Prototype of fancier logic, and start of some tests. Simple case of bug 723968 passes, harder case does not. Probably regresses some old behaviour.
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
        """
0.39.2 by Andrew Bennetts
Implement fancy logic to try satisfy the old and new use cases, including another test.
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)
0.39.1 by Andrew Bennetts
Prototype of fancier logic, and start of some tests. Simple case of bug 723968 passes, harder case does not. Probably regresses some old behaviour.
131
        result_entries = changelog_merge.merge_entries(
0.39.2 by Andrew Bennetts
Implement fancy logic to try satisfy the old and new use cases, including another test.
132
            sample2_base_entries, sample2_this_entries, sample2_other_entries,
133
            guess_edits=guess_edits)
0.39.1 by Andrew Bennetts
Prototype of fancier logic, and start of some tests. Simple case of bug 723968 passes, harder case does not. Probably regresses some old behaviour.
134
        self.assertEqual([
135
            'Other entry O1',
136
            'This entry T1',
137
            'This entry T2',
0.39.2 by Andrew Bennetts
Implement fancy logic to try satisfy the old and new use cases, including another test.
138
            'Base entry B1 edit',
0.39.1 by Andrew Bennetts
Prototype of fancier logic, and start of some tests. Simple case of bug 723968 passes, harder case does not. Probably regresses some old behaviour.
139
            'Base entry B2',
140
            ],
141
            list(result_entries))
142
5851.1.1 by Andrew Bennetts
Fix 'not_applicable' return in ChangeLogMerger.merge_text. Also add more tests and fix another bug they reveal.
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'])
6499.2.1 by Vincent Ladeuil
Save branch config options only during the final unlock
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(
5851.1.1 by Andrew Bennetts
Fix 'not_applicable' return in ChangeLogMerger.merge_text. Also add more tests and fix another bug they reveal.
193
            'changelog_merge_files', 'ChangeLog')
6388.1.8 by Jelmer Vernooij
Fix use of old MergeHookParams name.
194
        merge_hook_params = merge.MergeFileHookParams(merger, 'clog-id', None,
5851.1.1 by Andrew Bennetts
Fix 'not_applicable' return in ChangeLogMerger.merge_text. Also add more tests and fix another bug they reveal.
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