~bzr-pqm/bzr/bzr.dev

1551.12.36 by Aaron Bentley
Fix failing tests
1
# Copyright (C) 2007 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
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
1551.12.36 by Aaron Bentley
Fix failing tests
16
2520.7.2 by Aaron Bentley
Restore patch verification for CR, CRLF files
17
import re
1551.12.36 by Aaron Bentley
Fix failing tests
18
1551.12.2 by Aaron Bentley
Got directives round-tripping, with bundles and everything
19
from bzrlib import (
20
    errors,
1551.12.16 by Aaron Bentley
Enable signing merge directives
21
    gpg,
4098.5.16 by Aaron Bentley
Move hook to MergeDirective, implement MergeDirective.compose_merge_request.
22
    mail_client,
1551.12.2 by Aaron Bentley
Got directives round-tripping, with bundles and everything
23
    merge_directive,
24
    tests,
4098.5.18 by Aaron Bentley
Gracefully handle mail clients that don't support bodies.
25
    trace,
1551.12.2 by Aaron Bentley
Got directives round-tripping, with bundles and everything
26
    )
27
1551.12.4 by Aaron Bentley
Add failing test
28
1551.12.45 by Aaron Bentley
Change format marker to not experimental
29
OUTPUT1 = """# Bazaar merge directive format 1
1551.12.41 by Aaron Bentley
Clean up tests, add serialization text test
30
# revision_id: example:
31
# target_branch: http://example.com
32
# testament_sha1: sha
33
# timestamp: 1970-01-01 00:09:33 +0002
34
#\x20
35
booga"""
36
2687.2.2 by Martin Pool
Fix up other references to 0.19
37
OUTPUT1_2 = """# Bazaar merge directive format 2 (Bazaar 0.90)
2520.4.73 by Aaron Bentley
Implement new merge directive format
38
# revision_id: example:
39
# target_branch: http://example.com
40
# testament_sha1: sha
41
# timestamp: 1970-01-01 00:09:33 +0002
2520.4.105 by Aaron Bentley
Implement patch verification
42
# base_revision_id: null:
2520.4.73 by Aaron Bentley
Implement new merge directive format
43
#\x20
44
# Begin bundle
45
booga"""
1551.12.41 by Aaron Bentley
Clean up tests, add serialization text test
46
1551.12.45 by Aaron Bentley
Change format marker to not experimental
47
OUTPUT2 = """# Bazaar merge directive format 1
1551.12.41 by Aaron Bentley
Clean up tests, add serialization text test
48
# revision_id: example:
49
# target_branch: http://example.com
50
# testament_sha1: sha
51
# timestamp: 1970-01-01 00:09:33 +0002
52
# source_branch: http://example.org
53
# message: Hi mom!
54
#\x20
55
booga"""
56
2687.2.2 by Martin Pool
Fix up other references to 0.19
57
OUTPUT2_2 = """# Bazaar merge directive format 2 (Bazaar 0.90)
2520.4.73 by Aaron Bentley
Implement new merge directive format
58
# revision_id: example:
59
# target_branch: http://example.com
60
# testament_sha1: sha
61
# timestamp: 1970-01-01 00:09:33 +0002
62
# source_branch: http://example.org
63
# message: Hi mom!
2520.4.105 by Aaron Bentley
Implement patch verification
64
# base_revision_id: null:
2520.4.73 by Aaron Bentley
Implement new merge directive format
65
#\x20
66
# Begin patch
67
booga"""
1551.12.41 by Aaron Bentley
Clean up tests, add serialization text test
68
1551.12.51 by Aaron Bentley
Allow leading junk before merge directive header
69
INPUT1 = """
70
I was thinking today about creating a merge directive.
71
72
So I did.
73
74
Here it is.
75
76
(I've pasted it in the body of this message)
77
78
Aaron
79
80
# Bazaar merge directive format 1\r
81
# revision_id: example:
82
# target_branch: http://example.com
83
# testament_sha1: sha
84
# timestamp: 1970-01-01 00:09:33 +0002
85
# source_branch: http://example.org
86
# message: Hi mom!
87
#\x20
88
booga""".splitlines(True)
89
90
2520.4.73 by Aaron Bentley
Implement new merge directive format
91
INPUT1_2 = """
92
I was thinking today about creating a merge directive.
93
94
So I did.
95
96
Here it is.
97
98
(I've pasted it in the body of this message)
99
100
Aaron
101
2687.2.2 by Martin Pool
Fix up other references to 0.19
102
# Bazaar merge directive format 2 (Bazaar 0.90)\r
2520.4.73 by Aaron Bentley
Implement new merge directive format
103
# revision_id: example:
104
# target_branch: http://example.com
105
# testament_sha1: sha
106
# timestamp: 1970-01-01 00:09:33 +0002
107
# source_branch: http://example.org
2520.4.105 by Aaron Bentley
Implement patch verification
108
# base_revision_id: null:
2520.4.73 by Aaron Bentley
Implement new merge directive format
109
# message: Hi mom!
110
#\x20
111
# Begin patch
112
booga""".splitlines(True)
113
114
2694.1.1 by Aaron Bentley
Restore support for Merge directive 2 / 0.19
115
INPUT1_2_OLD = """
116
I was thinking today about creating a merge directive.
117
118
So I did.
119
120
Here it is.
121
122
(I've pasted it in the body of this message)
123
124
Aaron
125
126
# Bazaar merge directive format 2 (Bazaar 0.19)\r
127
# revision_id: example:
128
# target_branch: http://example.com
129
# testament_sha1: sha
130
# timestamp: 1970-01-01 00:09:33 +0002
131
# source_branch: http://example.org
132
# base_revision_id: null:
133
# message: Hi mom!
134
#\x20
135
# Begin patch
136
booga""".splitlines(True)
137
2694.1.2 by Aaron Bentley
Fix whitespace
138
2694.1.1 by Aaron Bentley
Restore support for Merge directive 2 / 0.19
139
OLD_DIRECTIVE_2 = """# Bazaar merge directive format 2 (Bazaar 0.19)
140
# revision_id: abentley@panoramicfeedback.com-20070807234458-\
141
#   nzhkoyza56lan7z5
142
# target_branch: http://panoramicfeedback.com/opensource/bzr/repo\
143
#   /bzr.ab
144
# testament_sha1: d825a5cdb267a90ec2ba86b00895f3d8a9bed6bf
145
# timestamp: 2007-08-10 16:15:02 -0400
146
# source_branch: http://panoramicfeedback.com/opensource/bzr/repo\
147
#   /bzr.ab
148
# base_revision_id: abentley@panoramicfeedback.com-20070731163346-\
149
#   623xwcycwij91xen
150
#
151
""".splitlines(True)
152
153
2520.4.73 by Aaron Bentley
Implement new merge directive format
154
class TestMergeDirective(object):
1551.12.2 by Aaron Bentley
Got directives round-tripping, with bundles and everything
155
1551.12.41 by Aaron Bentley
Clean up tests, add serialization text test
156
    def test_merge_source(self):
