/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_merge_directive.py

  • Committer: John Arbash Meinel
  • Date: 2009-06-18 18:18:36 UTC
  • mto: This revision was merged to the branch mainline in revision 4461.
  • Revision ID: john@arbash-meinel.com-20090618181836-biodfkat9a8eyzjz
The new add_inventory_by_delta is returning a CHKInventory when mapping from NULL
Which is completely valid, but 'broke' one of the tests.
So to fix it, changed the test to use CHKInventories on both sides, and add an __eq__
member. The nice thing is that CHKInventory.__eq__ is fairly cheap, since it only
has to check the root keys.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
16
 
 
17
import re
 
18
 
 
19
from bzrlib import (
 
20
    errors,
 
21
    gpg,
 
22
    mail_client,
 
23
    merge_directive,
 
24
    tests,
 
25
    trace,
 
26
    )
 
27
 
 
28
 
 
29
OUTPUT1 = """# Bazaar merge directive format 1
 
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
 
 
37
OUTPUT1_2 = """# Bazaar merge directive format 2 (Bazaar 0.90)
 
38
# revision_id: example:
 
39
# target_branch: http://example.com
 
40
# testament_sha1: sha
 
41
# timestamp: 1970-01-01 00:09:33 +0002
 
42
# base_revision_id: null:
 
43
#\x20
 
44
# Begin bundle
 
45
booga"""
 
46
 
 
47
OUTPUT2 = """# Bazaar merge directive format 1
 
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
 
 
57
OUTPUT2_2 = """# Bazaar merge directive format 2 (Bazaar 0.90)
 
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!
 
64
# base_revision_id: null:
 
65
#\x20
 
66
# Begin patch
 
67
booga"""
 
68
 
 
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
 
 
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
 
 
102
# Bazaar merge directive format 2 (Bazaar 0.90)\r
 
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
 
108
# base_revision_id: null:
 
109
# message: Hi mom!
 
110
#\x20
 
111
# Begin patch
 