2425.6.1 by Martin Pool
Fix formatting of timezones in bundles and merge directives.
157
        time = 500000.0
158
        timezone = 5 * 3600
2520.4.73 by Aaron Bentley
Implement new merge directive format
159
        self.assertRaises(errors.NoMergeSource, self.make_merge_directive,
1551.12.3 by Aaron Bentley
Add timestamps to merge directives
160
            'example:', 'sha', time, timezone, 'http://example.com')
2520.4.73 by Aaron Bentley
Implement new merge directive format
161
        self.assertRaises(errors.NoMergeSource, self.make_merge_directive,
1551.12.3 by Aaron Bentley
Add timestamps to merge directives
162
            'example:', 'sha', time, timezone, 'http://example.com',
163
            patch_type='diff')
2520.4.73 by Aaron Bentley
Implement new merge directive format
164
        self.make_merge_directive('example:', 'sha', time, timezone,
1551.12.13 by Aaron Bentley
Rename fields
165
            'http://example.com', source_branch='http://example.org')
2520.4.73 by Aaron Bentley
Implement new merge directive format
166
        md = self.make_merge_directive('null:', 'sha', time, timezone,
1551.12.2 by Aaron Bentley
Got directives round-tripping, with bundles and everything
167
            'http://example.com', patch='blah', patch_type='bundle')
1551.12.41 by Aaron Bentley
Clean up tests, add serialization text test
168
        self.assertIs(None, md.source_branch)
2520.4.73 by Aaron Bentley
Implement new merge directive format
169
        md2 = self.make_merge_directive('null:', 'sha', time, timezone,
1551.12.41 by Aaron Bentley
Clean up tests, add serialization text test
170
            'http://example.com', patch='blah', patch_type='bundle',
171
            source_branch='bar')
172
        self.assertEqual('bar', md2.source_branch)
173
1551.12.2 by Aaron Bentley
Got directives round-tripping, with bundles and everything
174
    def test_serialization(self):
2425.6.1 by Martin Pool
Fix formatting of timezones in bundles and merge directives.
175
        time = 453
176
        timezone = 120
2520.4.73 by Aaron Bentley
Implement new merge directive format
177
        md = self.make_merge_directive('example:', 'sha', time, timezone,
1551.12.41 by Aaron Bentley
Clean up tests, add serialization text test
178
            'http://example.com', patch='booga', patch_type='bundle')
2520.4.73 by Aaron Bentley
Implement new merge directive format
179
        self.assertEqualDiff(self.OUTPUT1, ''.join(md.to_lines()))
180
        md = self.make_merge_directive('example:', 'sha', time, timezone,
1551.12.41 by Aaron Bentley
Clean up tests, add serialization text test
181
            'http://example.com', source_branch="http://example.org",
182
            patch='booga', patch_type='diff', message="Hi mom!")
2520.4.73 by Aaron Bentley
Implement new merge directive format
183
        self.assertEqualDiff(self.OUTPUT2, ''.join(md.to_lines()))
1551.12.41 by Aaron Bentley
Clean up tests, add serialization text test
184
1551.12.49 by Aaron Bentley
Proper error when deserializing junk
185
    def test_deserialize_junk(self):
2425.6.1 by Martin Pool
Fix formatting of timezones in bundles and merge directives.
186
        time = 501
1551.12.49 by Aaron Bentley
Proper error when deserializing junk
187
        self.assertRaises(errors.NotAMergeDirective,
188
                          merge_directive.MergeDirective.from_lines, 'lala')
189
1551.12.59 by Aaron Bentley
Correctly handle empty merge directive texts
190
    def test_deserialize_empty(self):
191
        self.assertRaises(errors.NotAMergeDirective,
192
                          merge_directive.MergeDirective.from_lines, [])
193
1551.12.51 by Aaron Bentley
Allow leading junk before merge directive header
194
    def test_deserialize_leading_junk(self):
2520.4.73 by Aaron Bentley
Implement new merge directive format
195
        md = merge_directive.MergeDirective.from_lines(self.INPUT1)
1551.12.51 by Aaron Bentley
Allow leading junk before merge directive header
196
        self.assertEqual('example:', md.revision_id)
197
        self.assertEqual('sha', md.testament_sha1)
198
        self.assertEqual('http://example.com', md.target_branch)
199
        self.assertEqual('http://example.org', md.source_branch)
2425.6.1 by Martin Pool
Fix formatting of timezones in bundles and merge directives.
200
        self.assertEqual(453, md.time)
201
        self.assertEqual(120, md.timezone)
1551.12.51 by Aaron Bentley
Allow leading junk before merge directive header
202
        self.assertEqual('booga', md.patch)
203
        self.assertEqual('diff', md.patch_type)
204
        self.assertEqual('Hi mom!', md.message)
1551.12.49 by Aaron Bentley
Proper error when deserializing junk
205
1551.12.41 by Aaron Bentley
Clean up tests, add serialization text test
206
    def test_roundtrip(self):
2425.6.1 by Martin Pool
Fix formatting of timezones in bundles and merge directives.
207
        time = 500000
208
        timezone = 7.5 * 3600
2520.4.73 by Aaron Bentley
Implement new merge directive format
209
        md = self.make_merge_directive('example:', 'sha', time, timezone,
1551.12.13 by Aaron Bentley
Rename fields
210
            'http://example.com', source_branch="http://example.org",
1551.12.2 by Aaron Bentley
Got directives round-tripping, with bundles and everything
211
            patch='booga', patch_type='diff')
212
        md2 = merge_directive.MergeDirective.from_lines(md.to_lines())
1551.12.5 by Aaron Bentley
Get MergeDirective.from_objects working
213
        self.assertEqual('example:', md2.revision_id)
1551.12.54 by Aaron Bentley
Decoded revision ids are utf-8
214
        self.assertIsInstance(md2.revision_id, str)
1551.12.2 by Aaron Bentley
Got directives round-tripping, with bundles and everything
215
        self.assertEqual('sha', md2.testament_sha1)
1551.12.13 by Aaron Bentley
Rename fields
216
        self.assertEqual('http://example.com', md2.target_branch)
217
        self.assertEqual('http://example.org', md2.source_branch)
1551.12.3 by Aaron Bentley
Add timestamps to merge directives
218
        self.assertEqual(time, md2.time)
219
        self.assertEqual(timezone, md2.timezone)
1551.12.2 by Aaron Bentley
Got directives round-tripping, with bundles and everything
220
        self.assertEqual('diff', md2.patch_type)
221
        self.assertEqual('booga', md2.patch)
1551.12.26 by Aaron Bentley
Get email working, with optional message
222
        self.assertEqual(None, md2.message)
2520.4.73 by Aaron Bentley
Implement new merge directive format
223
        self.set_bundle(md, "# Bazaar revision bundle v0.9\n#\n")
1551.12.26 by Aaron Bentley
Get email working, with optional message
224
        md.message = "Hi mom!"
2520.4.73 by Aaron Bentley
Implement new merge directive format
225
        lines = md.to_lines()
226
        md3 = merge_directive.MergeDirective.from_lines(lines)
227
        self.assertEqual("# Bazaar revision bundle v0.9\n#\n", md3.bundle)
1551.12.2 by Aaron Bentley
Got directives round-tripping, with bundles and everything
228
        self.assertEqual("bundle", md3.patch_type)
1551.12.12 by Aaron Bentley
Add format header
229
        self.assertContainsRe(md3.to_lines()[0],
230
            '^# Bazaar merge directive format ')
1551.12.26 by Aaron Bentley
Get email working, with optional message
231
        self.assertEqual("Hi mom!", md3.message)
2520.4.73 by Aaron Bentley
Implement new merge directive format
232
        md3.clear_payload()
2520.4.80 by Aaron Bentley
Improve merge directive tests
233
        self.assertIs(None, md3.get_raw_bundle())
1551.12.53 by Aaron Bentley
Fix deserialization of merge directives with no patch
234
        md4 = merge_directive.MergeDirective.from_lines(md3.to_lines())
235
        self.assertIs(None, md4.patch_type)
1551.12.26 by Aaron Bentley
Get email working, with optional message
236
237
2520.4.73 by Aaron Bentley
Implement new merge directive format
238
class TestMergeDirective1(tests.TestCase, TestMergeDirective):
239
    """Test merge directive format 1"""
240
241
    INPUT1 = INPUT1
242
243
    OUTPUT1 = OUTPUT1
244
245
    OUTPUT2 = OUTPUT2
246
247
    def make_merge_directive(self, revision_id, testament_sha1, time, timezone,
248
                 target_branch, patch=None, patch_type=None,
249
                 source_branch=None, message=None):
250
        return merge_directive.MergeDirective(revision_id, testament_sha1,
251
                 time, timezone, target_branch, patch, patch_type,
252
                 source_branch, message)
253
254
    @staticmethod
255
    def set_bundle(md, value):
256
        md.patch = value
257
258
    def test_require_patch(self):
259
        time = 500.0
260
        timezone = 120
261
        self.assertRaises(errors.PatchMissing, merge_directive.MergeDirective,
262
            'example:', 'sha', time, timezone, 'http://example.com',
263
            patch_type='bundle')
264
        md = merge_directive.MergeDirective('example:', 'sha1', time, timezone,
265
            'http://example.com', source_branch="http://example.org",
266
            patch='', patch_type='diff')
267
        self.assertEqual(md.patch, '')
268
269
270
class TestMergeDirective2(tests.TestCase, TestMergeDirective):
271
    """Test merge directive format 2"""
272
273
    INPUT1 = INPUT1_2
274
275
    OUTPUT1 = OUTPUT1_2
276
277
    OUTPUT2 = OUTPUT2_2
278
279
    def make_merge_directive(self, revision_id, testament_sha1, time, timezone,
280
                 target_branch, patch=None, patch_type=None,
2520.4.105 by Aaron Bentley
Implement patch verification
281
                 source_branch=None, message=None, base_revision_id='null:'):
2520.4.73 by Aaron Bentley
Implement new merge directive format
282
        if patch_type == 'bundle':
283
            bundle = patch
284
            patch = None
285
        else:
286
            bundle = None
287
        return merge_directive.MergeDirective2(revision_id, testament_sha1,
288
            time, timezone, target_branch, patch, source_branch, message,
2520.4.105 by Aaron Bentley
Implement patch verification
289
            bundle, base_revision_id)
2520.4.73 by Aaron Bentley
Implement new merge directive format
290
291
    @staticmethod
292
    def set_bundle(md, value):
293
        md.bundle = value
294
295
2625.6.1 by Adeodato Simó
New EmailMessage class, façade around email.Message and MIMEMultipart.
296
EMAIL1 = """From: "J. Random Hacker" <jrandom@example.com>
1551.12.26 by Aaron Bentley
Get email working, with optional message
297
Subject: Commit of rev2a
2625.6.1 by Adeodato Simó
New EmailMessage class, façade around email.Message and MIMEMultipart.
298
To: pqm@example.com
299
User-Agent: Bazaar \(.*\)
1551.12.26 by Aaron Bentley
Get email working, with optional message
300
1551.12.45 by Aaron Bentley
Change format marker to not experimental
301
# Bazaar merge directive format 1
1551.12.26 by Aaron Bentley
Get email working, with optional message
302
# revision_id: rev2a
303
# target_branch: (.|\n)*
304
# testament_sha1: .*
1551.12.30 by Aaron Bentley
Use patch-style dates for timestamps in merge directives
305
# timestamp: 1970-01-01 00:08:56 \\+0001
1551.12.26 by Aaron Bentley
Get email working, with optional message
306
# source_branch: (.|\n)*
307
"""
308
309
2625.6.2 by Adeodato Simó
Merge bzr.dev, resolving conflicts and updating test_merge_directive.py.
310
EMAIL1_2 = """From: "J. Random Hacker" <jrandom@example.com>
2520.4.80 by Aaron Bentley
Improve merge directive tests
311
Subject: Commit of rev2a
2625.6.2 by Adeodato Simó
Merge bzr.dev, resolving conflicts and updating test_merge_directive.py.
312
To: pqm@example.com
313
User-Agent: Bazaar \(.*\)
2520.4.80 by Aaron Bentley
Improve merge directive tests
314
2687.2.2 by Martin Pool
Fix up other references to 0.19
315
# Bazaar merge directive format 2 \\(Bazaar 0.90\\)
2520.4.80 by Aaron Bentley
Improve merge directive tests
316
# revision_id: rev2a
317
# target_branch: (.|\n)*
318
# testament_sha1: .*
319
# timestamp: 1970-01-01 00:08:56 \\+0001
320
# source_branch: (.|\n)*
321
"""
322
323
2625.6.1 by Adeodato Simó
New EmailMessage class, façade around email.Message and MIMEMultipart.
324
EMAIL2 = """From: "J. Random Hacker" <jrandom@example.com>
1551.12.26 by Aaron Bentley
Get email working, with optional message
325
Subject: Commit of rev2a with special message
2625.6.1 by Adeodato Simó
New EmailMessage class, façade around email.Message and MIMEMultipart.
326
To: pqm@example.com
327
User-Agent: Bazaar \(.*\)
1551.12.26 by Aaron Bentley
Get email working, with optional message
328
1551.12.45 by Aaron Bentley
Change format marker to not experimental
329
# Bazaar merge directive format 1
1551.12.26 by Aaron Bentley
Get email working, with optional message
330
# revision_id: rev2a
331
# target_branch: (.|\n)*
332
# testament_sha1: .*
1551.12.30 by Aaron Bentley
Use patch-style dates for timestamps in merge directives
333
# timestamp: 1970-01-01 00:08:56 \\+0001
1551.12.26 by Aaron Bentley
Get email working, with optional message
334
# source_branch: (.|\n)*
335
# message: Commit of rev2a with special message
336
"""
1551.12.4 by Aaron Bentley
Add failing test
337
2625.6.2 by Adeodato Simó
Merge bzr.dev, resolving conflicts and updating test_merge_directive.py.
338
EMAIL2_2 = """From: "J. Random Hacker" <jrandom@example.com>
2520.4.80 by Aaron Bentley
Improve merge directive tests
339
Subject: Commit of rev2a with special message
2625.6.2 by Adeodato Simó
Merge bzr.dev, resolving conflicts and updating test_merge_directive.py.
340
To: pqm@example.com
341
User-Agent: Bazaar \(.*\)
2520.4.80 by Aaron Bentley
Improve merge directive tests
342
2687.2.2 by Martin Pool
Fix up other references to 0.19
343
# Bazaar merge directive format 2 \\(Bazaar 0.90\\)
2520.4.80 by Aaron Bentley
Improve merge directive tests
344
# revision_id: rev2a
345
# target_branch: (.|\n)*
346
# testament_sha1: .*
347
# timestamp: 1970-01-01 00:08:56 \\+0001
348
# source_branch: (.|\n)*
349
# message: Commit of rev2a with special message
350
"""
1551.12.4 by Aaron Bentley
Add failing test
351
2520.4.73 by Aaron Bentley
Implement new merge directive format
352
class TestMergeDirectiveBranch(object):
1551.12.4 by Aaron Bentley
Add failing test
353
1551.12.26 by Aaron Bentley
Get email working, with optional message
354
    def make_trees(self):