112
booga""".splitlines(True)
 
113
 
 
114
 
 
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
 
 
138
 
 
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
 
 
154
class TestMergeDirective(object):
 
155
 
 
156
    def test_merge_source(self):
 
157
        time = 500000.0
 
158
        timezone = 5 * 3600
 
159
        self.assertRaises(errors.NoMergeSource, self.make_merge_directive,
 
160
            'example:', 'sha', time, timezone, 'http://example.com')
 
161
        self.assertRaises(errors.NoMergeSource, self.make_merge_directive,
 
162
            'example:', 'sha', time, timezone, 'http://example.com',
 
163
            patch_type='diff')
 
164
        self.make_merge_directive('example:', 'sha', time, timezone,
 
165
            'http://example.com', source_branch='http://example.org')
 
166
        md = self.make_merge_directive('null:', 'sha', time, timezone,
 
167
            'http://example.com', patch='blah', patch_type='bundle')
 
168
        self.assertIs(None, md.source_branch)
 
169
        md2 = self.make_merge_directive('null:', 'sha', time, timezone,
 
170
            'http://example.com', patch='blah', patch_type='bundle',
 
171
            source_branch='bar')
 
172
        self.assertEqual('bar', md2.source_branch)
 
173
 
 
174
    def test_serialization(self):
 
175
        time = 453
 
176
        timezone = 120
 
177
        md = self.make_merge_directive('example:', 'sha', time, timezone,
 
178
            'http://example.com', patch='booga', patch_type='bundle')
 
179
        self.assertEqualDiff(self.OUTPUT1, ''.join(md.to_lines()))
 
180
        md = self.make_merge_directive('example:', 'sha', time, timezone,
 
181
            'http://example.com', source_branch="http://example.org",
 
182
            patch='booga', patch_type='diff', message="Hi mom!")
 
183
        self.assertEqualDiff(self.OUTPUT2, ''.join(md.to_lines()))
 
184
 
 
185
    def test_deserialize_junk(self):
 
186
        time = 501
 
187
        self.assertRaises(errors.NotAMergeDirective,
 
188
                          merge_directive.MergeDirective.from_lines, 'lala')
 
189
 
 
190
    def test_deserialize_empty(self):
 
191
        self.assertRaises(errors.NotAMergeDirective,
 
192
                          merge_directive.MergeDirective.from_lines, [])
 
193
 
 
194
    def test_deserialize_leading_junk(self):
 
195
        md = merge_directive.MergeDirective.from_lines(self.INPUT1)
 
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)
 
200
        self.assertEqual(453, md.time)
 
201
        self.assertEqual(120, md.timezone)
 
202
        self.assertEqual('booga', md.patch)
 
203
        self.assertEqual('diff', md.patch_type)
 
204
        self.assertEqual('Hi mom!', md.message)
 
205
 
 
206
    def test_roundtrip(self):
 
207
        time = 500000
 
208
        timezone = 7.5 * 3600
 
209
        md = self.make_merge_directive('example:', 'sha', time, timezone,
 
210
            'http://example.com', source_branch="http://example.org",
 
211
            patch='booga', patch_type='diff')
 
212
        md2 = merge_directive.MergeDirective.from_lines(md.to_lines())
 
213
        self.assertEqual('example:', md2.revision_id)
 
214
        self.assertIsInstance(md2.revision_id, str)
 
215
        self.assertEqual('sha', md2.testament_sha1)
 
216
        self.assertEqual('http://example.com', md2.target_branch)
 
217
        self.assertEqual('http://example.org', md2.source_branch)
 
218
        self.assertEqual(time, md2.time)
 
219
        self.assertEqual(timezone, md2.timezone)
 
220
        self.assertEqual('diff', md2.patch_type)
 
221
        self.assertEqual('booga', md2.patch)
 
222
        self.assertEqual(None, md2.message)
 
223
        self.set_bundle(md, "# Bazaar revision bundle v0.9\n#\n")
 
224
        md.message = "Hi mom!"
 
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)
 
228
        self.assertEqual("bundle", md3.patch_type)
 
229
        self.assertContainsRe(md3.to_lines()[0],
 
230
            '^# Bazaar merge directive format ')
 
231
        self.assertEqual("Hi mom!", md3.message)
 
232
        md3.clear_payload()
 
233
        self.assertIs(None, md3.get_raw_bundle())
 
234
        md4 = merge_directive.MergeDirective.from_lines(md3.to_lines())
 
235
        self.assertIs(None, md4.patch_type)
 
236
 
 
237
 
 
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,
 
281
                 source_branch=None, message=None, base_revision_id='null:'):
 
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,
 
289
            bundle, base_revision_id)
 
290
 
 
291
    @staticmethod
 
292
    def set_bundle(md, value):
 
293
        md.bundle = value
 
294
 
 
295
 
 
296
EMAIL1 = """From: "J. Random Hacker" <jrandom@example.com>
 
297
Subject: Commit of rev2a
 
298
To: pqm@example.com
 
299
User-Agent: Bazaar \(.*\)
 
300
 
 
301
# Bazaar merge directive format 1
 
302
# revision_id: rev2a
 
303
# target_branch: (.|\n)*
 
304
# testament_sha1: .*
 
305
# timestamp: 1970-01-01 00:08:56 \\+0001
 
306
# source_branch: (.|\n)*
 
307
"""
 
308
 
 
309
 
 
310
EMAIL1_2 = """From: "J. Random Hacker" <jrandom@example.com>
 
311
Subject: Commit of rev2a
 
312
To: pqm@example.com
 
313
User-Agent: Bazaar \(.*\)
 
314
 
 
315
# Bazaar merge directive format 2 \\(Bazaar 0.90\\)
 
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
 
 
324
EMAIL2 = """From: "J. Random Hacker" <jrandom@example.com>
 
325
Subject: Commit of rev2a with special message
 
326
To: pqm@example.com
 
327
User-Agent: Bazaar \(.*\)
 
328
 
 
329
# Bazaar merge directive format 1
 
330
# revision_id: rev2a
 
331
# target_branch: (.|\n)*
 
332
# testament_sha1: .*
 
333
# timestamp: 1970-01-01 00:08:56 \\+0001
 
334
# source_branch: (.|\n)*
 
335
# message: Commit of rev2a with special message
 
336
"""
 
337
 
 
338
EMAIL2_2 = """From: "J. Random Hacker" <jrandom@example.com>
 
339
Subject: Commit of rev2a with special message
 
340
To: pqm@example.com
 
341
User-Agent: Bazaar \(.*\)
 
342
 
 
343
# Bazaar merge directive format 2 \\(Bazaar 0.90\\)
 
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
"""
 
351
 
 
352
class TestMergeDirectiveBranch(object):
 