1551.12.4 by Aaron Bentley
Add failing test
355
        tree_a = self.make_branch_and_tree('tree_a')
1551.12.26 by Aaron Bentley
Get email working, with optional message
356
        tree_a.branch.get_config().set_user_option('email',
357
            'J. Random Hacker <jrandom@example.com>')
2520.7.2 by Aaron Bentley
Restore patch verification for CR, CRLF files
358
        self.build_tree_contents([('tree_a/file', 'content_a\ncontent_b\n'),
359
                                  ('tree_a/file_2', 'content_x\rcontent_y\r')])
360
        tree_a.add(['file', 'file_2'])
1551.12.4 by Aaron Bentley
Add failing test
361
        tree_a.commit('message', rev_id='rev1')
362
        tree_b = tree_a.bzrdir.sprout('tree_b').open_workingtree()
1551.12.5 by Aaron Bentley
Get MergeDirective.from_objects working
363
        branch_c = tree_a.bzrdir.sprout('branch_c').open_branch()
1551.12.4 by Aaron Bentley
Add failing test
364
        tree_b.commit('message', rev_id='rev2b')
2520.7.2 by Aaron Bentley
Restore patch verification for CR, CRLF files
365
        self.build_tree_contents([('tree_a/file', 'content_a\ncontent_c \n'),
366
                                  ('tree_a/file_2', 'content_x\rcontent_z\r')])
1551.12.26 by Aaron Bentley
Get email working, with optional message
367
        tree_a.commit('Commit of rev2a', rev_id='rev2a')
368
        return tree_a, tree_b, branch_c
369
2490.2.28 by Aaron Bentley
Fix handling of null revision
370
    def test_empty_target(self):
371
        tree_a, tree_b, branch_c = self.make_trees()
372
        tree_d = self.make_branch_and_tree('tree_d')
2520.4.73 by Aaron Bentley
Implement new merge directive format
373
        md2 = self.from_objects(tree_a.branch.repository, 'rev2a', 500, 120,
374
            tree_d.branch.base, patch_type='diff',
375
            public_branch=tree_a.branch.base)
2490.2.28 by Aaron Bentley
Fix handling of null revision
376
3251.2.1 by Aaron Bentley
Use nick/revno-based names for merge directives
377
    def test_disk_name(self):
378
        tree_a, tree_b, branch_c = self.make_trees()
3449.4.1 by Lukáš Lalinský
Sanitize branch nick before using it as an attachment filename in ``bzr send``
379
        tree_a.branch.nick = 'fancy <name>'
3251.2.1 by Aaron Bentley
Use nick/revno-based names for merge directives
380
        md = self.from_objects(tree_a.branch.repository, 'rev2a', 500, 120,
381
            tree_b.branch.base)
382
        self.assertEqual('fancy-name-2', md.get_disk_name(tree_a.branch))
383
384
    def test_disk_name_old_revno(self):
385
        tree_a, tree_b, branch_c = self.make_trees()
386
        tree_a.branch.nick = 'fancy-name'
387
        md = self.from_objects(tree_a.branch.repository, 'rev1', 500, 120,
388
            tree_b.branch.base)
389
        self.assertEqual('fancy-name-1', md.get_disk_name(tree_a.branch))
390
1551.12.41 by Aaron Bentley
Clean up tests, add serialization text test
391
    def test_generate_patch(self):
392
        tree_a, tree_b, branch_c = self.make_trees()
2520.4.73 by Aaron Bentley
Implement new merge directive format
393
        md2 = self.from_objects(tree_a.branch.repository, 'rev2a', 500, 120,
394
            tree_b.branch.base, patch_type='diff',
395
            public_branch=tree_a.branch.base)
1551.12.41 by Aaron Bentley
Clean up tests, add serialization text test
396
        self.assertNotContainsRe(md2.patch, 'Bazaar revision bundle')
397
        self.assertContainsRe(md2.patch, '\\+content_c')
398
        self.assertNotContainsRe(md2.patch, '\\+\\+\\+ b/')
399
        self.assertContainsRe(md2.patch, '\\+\\+\\+ file')
400
401
    def test_public_branch(self):
1551.12.26 by Aaron Bentley
Get email working, with optional message
402
        tree_a, tree_b, branch_c = self.make_trees()
1551.12.5 by Aaron Bentley
Get MergeDirective.from_objects working
403
        self.assertRaises(errors.PublicBranchOutOfDate,
2520.4.73 by Aaron Bentley
Implement new merge directive format
404
            self.from_objects, tree_a.branch.repository, 'rev2a', 500, 144,
405
            tree_b.branch.base, public_branch=branch_c.base, patch_type='diff')
2520.5.4 by Aaron Bentley
Replace 'bundle-revisions' with 'submit' command
406
        self.assertRaises(errors.PublicBranchOutOfDate,
407
            self.from_objects, tree_a.branch.repository, 'rev2a', 500, 144,
408
            tree_b.branch.base, public_branch=branch_c.base, patch_type=None)
1551.12.34 by Aaron Bentley
Check public branch only if not using a bundle
409
        # public branch is not checked if patch format is bundle.
2520.4.73 by Aaron Bentley
Implement new merge directive format
410
        md1 = self.from_objects(tree_a.branch.repository, 'rev2a', 500, 144,
411
            tree_b.branch.base, public_branch=branch_c.base)
1551.12.34 by Aaron Bentley
Check public branch only if not using a bundle
412
        # public branch is provided with a bundle, despite possibly being out
413
        # of date, because it's not required if a bundle is present.
414
        self.assertEqual(md1.source_branch, branch_c.base)
1551.12.41 by Aaron Bentley
Clean up tests, add serialization text test
415
        # Once we update the public branch, we can generate a diff.
1551.12.5 by Aaron Bentley
Get MergeDirective.from_objects working
416
        branch_c.pull(tree_a.branch)
2520.4.73 by Aaron Bentley
Implement new merge directive format
417
        md3 = self.from_objects(tree_a.branch.repository, 'rev2a', 500, 144,
418
            tree_b.branch.base, patch_type=None, public_branch=branch_c.base)
1551.12.41 by Aaron Bentley
Clean up tests, add serialization text test
419
1551.12.50 by Aaron Bentley
Use public location of submit branch if possible
420
    def test_use_public_submit_branch(self):
421
        tree_a, tree_b, branch_c = self.make_trees()
422
        branch_c.pull(tree_a.branch)
2520.4.80 by Aaron Bentley
Improve merge directive tests
423
        md = self.from_objects(tree_a.branch.repository, 'rev2a', 500, 144,
424
            tree_b.branch.base, patch_type=None, public_branch=branch_c.base)
1551.12.50 by Aaron Bentley
Use public location of submit branch if possible
425
        self.assertEqual(md.target_branch, tree_b.branch.base)
426
        tree_b.branch.set_public_branch('http://example.com')
2520.4.80 by Aaron Bentley
Improve merge directive tests
427
        md2 = self.from_objects(
1551.12.50 by Aaron Bentley
Use public location of submit branch if possible
428
              tree_a.branch.repository, 'rev2a', 500, 144, tree_b.branch.base,
429
              patch_type=None, public_branch=branch_c.base)
430
        self.assertEqual(md2.target_branch, 'http://example.com')
431
1551.12.41 by Aaron Bentley
Clean up tests, add serialization text test
432
    def test_message(self):
433
        tree_a, tree_b, branch_c = self.make_trees()
2520.5.4 by Aaron Bentley
Replace 'bundle-revisions' with 'submit' command
434
        md3 = self.from_objects(tree_a.branch.repository, 'rev1', 500, 120,
2520.4.80 by Aaron Bentley
Improve merge directive tests
435
            tree_b.branch.base, patch_type=None, public_branch=branch_c.base,
1551.12.33 by Aaron Bentley
Take public_branch as a string, not object
436
            message='Merge message')
1551.12.7 by Aaron Bentley
Fix use of public location/branch
437
        md3.to_lines()
1551.12.5 by Aaron Bentley
Get MergeDirective.from_objects working
438
        self.assertIs(None, md3.patch)
1551.12.27 by Aaron Bentley
support custom message everywhere
439
        self.assertEqual('Merge message', md3.message)
1551.12.16 by Aaron Bentley
Enable signing merge directives
440
1551.12.41 by Aaron Bentley
Clean up tests, add serialization text test
441
    def test_generate_bundle(self):
1551.12.40 by Aaron Bentley
Do not show prefixes in diffs
442
        tree_a, tree_b, branch_c = self.make_trees()
2520.4.80 by Aaron Bentley
Improve merge directive tests
443
        md1 = self.from_objects(tree_a.branch.repository, 'rev2a', 500, 120,
444
            tree_b.branch.base, public_branch=branch_c.base)
445
446
        self.assertContainsRe(md1.get_raw_bundle(), 'Bazaar revision bundle')
1551.12.41 by Aaron Bentley
Clean up tests, add serialization text test
447
        self.assertContainsRe(md1.patch, '\\+content_c')
448
        self.assertNotContainsRe(md1.patch, '\\+content_a')
449
        self.assertContainsRe(md1.patch, '\\+content_c')
450
        self.assertNotContainsRe(md1.patch, '\\+content_a')
1551.12.40 by Aaron Bentley
Do not show prefixes in diffs
451
1551.15.29 by Aaron Bentley
Make merge directives robust against broken bundles
452
    def test_broken_bundle(self):
453
        tree_a, tree_b, branch_c = self.make_trees()
2520.4.73 by Aaron Bentley
Implement new merge directive format
454
        md1 = self.from_objects(tree_a.branch.repository, 'rev2a', 500, 120,
455
            tree_b.branch.base, public_branch=branch_c.base)
1551.15.29 by Aaron Bentley
Make merge directives robust against broken bundles
456
        lines = md1.to_lines()
457
        lines = [l.replace('\n', '\r\n') for l in lines]
458
        md2 = merge_directive.MergeDirective.from_lines(lines)
459
        self.assertEqual('rev2a', md2.revision_id)
460
1551.12.16 by Aaron Bentley
Enable signing merge directives
461
    def test_signing(self):
2425.6.1 by Martin Pool
Fix formatting of timezones in bundles and merge directives.
462
        time = 453
463
        timezone = 7200
1551.12.16 by Aaron Bentley
Enable signing merge directives
464
        class FakeBranch(object):
465
            def get_config(self):
466
                return self
467
            def gpg_signing_command(self):
468
                return 'loopback'
2520.4.80 by Aaron Bentley
Improve merge directive tests
469
        md = self.make_merge_directive('example:', 'sha', time, timezone,
1551.12.16 by Aaron Bentley
Enable signing merge directives
470
            'http://example.com', source_branch="http://example.org",
471
            patch='booga', patch_type='diff')
472
        old_strategy = gpg.GPGStrategy
473
        gpg.GPGStrategy = gpg.LoopbackGPGStrategy
474
        try:
475
            signed = md.to_signed(FakeBranch())
476
        finally:
477
            gpg.GPGStrategy = old_strategy
478
        self.assertContainsRe(signed, '^-----BEGIN PSEUDO-SIGNED CONTENT')
479
        self.assertContainsRe(signed, 'example.org')
480
        self.assertContainsRe(signed, 'booga')
1551.12.26 by Aaron Bentley
Get email working, with optional message
481
482
    def test_email(self):
483
        tree_a, tree_b, branch_c = self.make_trees()
2520.4.80 by Aaron Bentley
Improve merge directive tests
484
        md = self.from_objects(tree_a.branch.repository, 'rev2a', 476, 60,
485
            tree_b.branch.base, patch_type=None,
486
            public_branch=tree_a.branch.base)