353
 
 
354
    def make_trees(self):
 
355
        tree_a = self.make_branch_and_tree('tree_a')
 
356
        tree_a.branch.get_config().set_user_option('email',
 
357
            'J. Random Hacker <jrandom@example.com>')
 
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'])
 
361
        tree_a.commit('message', rev_id='rev1')
 
362
        tree_b = tree_a.bzrdir.sprout('tree_b').open_workingtree()
 
363
        branch_c = tree_a.bzrdir.sprout('branch_c').open_branch()
 
364
        tree_b.commit('message', rev_id='rev2b')
 
365
        self.build_tree_contents([('tree_a/file', 'content_a\ncontent_c \n'),
 
366
                                  ('tree_a/file_2', 'content_x\rcontent_z\r')])
 
367
        tree_a.commit('Commit of rev2a', rev_id='rev2a')
 
368
        return tree_a, tree_b, branch_c
 
369
 
 
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')
 
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)
 
376
 
 
377
    def test_disk_name(self):
 
378
        tree_a, tree_b, branch_c = self.make_trees()
 
379
        tree_a.branch.nick = 'fancy <name>'
 
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
 
 
391
    def test_generate_patch(self):
 
392
        tree_a, tree_b, branch_c = self.make_trees()
 
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)
 
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):
 
402
        tree_a, tree_b, branch_c = self.make_trees()
 
403
        self.assertRaises(errors.PublicBranchOutOfDate,
 
404
            self.from_objects, tree_a.branch.repository, 'rev2a', 500, 144,
 
405
            tree_b.branch.base, public_branch=branch_c.base, patch_type='diff')
 
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)
 
409
        # public branch is not checked if patch format is bundle.
 
410
        md1 = self.from_objects(tree_a.branch.repository, 'rev2a', 500, 144,
 
411
            tree_b.branch.base, public_branch=branch_c.base)
 
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)
 
415
        # Once we update the public branch, we can generate a diff.
 
416
        branch_c.pull(tree_a.branch)
 
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)
 
419
 
 
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)
 
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)
 
425
        self.assertEqual(md.target_branch, tree_b.branch.base)
 
426
        tree_b.branch.set_public_branch('http://example.com')
 
427
        md2 = self.from_objects(
 
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
 
 
432
    def test_message(self):
 
433
        tree_a, tree_b, branch_c = self.make_trees()
 
434
        md3 = self.from_objects(tree_a.branch.repository, 'rev1', 500, 120,
 
435
            tree_b.branch.base, patch_type=None, public_branch=branch_c.base,
 
436
            message='Merge message')
 
437
        md3.to_lines()
 
438
        self.assertIs(None, md3.patch)
 
439
        self.assertEqual('Merge message', md3.message)
 
440
 
 
441
    def test_generate_bundle(self):
 
442
        tree_a, tree_b, branch_c = self.make_trees()
 
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')
 
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')
 
451
 
 
452
    def test_broken_bundle(self):
 
453
        tree_a, tree_b, branch_c = self.make_trees()
 
454
        md1 = self.from_objects(tree_a.branch.repository, 'rev2a', 500, 120,
 
455
            tree_b.branch.base, public_branch=branch_c.base)
 
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
 
 
461
    def test_signing(self):
 
462
        time = 453
 
463
        timezone = 7200
 
464
        class FakeBranch(object):
 
465
            def get_config(self):
 
466
                return self
 
467
            def gpg_signing_command(self):
 
468
                return 'loopback'
 
469
        md = self.make_merge_directive('example:', 'sha', time, timezone,
 
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')
 
481
 
 
482
    def test_email(self):
 
483
        tree_a, tree_b, branch_c = self.make_trees()
 
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)
 
487
        message = md.to_email('pqm@example.com', tree_a.branch)
 
488
        self.assertContainsRe(message.as_string(), self.EMAIL1)
 
489
        md.message = 'Commit of rev2a with special message'
 
490
        message = md.to_email('pqm@example.com', tree_a.branch)
 
491
        self.assertContainsRe(message.as_string(), self.EMAIL2)
 
492
 
 
493
    def test_install_revisions_branch(self):
 
494
        tree_a, tree_b, branch_c = self.make_trees()
 
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)
 
498
        self.assertFalse(tree_b.branch.repository.has_revision('rev2a'))
 
499
        revision = md.install_revisions(tree_b.branch.repository)
 