1551.12.26 by Aaron Bentley
Get email working, with optional message
487
        message = md.to_email('pqm@example.com', tree_a.branch)
2520.4.80 by Aaron Bentley
Improve merge directive tests
488
        self.assertContainsRe(message.as_string(), self.EMAIL1)
1551.12.26 by Aaron Bentley
Get email working, with optional message
489
        md.message = 'Commit of rev2a with special message'
490
        message = md.to_email('pqm@example.com', tree_a.branch)
2520.4.80 by Aaron Bentley
Improve merge directive tests
491
        self.assertContainsRe(message.as_string(), self.EMAIL2)
1551.14.4 by Aaron Bentley
Change bundle reader and merge directive to both be 'mergeables'
492
1551.14.9 by Aaron Bentley
rename get_target_revision to install_revisions
493
    def test_install_revisions_branch(self):
1551.14.4 by Aaron Bentley
Change bundle reader and merge directive to both be 'mergeables'
494
        tree_a, tree_b, branch_c = self.make_trees()
2520.4.80 by Aaron Bentley
Improve merge directive tests
495
        md = self.from_objects(tree_a.branch.repository, 'rev2a', 500, 36,
496
            tree_b.branch.base, patch_type=None,
497
            public_branch=tree_a.branch.base)
1551.14.4 by Aaron Bentley
Change bundle reader and merge directive to both be 'mergeables'
498
        self.assertFalse(tree_b.branch.repository.has_revision('rev2a'))
1551.14.9 by Aaron Bentley
rename get_target_revision to install_revisions
499
        revision = md.install_revisions(tree_b.branch.repository)
1551.14.4 by Aaron Bentley
Change bundle reader and merge directive to both be 'mergeables'
500
        self.assertEqual('rev2a', revision)
501
        self.assertTrue(tree_b.branch.repository.has_revision('rev2a'))
502
2520.4.109 by Aaron Bentley
start work on directive cherry-picking
503
    def test_get_merge_request(self):
2520.4.108 by Aaron Bentley
Start work on using merge base from directives
504
        tree_a, tree_b, branch_c = self.make_trees()
505
        md = self.from_objects(tree_a.branch.repository, 'rev2a', 500, 36,
2520.4.109 by Aaron Bentley
start work on directive cherry-picking
506
            tree_b.branch.base, patch_type='bundle',
507
            public_branch=tree_a.branch.base)
508
        self.assertFalse(tree_b.branch.repository.has_revision('rev2a'))
509
        md.install_revisions(tree_b.branch.repository)
510
        base, revision, verified = md.get_merge_request(
511
            tree_b.branch.repository)
512
        if isinstance(md, merge_directive.MergeDirective):
513
            self.assertIs(None, base)
514
            self.assertEqual('inapplicable', verified)
515
        else:
516
            self.assertEqual('rev1', base)
517
            self.assertEqual('verified', verified)
518
        self.assertEqual('rev2a', revision)
519
        self.assertTrue(tree_b.branch.repository.has_revision('rev2a'))
520
        md = self.from_objects(tree_a.branch.repository, 'rev2a', 500, 36,
2520.4.108 by Aaron Bentley
Start work on using merge base from directives
521
            tree_b.branch.base, patch_type=None,
522
            public_branch=tree_a.branch.base)
2520.4.109 by Aaron Bentley
start work on directive cherry-picking
523
        base, revision, verified = md.get_merge_request(
524
            tree_b.branch.repository)
525
        if isinstance(md, merge_directive.MergeDirective):
526
            self.assertIs(None, base)
527
            self.assertEqual('inapplicable', verified)
528
        else:
529
            self.assertEqual('rev1', base)
530
            self.assertEqual('inapplicable', verified)
531
        md = self.from_objects(tree_a.branch.repository, 'rev2a', 500, 36,
532
            tree_b.branch.base, patch_type='diff',
533
            public_branch=tree_a.branch.base)
534
        base, revision, verified = md.get_merge_request(
2520.4.108 by Aaron Bentley
Start work on using merge base from directives
535
            tree_b.branch.repository)
536
        if isinstance(md, merge_directive.MergeDirective):
537
            self.assertIs(None, base)
538
            self.assertEqual('inapplicable', verified)
539
        else:
540
            self.assertEqual('rev1', base)
541
            self.assertEqual('verified', verified)
2520.4.109 by Aaron Bentley
start work on directive cherry-picking
542
        md.patch='asdf'
543
        base, revision, verified = md.get_merge_request(
544
            tree_b.branch.repository)
545
        if isinstance(md, merge_directive.MergeDirective):
546
            self.assertIs(None, base)
547
            self.assertEqual('inapplicable', verified)
548
        else:
549
            self.assertEqual('rev1', base)
2520.7.1 by Aaron Bentley
Reactivate patch verification
550
            self.assertEqual('failed', verified)
2520.4.108 by Aaron Bentley
Start work on using merge base from directives
551
1551.14.9 by Aaron Bentley
rename get_target_revision to install_revisions
552
    def test_install_revisions_bundle(self):
1551.14.4 by Aaron Bentley
Change bundle reader and merge directive to both be 'mergeables'
553
        tree_a, tree_b, branch_c = self.make_trees()
2520.4.80 by Aaron Bentley
Improve merge directive tests
554
        md = self.from_objects(tree_a.branch.repository, 'rev2a', 500, 36,
555
            tree_b.branch.base, patch_type='bundle',
556
            public_branch=tree_a.branch.base)
1551.14.4 by Aaron Bentley
Change bundle reader and merge directive to both be 'mergeables'
557
        self.assertFalse(tree_b.branch.repository.has_revision('rev2a'))
1551.14.9 by Aaron Bentley
rename get_target_revision to install_revisions
558
        revision = md.install_revisions(tree_b.branch.repository)
1551.14.4 by Aaron Bentley
Change bundle reader and merge directive to both be 'mergeables'
559
        self.assertEqual('rev2a', revision)
560
        self.assertTrue(tree_b.branch.repository.has_revision('rev2a'))
561
562
    def test_get_target_revision_nofetch(self):
563
        tree_a, tree_b, branch_c = self.make_trees()
564
        tree_b.branch.fetch(tree_a.branch)
2520.4.80 by Aaron Bentley
Improve merge directive tests
565
        md = self.from_objects( tree_a.branch.repository, 'rev2a', 500, 36,
566
            tree_b.branch.base, patch_type=None,
567
            public_branch=tree_a.branch.base)
1551.14.4 by Aaron Bentley
Change bundle reader and merge directive to both be 'mergeables'
568
        md.source_branch = '/dev/null'
1551.14.9 by Aaron Bentley
rename get_target_revision to install_revisions
569
        revision = md.install_revisions(tree_b.branch.repository)
1551.14.4 by Aaron Bentley
Change bundle reader and merge directive to both be 'mergeables'
570
        self.assertEqual('rev2a', revision)
2520.4.73 by Aaron Bentley
Implement new merge directive format
571
1551.19.19 by Aaron Bentley
Merge directives can now fetch prerequisites from the target branch
572
    def test_use_submit_for_missing_dependency(self):
573
        tree_a, tree_b, branch_c = self.make_trees()
574
        branch_c.pull(tree_a.branch)
575
        self.build_tree_contents([('tree_a/file', 'content_q\ncontent_r\n')])
576
        tree_a.commit('rev3a', rev_id='rev3a')
577
        md = self.from_objects(tree_a.branch.repository, 'rev3a', 500, 36,
578
            branch_c.base, base_revision_id='rev2a')
579
        revision = md.install_revisions(tree_b.branch.repository)
580
3535.8.1 by James Westby
Handle something that isn't a branch being specified in target_branch.
581
    def test_handle_target_not_a_branch(self):
582
        tree_a, tree_b, branch_c = self.make_trees()
583
        branch_c.pull(tree_a.branch)
584
        self.build_tree_contents([('tree_a/file', 'content_q\ncontent_r\n')])
585
        tree_a.commit('rev3a', rev_id='rev3a')
586
        md = self.from_objects(tree_a.branch.repository, 'rev3a', 500, 36,
587
            branch_c.base, base_revision_id='rev2a')
3535.8.2 by James Westby
Incorporate spiv's feedback.
588
        md.target_branch = self.get_url('not-a-branch')
3535.8.1 by James Westby
Handle something that isn't a branch being specified in target_branch.
589
        self.assertRaises(errors.TargetNotBranch, md.install_revisions,
590
                tree_b.branch.repository)
591
2520.4.73 by Aaron Bentley
Implement new merge directive format
592
593
class TestMergeDirective1Branch(tests.TestCaseWithTransport,
594
    TestMergeDirectiveBranch):
595
    """Test merge directive format 1 with a branch"""
2520.4.80 by Aaron Bentley
Improve merge directive tests
596
597
    EMAIL1 = EMAIL1
598
599
    EMAIL2 = EMAIL2
600
2520.4.73 by Aaron Bentley
Implement new merge directive format
601
    def from_objects(self, repository, revision_id, time, timezone,
602
        target_branch, patch_type='bundle', local_target_branch=None,
1551.19.19 by Aaron Bentley
Merge directives can now fetch prerequisites from the target branch
603
        public_branch=None, message=None, base_revision_id=None):
604
        if base_revision_id is not None:
605
            raise tests.TestNotApplicable('This format does not support'
606
                                          ' explicit bases.')
3010.1.9 by Robert Collins
test_merge_directive locking correctness.
607
        repository.lock_write()
608
        try:
609
            return merge_directive.MergeDirective.from_objects( repository,
610
                revision_id, time, timezone, target_branch, patch_type,
611
                local_target_branch, public_branch, message)
612
        finally:
613
            repository.unlock()
2520.4.73 by Aaron Bentley
Implement new merge directive format
614
2520.4.80 by Aaron Bentley
Improve merge directive tests
615
    def make_merge_directive(self, revision_id, testament_sha1, time, timezone,
616
                 target_branch, patch=None, patch_type=None,
617
                 source_branch=None, message=None):
618
        return merge_directive.MergeDirective(revision_id, testament_sha1,
619
                 time, timezone, target_branch, patch, patch_type,
620
                 source_branch, message)
621
2520.4.73 by Aaron Bentley
Implement new merge directive format
622
623
class TestMergeDirective2Branch(tests.TestCaseWithTransport,
624
    TestMergeDirectiveBranch):
625
    """Test merge directive format 2 with a branch"""
626
2520.4.80 by Aaron Bentley
Improve merge directive tests
627
    EMAIL1 = EMAIL1_2
628
629
    EMAIL2 = EMAIL2_2
630
2520.4.73 by Aaron Bentley
Implement new merge directive format
631
    def from_objects(self, repository, revision_id, time, timezone,
632
        target_branch, patch_type='bundle', local_target_branch=None,
2520.4.112 by Aaron Bentley
Make cherry-pick merge directives possible
633
        public_branch=None, message=None, base_revision_id=None):
2520.5.4 by Aaron Bentley
Replace 'bundle-revisions' with 'submit' command
634
        include_patch = (patch_type in ('bundle', 'diff'))
635
        include_bundle = (patch_type == 'bundle')
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
636
        self.assertTrue(patch_type in ('bundle', 'diff', None))
2520.4.73 by Aaron Bentley
Implement new merge directive format
637
        return merge_directive.MergeDirective2.from_objects(
638
            repository, revision_id, time, timezone, target_branch,
2520.5.4 by Aaron Bentley
Replace 'bundle-revisions' with 'submit' command
639
            include_patch, include_bundle, local_target_branch, public_branch,
640
            message, base_revision_id)
2520.4.80 by Aaron Bentley
Improve merge directive tests
641
642
    def make_merge_directive(self, revision_id, testament_sha1, time, timezone,
643
                 target_branch, patch=None, patch_type=None,
2520.4.105 by Aaron Bentley
Implement patch verification
644
                 source_branch=None, message=None, base_revision_id='null:'):
2520.4.80 by Aaron Bentley
Improve merge directive tests
645
        if patch_type == 'bundle':
646
            bundle = patch
647
            patch = None
648
        else:
649
            bundle = None
650
        return merge_directive.MergeDirective2(revision_id, testament_sha1,
651
            time, timezone, target_branch, patch, source_branch, message,
2520.4.105 by Aaron Bentley
Implement patch verification
652
            bundle, base_revision_id)
653
654
    def test_base_revision(self):
655
        tree_a, tree_b, branch_c = self.make_trees()
656
        md = self.from_objects(tree_a.branch.repository, 'rev2a', 500, 60,
657
            tree_b.branch.base, patch_type='bundle',
2520.4.112 by Aaron Bentley
Make cherry-pick merge directives possible
658
            public_branch=tree_a.branch.base, base_revision_id=None)
2520.4.105 by Aaron Bentley
Implement patch verification
659
        self.assertEqual('rev1', md.base_revision_id)
2520.4.112 by Aaron Bentley
Make cherry-pick merge directives possible
660
        md = self.from_objects(tree_a.branch.repository, 'rev2a', 500, 60,
661
            tree_b.branch.base, patch_type='bundle',
662
            public_branch=tree_a.branch.base, base_revision_id='null:')
663
        self.assertEqual('null:', md.base_revision_id)
2520.4.105 by Aaron Bentley
Implement patch verification
664
        lines = md.to_lines()
665
        md2 = merge_directive.MergeDirective.from_lines(lines)