500
        self.assertEqual('rev2a', revision)
 
501
        self.assertTrue(tree_b.branch.repository.has_revision('rev2a'))
 
502
 
 
503
    def test_get_merge_request(self):
 
504
        tree_a, tree_b, branch_c = self.make_trees()
 
505
        md = self.from_objects(tree_a.branch.repository, 'rev2a', 500, 36,
 
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,
 
521
            tree_b.branch.base, patch_type=None,
 
522
            public_branch=tree_a.branch.base)
 
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(
 
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)
 
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)
 
550
            self.assertEqual('failed', verified)
 
551
 
 
552
    def test_install_revisions_bundle(self):
 
553
        tree_a, tree_b, branch_c = self.make_trees()
 
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)
 
557
        self.assertFalse(tree_b.branch.repository.has_revision('rev2a'))
 
558
        revision = md.install_revisions(tree_b.branch.repository)
 
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)
 
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)
 
568
        md.source_branch = '/dev/null'
 
569
        revision = md.install_revisions(tree_b.branch.repository)
 
570
        self.assertEqual('rev2a', revision)
 
571
 
 
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
 
 
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')
 
588
        md.target_branch = self.get_url('not-a-branch')
 
589
        self.assertRaises(errors.TargetNotBranch, md.install_revisions,
 
590
                tree_b.branch.repository)
 
591
 
 
592
 
 
593
class TestMergeDirective1Branch(tests.TestCaseWithTransport,
 
594
    TestMergeDirectiveBranch):
 
595
    """Test merge directive format 1 with a branch"""
 
596
 
 
597
    EMAIL1 = EMAIL1
 
598
 
 
599
    EMAIL2 = EMAIL2
 
600
 
 
601
    def from_objects(self, repository, revision_id, time, timezone,
 
602
        target_branch, patch_type='bundle', local_target_branch=None,
 
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.')
 
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()
 
614
 
 
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
 
 
622
 
 
623
class TestMergeDirective2Branch(tests.TestCaseWithTransport,
 
624
    TestMergeDirectiveBranch):
 
625
    """Test merge directive format 2 with a branch"""
 
626
 
 
627
    EMAIL1 = EMAIL1_2
 
628
 
 
629
    EMAIL2 = EMAIL2_2
 
630
 
 
631
    def from_objects(self, repository, revision_id, time, timezone,
 
632
        target_branch, patch_type='bundle', local_target_branch=None,
 
633
        public_branch=None, message=None, base_revision_id=None):
 
634
        include_patch = (patch_type in ('bundle', 'diff'))
 
635
        include_bundle = (patch_type == 'bundle')
 
636
        self.assertTrue(patch_type in ('bundle', 'diff', None))
 
637
        return merge_directive.MergeDirective2.from_objects(
 
638
            repository, revision_id, time, timezone, target_branch,
 
639
            include_patch, include_bundle, local_target_branch, public_branch,
 
640
            message, base_revision_id)
 
641
 
 
642
    def make_merge_directive(self, revision_id, testament_sha1, time, timezone,
 
643
                 target_branch, patch=None, patch_type=None,
 
644
                 source_branch=None, message=None, base_revision_id='null:'):
 
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,
 
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',
 
658
            public_branch=tree_a.branch.base, base_revision_id=None)
 
659
        self.assertEqual('rev1', md.base_revision_id)
 
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)
 
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)
 
676
        # Strip trailing whitespace
 
677
        md2.patch = md2.patch.replace(' \n', '\n')
 
678
        md2._verify_patch(tree_a.branch.repository)
 
679
        # Convert to Mac line-endings
 
680
        md2.patch = re.sub('(\r\n|\r|\n)', '\r', md2.patch)
 
681
        self.assertTrue(md2._verify_patch(tree_a.branch.repository))
 
682
        # Convert to DOS line-endings
 
683
        md2.patch = re.sub('(\r\n|\r|\n)', '\r\n', md2.patch)
 
684
        self.assertTrue(md2._verify_patch(tree_a.branch.repository))
 
685
        md2.patch = md2.patch.replace('content_c', 'content_d')
 
686
        self.assertFalse(md2._verify_patch(tree_a.branch.repository))
 
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)
 
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
 
 
730
    def compose_with_hooks(self, test_hooks, supports_body=True):
 
731
        client = HookMailClient({})
 
732
        client.supports_body = supports_body
 
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
 
 
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
 
 
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)