666
        self.assertEqual(md2.base_revision_id, md.base_revision_id)
667
668
    def test_patch_verification(self):
669
        tree_a, tree_b, branch_c = self.make_trees()
670
        md = self.from_objects(tree_a.branch.repository, 'rev2a', 500, 60,
671
            tree_b.branch.base, patch_type='bundle',
672
            public_branch=tree_a.branch.base)
673
        lines = md.to_lines()
674
        md2 = merge_directive.MergeDirective.from_lines(lines)
675
        md2._verify_patch(tree_a.branch.repository)
2520.7.2 by Aaron Bentley
Restore patch verification for CR, CRLF files
676
        # Strip trailing whitespace
2520.4.105 by Aaron Bentley
Implement patch verification
677
        md2.patch = md2.patch.replace(' \n', '\n')
678
        md2._verify_patch(tree_a.branch.repository)
679
        # Convert to Mac line-endings
2520.7.2 by Aaron Bentley
Restore patch verification for CR, CRLF files
680
        md2.patch = re.sub('(\r\n|\r|\n)', '\r', md2.patch)
2520.4.108 by Aaron Bentley
Start work on using merge base from directives
681
        self.assertTrue(md2._verify_patch(tree_a.branch.repository))
2520.4.105 by Aaron Bentley
Implement patch verification
682
        # Convert to DOS line-endings
2520.7.2 by Aaron Bentley
Restore patch verification for CR, CRLF files
683
        md2.patch = re.sub('(\r\n|\r|\n)', '\r\n', md2.patch)
2520.4.108 by Aaron Bentley
Start work on using merge base from directives
684
        self.assertTrue(md2._verify_patch(tree_a.branch.repository))
2520.4.105 by Aaron Bentley
Implement patch verification
685
        md2.patch = md2.patch.replace('content_c', 'content_d')
2520.4.108 by Aaron Bentley
Start work on using merge base from directives
686
        self.assertFalse(md2._verify_patch(tree_a.branch.repository))
2694.1.1 by Aaron Bentley
Restore support for Merge directive 2 / 0.19
687
688
689
class TestParseOldMergeDirective2(tests.TestCase):
690
691
    def test_parse_old_merge_directive(self):
692
        md = merge_directive.MergeDirective.from_lines(INPUT1_2_OLD)
693
        self.assertEqual('example:', md.revision_id)
694
        self.assertEqual('sha', md.testament_sha1)
695
        self.assertEqual('http://example.com', md.target_branch)
696
        self.assertEqual('http://example.org', md.source_branch)
697
        self.assertEqual(453, md.time)
698
        self.assertEqual(120, md.timezone)
699
        self.assertEqual('booga', md.patch)
700
        self.assertEqual('diff', md.patch_type)
701
        self.assertEqual('Hi mom!', md.message)
4098.5.16 by Aaron Bentley
Move hook to MergeDirective, implement MergeDirective.compose_merge_request.
702
703
704
class TestHook(object):
705
    """Hook callback for test purposes."""
706
707
    def __init__(self, result=None):
708
        self.calls = []
709
        self.result = result
710
711
    def __call__(self, params):
712
        self.calls.append(params)
713
        return self.result
714
715
716
class HookMailClient(mail_client.MailClient):
717
    """Mail client for testing hooks."""
718
719
    def __init__(self, config):
720
        self.body = None
721
        self.config = config
722
723
    def compose(self, prompt, to, subject, attachment, mime_subtype,
724
                extension, basename=None, body=None):
725
        self.body = body
726
727
728
class TestBodyHook(tests.TestCaseWithTransport):
729
4098.5.18 by Aaron Bentley
Gracefully handle mail clients that don't support bodies.
730
    def compose_with_hooks(self, test_hooks, supports_body=True):
4098.5.16 by Aaron Bentley
Move hook to MergeDirective, implement MergeDirective.compose_merge_request.
731
        client = HookMailClient({})
4098.5.18 by Aaron Bentley
Gracefully handle mail clients that don't support bodies.
732
        client.supports_body = supports_body
4098.5.16 by Aaron Bentley
Move hook to MergeDirective, implement MergeDirective.compose_merge_request.
733
        for test_hook in test_hooks:
734
            merge_directive.MergeDirective.hooks.install_named_hook(
735
                'merge_request_body', test_hook, 'test')
736
        tree = self.make_branch_and_tree('foo')
737
        tree.commit('foo')
738
        directive = merge_directive.MergeDirective2(
739
            tree.branch.last_revision(), 'sha', 0, 0, 'sha',
740
            source_branch=tree.branch.base,
741
            base_revision_id=tree.branch.last_revision(),
742
            message='This code rox')
743
        directive.compose_merge_request(client, 'jrandom@example.com',
744
            None, tree.branch)
745
        return client, directive
746
4098.5.18 by Aaron Bentley
Gracefully handle mail clients that don't support bodies.
747
    def test_no_supports_body(self):
748
        test_hook = TestHook('foo')
749
        old_warn = trace.warning
750
        warnings = []
751
        def warn(*args):
752
            warnings.append(args)
753
        trace.warning = warn
754
        try:
755
            client, directive = self.compose_with_hooks([test_hook],
756
                supports_body=False)
757
        finally:
758
            trace.warning = old_warn
759
        self.assertEqual(0, len(test_hook.calls))
760
        self.assertEqual(('Cannot run merge_request_body hooks because mail'
761
                          ' client %s does not support message bodies.',
762
                          'HookMailClient'), warnings[0])
763
4098.5.16 by Aaron Bentley
Move hook to MergeDirective, implement MergeDirective.compose_merge_request.
764
    def test_body_hook(self):
765
        test_hook = TestHook('foo')
766
        client, directive = self.compose_with_hooks([test_hook])
767
        self.assertEqual(1, len(test_hook.calls))
768
        self.assertEqual('foo', client.body)
769
        params = test_hook.calls[0]
770
        self.assertIsInstance(params,
771
                              merge_directive.MergeRequestBodyParams)
772
        self.assertIs(None, params.body)
773
        self.assertIs(None, params.orig_body)
774
        self.assertEqual('jrandom@example.com', params.to)
775
        self.assertEqual('[MERGE] This code rox', params.subject)
776
        self.assertEqual(directive, params.directive)
777
        self.assertEqual('foo-1', params.basename)
778
779
    def test_body_hook_chaining(self):
780
        test_hook1 = TestHook('foo')
781
        test_hook2 = TestHook('bar')
782
        client = self.compose_with_hooks([test_hook1, test_hook2])[0]
783
        self.assertEqual(None, test_hook1.calls[0].body)
784
        self.assertEqual(None, test_hook1.calls[0].orig_body)
785
        self.assertEqual('foo', test_hook2.calls[0].body)
786
        self.assertEqual(None, test_hook2.calls[0].orig_body)
787
        self.assertEqual('bar', client.body)