/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: mernst at mit
  • Date: 2008-10-16 10:57:16 UTC
  • mto: This revision was merged to the branch mainline in revision 3799.
  • Revision ID: mernst@csail.mit.edu-20081016105716-v8x8n5t2pf7f6uds
Improved documentation of stacked and lightweight branches

These patches improve the User Guide's documentation of stacked and
lightweight branches.

Section "1.2.6 Putting the concepts together" should mention stacked
branches and the difference between them and lightweight branches.  It
should also contain links to further details of the common scenarios.

Section "5.3.4 Getting a lightweight checkout" should mention stacked
branches as an option, and should link to all the options, not just some of
them.  It should also clarify that lightweight only applies to checkouts,
not to arbitrary branches.

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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
 
 
17
import re
 
18
 
 
19
from bzrlib import (
 
20
    errors,
 
21
    gpg,
 
22
    merge_directive,
 
23
    tests,
 
24
    )
 
25
 
 
26
 
 
27
OUTPUT1 = """# Bazaar merge directive format 1
 
28
# revision_id: example:
 
29
# target_branch: http://example.com
 
30
# testament_sha1: sha
 
31
# timestamp: 1970-01-01 00:09:33 +0002
 
32
#\x20
 
33
booga"""
 
34
 
 
35
OUTPUT1_2 = """# Bazaar merge directive format 2 (Bazaar 0.90)
 
36
# revision_id: example:
 
37
# target_branch: http://example.com
 
38
# testament_sha1: sha
 
39
# timestamp: 1970-01-01 00:09:33 +0002
 
40
# base_revision_id: null:
 
41
#\x20
 
42
# Begin bundle
 
43
booga"""
 
44
 
 
45
OUTPUT2 = """# Bazaar merge directive format 1
 
46
# revision_id: example:
 
47
# target_branch: http://example.com
 
48
# testament_sha1: sha
 
49
# timestamp: 1970-01-01 00:09:33 +0002
 
50
# source_branch: http://example.org
 
51
# message: Hi mom!
 
52
#\x20
 
53
booga"""
 
54
 
 
55
OUTPUT2_2 = """# Bazaar merge directive format 2 (Bazaar 0.90)
 
56
# revision_id: example:
 
57
# target_branch: http://example.com
 
58
# testament_sha1: sha
 
59
# timestamp: 1970-01-01 00:09:33 +0002
 
60
# source_branch: http://example.org
 
61
# message: Hi mom!
 
62
# base_revision_id: null:
 
63
#\x20
 
64
# Begin patch
 
65
booga"""
 
66
 
 
67
INPUT1 = """
 
68
I was thinking today about creating a merge directive.
 
69
 
 
70
So I did.
 
71
 
 
72
Here it is.
 
73
 
 
74
(I've pasted it in the body of this message)
 
75
 
 
76
Aaron
 
77
 
 
78
# Bazaar merge directive format 1\r
 
79
# revision_id: example:
 
80
# target_branch: http://example.com
 
81
# testament_sha1: sha
 
82
# timestamp: 1970-01-01 00:09:33 +0002
 
83
# source_branch: http://example.org
 
84
# message: Hi mom!
 
85
#\x20
 
86
booga""".splitlines(True)
 
87
 
 
88
 
 
89
INPUT1_2 = """
 
90
I was thinking today about creating a merge directive.
 
91
 
 
92
So I did.
 
93
 
 
94
Here it is.
 
95
 
 
96
(I've pasted it in the body of this message)
 
97
 
 
98
Aaron
 
99
 
 
100
# Bazaar merge directive format 2 (Bazaar 0.90)\r
 
101
# revision_id: example:
 
102
# target_branch: http://example.com
 
103
# testament_sha1: sha
 
104
# timestamp: 1970-01-01 00:09:33 +0002
 
105
# source_branch: http://example.org
 
106
# base_revision_id: null:
 
107
# message: Hi mom!
 
108
#\x20
 
109
# Begin patch
 
110
booga""".splitlines(True)
 
111
 
 
112
 
 
113
INPUT1_2_OLD = """
 
114
I was thinking today about creating a merge directive.
 
115
 
 
116
So I did.
 
117
 
 
118
Here it is.
 
119
 
 
120
(I've pasted it in the body of this message)
 
121
 
 
122
Aaron
 
123
 
 
124
# Bazaar merge directive format 2 (Bazaar 0.19)\r
 
125
# revision_id: example:
 
126
# target_branch: http://example.com
 
127
# testament_sha1: sha
 
128
# timestamp: 1970-01-01 00:09:33 +0002
 
129
# source_branch: http://example.org
 
130
# base_revision_id: null:
 
131
# message: Hi mom!
 
132
#\x20
 
133
# Begin patch
 
134
booga""".splitlines(True)
 
135
 
 
136
 
 
137
OLD_DIRECTIVE_2 = """# Bazaar merge directive format 2 (Bazaar 0.19)
 
138
# revision_id: abentley@panoramicfeedback.com-20070807234458-\
 
139
#   nzhkoyza56lan7z5
 
140
# target_branch: http://panoramicfeedback.com/opensource/bzr/repo\
 
141
#   /bzr.ab
 
142
# testament_sha1: d825a5cdb267a90ec2ba86b00895f3d8a9bed6bf
 
143
# timestamp: 2007-08-10 16:15:02 -0400
 
144
# source_branch: http://panoramicfeedback.com/opensource/bzr/repo\
 
145
#   /bzr.ab
 
146
# base_revision_id: abentley@panoramicfeedback.com-20070731163346-\
 
147
#   623xwcycwij91xen
 
148
#
 
149
""".splitlines(True)
 
150
 
 
151
 
 
152
class TestMergeDirective(object):
 
153
 
 
154
    def test_merge_source(self):
 
155
        time = 500000.0
 
156
        timezone = 5 * 3600
 
157
        self.assertRaises(errors.NoMergeSource, self.make_merge_directive,
 
158
            'example:', 'sha', time, timezone, 'http://example.com')
 
159
        self.assertRaises(errors.NoMergeSource, self.make_merge_directive,
 
160
            'example:', 'sha', time, timezone, 'http://example.com',
 
161
            patch_type='diff')
 
162
        self.make_merge_directive('example:', 'sha', time, timezone,
 
163
            'http://example.com', source_branch='http://example.org')
 
164
        md = self.make_merge_directive('null:', 'sha', time, timezone,
 
165
            'http://example.com', patch='blah', patch_type='bundle')
 
166
        self.assertIs(None, md.source_branch)
 
167
        md2 = self.make_merge_directive('null:', 'sha', time, timezone,
 
168
            'http://example.com', patch='blah', patch_type='bundle',
 
169
            source_branch='bar')
 
170
        self.assertEqual('bar', md2.source_branch)
 
171
 
 
172
    def test_serialization(self):
 
173
        time = 453
 
174
        timezone = 120
 
175
        md = self.make_merge_directive('example:', 'sha', time, timezone,
 
176
            'http://example.com', patch='booga', patch_type='bundle')
 
177
        self.assertEqualDiff(self.OUTPUT1, ''.join(md.to_lines()))
 
178
        md = self.make_merge_directive('example:', 'sha', time, timezone,
 
179
            'http://example.com', source_branch="http://example.org",
 
180
            patch='booga', patch_type='diff', message="Hi mom!")
 
181
        self.assertEqualDiff(self.OUTPUT2, ''.join(md.to_lines()))
 
182
 
 
183
    def test_deserialize_junk(self):
 
184
        time = 501
 
185
        self.assertRaises(errors.NotAMergeDirective,
 
186
                          merge_directive.MergeDirective.from_lines, 'lala')
 
187
 
 
188
    def test_deserialize_empty(self):
 
189
        self.assertRaises(errors.NotAMergeDirective,
 
190
                          merge_directive.MergeDirective.from_lines, [])
 
191
 
 
192
    def test_deserialize_leading_junk(self):
 
193
        md = merge_directive.MergeDirective.from_lines(self.INPUT1)
 
194
        self.assertEqual('example:', md.revision_id)
 
195
        self.assertEqual('sha', md.testament_sha1)
 
196
        self.assertEqual('http://example.com', md.target_branch)
 
197
        self.assertEqual('http://example.org', md.source_branch)
 
198
        self.assertEqual(453, md.time)
 
199
        self.assertEqual(120, md.timezone)
 
200
        self.assertEqual('booga', md.patch)
 
201
        self.assertEqual('diff', md.patch_type)
 
202
        self.assertEqual('Hi mom!', md.message)
 
203
 
 
204
    def test_roundtrip(self):
 
205
        time = 500000
 
206
        timezone = 7.5 * 3600
 
207
        md = self.make_merge_directive('example:', 'sha', time, timezone,
 
208
            'http://example.com', source_branch="http://example.org",
 
209
            patch='booga', patch_type='diff')
 
210
        md2 = merge_directive.MergeDirective.from_lines(md.to_lines())
 
211
        self.assertEqual('example:', md2.revision_id)
 
212
        self.assertIsInstance(md2.revision_id, str)
 
213
        self.assertEqual('sha', md2.testament_sha1)
 
214
        self.assertEqual('http://example.com', md2.target_branch)
 
215
        self.assertEqual('http://example.org', md2.source_branch)
 
216
        self.assertEqual(time, md2.time)
 
217
        self.assertEqual(timezone, md2.timezone)
 
218
        self.assertEqual('diff', md2.patch_type)
 
219
        self.assertEqual('booga', md2.patch)
 
220
        self.assertEqual(None, md2.message)
 
221
        self.set_bundle(md, "# Bazaar revision bundle v0.9\n#\n")
 
222
        md.message = "Hi mom!"
 
223
        lines = md.to_lines()
 
224
        md3 = merge_directive.MergeDirective.from_lines(lines)
 
225
        self.assertEqual("# Bazaar revision bundle v0.9\n#\n", md3.bundle)
 
226
        self.assertEqual("bundle", md3.patch_type)
 
227
        self.assertContainsRe(md3.to_lines()[0],
 
228
            '^# Bazaar merge directive format ')
 
229
        self.assertEqual("Hi mom!", md3.message)
 
230
        md3.clear_payload()
 
231
        self.assertIs(None, md3.get_raw_bundle())
 
232
        md4 = merge_directive.MergeDirective.from_lines(md3.to_lines())
 
233
        self.assertIs(None, md4.patch_type)
 
234
 
 
235
 
 
236
class TestMergeDirective1(tests.TestCase, TestMergeDirective):
 
237
    """Test merge directive format 1"""
 
238
 
 
239
    INPUT1 = INPUT1
 
240
 
 
241
    OUTPUT1 = OUTPUT1
 
242
 
 
243
    OUTPUT2 = OUTPUT2
 
244
 
 
245
    def make_merge_directive(self, revision_id, testament_sha1, time, timezone,
 
246
                 target_branch, patch=None, patch_type=None,
 
247
                 source_branch=None, message=None):
 
248
        return merge_directive.MergeDirective(revision_id, testament_sha1,
 
249
                 time, timezone, target_branch, patch, patch_type,
 
250
                 source_branch, message)
 
251
 
 
252
    @staticmethod
 
253
    def set_bundle(md, value):
 
254
        md.patch = value
 
255
 
 
256
    def test_require_patch(self):
 
257
        time = 500.0
 
258
        timezone = 120
 
259
        self.assertRaises(errors.PatchMissing, merge_directive.MergeDirective,
 
260
            'example:', 'sha', time, timezone, 'http://example.com',
 
261
            patch_type='bundle')
 
262
        md = merge_directive.MergeDirective('example:', 'sha1', time, timezone,
 
263
            'http://example.com', source_branch="http://example.org",
 
264
            patch='', patch_type='diff')
 
265
        self.assertEqual(md.patch, '')
 
266
 
 
267
 
 
268
class TestMergeDirective2(tests.TestCase, TestMergeDirective):
 
269
    """Test merge directive format 2"""
 
270
 
 
271
    INPUT1 = INPUT1_2
 
272
 
 
273
    OUTPUT1 = OUTPUT1_2
 
274
 
 
275
    OUTPUT2 = OUTPUT2_2
 
276
 
 
277
    def make_merge_directive(self, revision_id, testament_sha1, time, timezone,
 
278
                 target_branch, patch=None, patch_type=None,
 
279
                 source_branch=None, message=None, base_revision_id='null:'):
 
280
        if patch_type == 'bundle':
 
281
            bundle = patch
 
282
            patch = None
 
283
        else:
 
284
            bundle = None
 
285
        return merge_directive.MergeDirective2(revision_id, testament_sha1,
 
286
            time, timezone, target_branch, patch, source_branch, message,
 
287
            bundle, base_revision_id)
 
288
 
 
289
    @staticmethod
 
290
    def set_bundle(md, value):
 
291
        md.bundle = value
 
292
 
 
293
 
 
294
EMAIL1 = """From: "J. Random Hacker" <jrandom@example.com>
 
295
Subject: Commit of rev2a
 
296
To: pqm@example.com
 
297
User-Agent: Bazaar \(.*\)
 
298
 
 
299
# Bazaar merge directive format 1
 
300
# revision_id: rev2a
 
301
# target_branch: (.|\n)*
 
302
# testament_sha1: .*
 
303
# timestamp: 1970-01-01 00:08:56 \\+0001
 
304
# source_branch: (.|\n)*
 
305
"""
 
306
 
 
307
 
 
308
EMAIL1_2 = """From: "J. Random Hacker" <jrandom@example.com>
 
309
Subject: Commit of rev2a
 
310
To: pqm@example.com
 
311
User-Agent: Bazaar \(.*\)
 
312
 
 
313
# Bazaar merge directive format 2 \\(Bazaar 0.90\\)
 
314
# revision_id: rev2a
 
315
# target_branch: (.|\n)*
 
316
# testament_sha1: .*
 
317
# timestamp: 1970-01-01 00:08:56 \\+0001
 
318
# source_branch: (.|\n)*
 
319
"""
 
320
 
 
321
 
 
322
EMAIL2 = """From: "J. Random Hacker" <jrandom@example.com>
 
323
Subject: Commit of rev2a with special message
 
324
To: pqm@example.com
 
325
User-Agent: Bazaar \(.*\)
 
326
 
 
327
# Bazaar merge directive format 1
 
328
# revision_id: rev2a
 
329
# target_branch: (.|\n)*
 
330
# testament_sha1: .*
 
331
# timestamp: 1970-01-01 00:08:56 \\+0001
 
332
# source_branch: (.|\n)*
 
333
# message: Commit of rev2a with special message
 
334
"""
 
335
 
 
336
EMAIL2_2 = """From: "J. Random Hacker" <jrandom@example.com>
 
337
Subject: Commit of rev2a with special message
 
338
To: pqm@example.com
 
339
User-Agent: Bazaar \(.*\)
 
340
 
 
341
# Bazaar merge directive format 2 \\(Bazaar 0.90\\)
 
342
# revision_id: rev2a
 
343
# target_branch: (.|\n)*
 
344
# testament_sha1: .*
 
345
# timestamp: 1970-01-01 00:08:56 \\+0001
 
346
# source_branch: (.|\n)*
 
347
# message: Commit of rev2a with special message
 
348
"""
 
349
 
 
350
class TestMergeDirectiveBranch(object):
 
351
 
 
352
    def make_trees(self):
 
353
        tree_a = self.make_branch_and_tree('tree_a')
 
354
        tree_a.branch.get_config().set_user_option('email',
 
355
            'J. Random Hacker <jrandom@example.com>')
 
356
        self.build_tree_contents([('tree_a/file', 'content_a\ncontent_b\n'),
 
357
                                  ('tree_a/file_2', 'content_x\rcontent_y\r')])
 
358
        tree_a.add(['file', 'file_2'])
 
359
        tree_a.commit('message', rev_id='rev1')
 
360
        tree_b = tree_a.bzrdir.sprout('tree_b').open_workingtree()
 
361
        branch_c = tree_a.bzrdir.sprout('branch_c').open_branch()
 
362
        tree_b.commit('message', rev_id='rev2b')
 
363
        self.build_tree_contents([('tree_a/file', 'content_a\ncontent_c \n'),
 
364
                                  ('tree_a/file_2', 'content_x\rcontent_z\r')])
 
365
        tree_a.commit('Commit of rev2a', rev_id='rev2a')
 
366
        return tree_a, tree_b, branch_c
 
367
 
 
368
    def test_empty_target(self):
 
369
        tree_a, tree_b, branch_c = self.make_trees()
 
370
        tree_d = self.make_branch_and_tree('tree_d')
 
371
        md2 = self.from_objects(tree_a.branch.repository, 'rev2a', 500, 120,
 
372
            tree_d.branch.base, patch_type='diff',
 
373
            public_branch=tree_a.branch.base)
 
374
 
 
375
    def test_disk_name(self):
 
376
        tree_a, tree_b, branch_c = self.make_trees()
 
377
        tree_a.branch.nick = 'fancy <name>'
 
378
        md = self.from_objects(tree_a.branch.repository, 'rev2a', 500, 120,
 
379
            tree_b.branch.base)
 
380
        self.assertEqual('fancy-name-2', md.get_disk_name(tree_a.branch))
 
381
 
 
382
    def test_disk_name_old_revno(self):
 
383
        tree_a, tree_b, branch_c = self.make_trees()
 
384
        tree_a.branch.nick = 'fancy-name'
 
385
        md = self.from_objects(tree_a.branch.repository, 'rev1', 500, 120,
 
386
            tree_b.branch.base)
 
387
        self.assertEqual('fancy-name-1', md.get_disk_name(tree_a.branch))
 
388
 
 
389
    def test_generate_patch(self):
 
390
        tree_a, tree_b, branch_c = self.make_trees()
 
391
        md2 = self.from_objects(tree_a.branch.repository, 'rev2a', 500, 120,
 
392
            tree_b.branch.base, patch_type='diff',
 
393
            public_branch=tree_a.branch.base)
 
394
        self.assertNotContainsRe(md2.patch, 'Bazaar revision bundle')
 
395
        self.assertContainsRe(md2.patch, '\\+content_c')
 
396
        self.assertNotContainsRe(md2.patch, '\\+\\+\\+ b/')
 
397
        self.assertContainsRe(md2.patch, '\\+\\+\\+ file')
 
398
 
 
399
    def test_public_branch(self):
 
400
        tree_a, tree_b, branch_c = self.make_trees()
 
401
        self.assertRaises(errors.PublicBranchOutOfDate,
 
402
            self.from_objects, tree_a.branch.repository, 'rev2a', 500, 144,
 
403
            tree_b.branch.base, public_branch=branch_c.base, patch_type='diff')
 
404
        self.assertRaises(errors.PublicBranchOutOfDate,
 
405
            self.from_objects, tree_a.branch.repository, 'rev2a', 500, 144,
 
406
            tree_b.branch.base, public_branch=branch_c.base, patch_type=None)
 
407
        # public branch is not checked if patch format is bundle.
 
408
        md1 = self.from_objects(tree_a.branch.repository, 'rev2a', 500, 144,
 
409
            tree_b.branch.base, public_branch=branch_c.base)
 
410
        # public branch is provided with a bundle, despite possibly being out
 
411
        # of date, because it's not required if a bundle is present.
 
412
        self.assertEqual(md1.source_branch, branch_c.base)
 
413
        # Once we update the public branch, we can generate a diff.
 
414
        branch_c.pull(tree_a.branch)
 
415
        md3 = self.from_objects(tree_a.branch.repository, 'rev2a', 500, 144,
 
416
            tree_b.branch.base, patch_type=None, public_branch=branch_c.base)
 
417
 
 
418
    def test_use_public_submit_branch(self):
 
419
        tree_a, tree_b, branch_c = self.make_trees()
 
420
        branch_c.pull(tree_a.branch)
 
421
        md = self.from_objects(tree_a.branch.repository, 'rev2a', 500, 144,
 
422
            tree_b.branch.base, patch_type=None, public_branch=branch_c.base)
 
423
        self.assertEqual(md.target_branch, tree_b.branch.base)
 
424
        tree_b.branch.set_public_branch('http://example.com')
 
425
        md2 = self.from_objects(
 
426
              tree_a.branch.repository, 'rev2a', 500, 144, tree_b.branch.base,
 
427
              patch_type=None, public_branch=branch_c.base)
 
428
        self.assertEqual(md2.target_branch, 'http://example.com')
 
429
 
 
430
    def test_message(self):
 
431
        tree_a, tree_b, branch_c = self.make_trees()
 
432
        md3 = self.from_objects(tree_a.branch.repository, 'rev1', 500, 120,
 
433
            tree_b.branch.base, patch_type=None, public_branch=branch_c.base,
 
434
            message='Merge message')
 
435
        md3.to_lines()
 
436
        self.assertIs(None, md3.patch)
 
437
        self.assertEqual('Merge message', md3.message)
 
438
 
 
439
    def test_generate_bundle(self):
 
440
        tree_a, tree_b, branch_c = self.make_trees()
 
441
        md1 = self.from_objects(tree_a.branch.repository, 'rev2a', 500, 120,
 
442
            tree_b.branch.base, public_branch=branch_c.base)
 
443
 
 
444
        self.assertContainsRe(md1.get_raw_bundle(), 'Bazaar revision bundle')
 
445
        self.assertContainsRe(md1.patch, '\\+content_c')
 
446
        self.assertNotContainsRe(md1.patch, '\\+content_a')
 
447
        self.assertContainsRe(md1.patch, '\\+content_c')
 
448
        self.assertNotContainsRe(md1.patch, '\\+content_a')
 
449
 
 
450
    def test_broken_bundle(self):
 
451
        tree_a, tree_b, branch_c = self.make_trees()
 
452
        md1 = self.from_objects(tree_a.branch.repository, 'rev2a', 500, 120,
 
453
            tree_b.branch.base, public_branch=branch_c.base)
 
454
        lines = md1.to_lines()
 
455
        lines = [l.replace('\n', '\r\n') for l in lines]
 
456
        md2 = merge_directive.MergeDirective.from_lines(lines)
 
457
        self.assertEqual('rev2a', md2.revision_id)
 
458
 
 
459
    def test_signing(self):
 
460
        time = 453
 
461
        timezone = 7200
 
462
        class FakeBranch(object):
 
463
            def get_config(self):
 
464
                return self
 
465
            def gpg_signing_command(self):
 
466
                return 'loopback'
 
467
        md = self.make_merge_directive('example:', 'sha', time, timezone,
 
468
            'http://example.com', source_branch="http://example.org",
 
469
            patch='booga', patch_type='diff')
 
470
        old_strategy = gpg.GPGStrategy
 
471
        gpg.GPGStrategy = gpg.LoopbackGPGStrategy
 
472
        try:
 
473
            signed = md.to_signed(FakeBranch())
 
474
        finally:
 
475
            gpg.GPGStrategy = old_strategy
 
476
        self.assertContainsRe(signed, '^-----BEGIN PSEUDO-SIGNED CONTENT')
 
477
        self.assertContainsRe(signed, 'example.org')
 
478
        self.assertContainsRe(signed, 'booga')
 
479
 
 
480
    def test_email(self):
 
481
        tree_a, tree_b, branch_c = self.make_trees()
 
482
        md = self.from_objects(tree_a.branch.repository, 'rev2a', 476, 60,
 
483
            tree_b.branch.base, patch_type=None,
 
484
            public_branch=tree_a.branch.base)
 
485
        message = md.to_email('pqm@example.com', tree_a.branch)
 
486
        self.assertContainsRe(message.as_string(), self.EMAIL1)
 
487
        md.message = 'Commit of rev2a with special message'
 
488
        message = md.to_email('pqm@example.com', tree_a.branch)
 
489
        self.assertContainsRe(message.as_string(), self.EMAIL2)
 
490
 
 
491
    def test_install_revisions_branch(self):
 
492
        tree_a, tree_b, branch_c = self.make_trees()
 
493
        md = self.from_objects(tree_a.branch.repository, 'rev2a', 500, 36,
 
494
            tree_b.branch.base, patch_type=None,
 
495
            public_branch=tree_a.branch.base)
 
496
        self.assertFalse(tree_b.branch.repository.has_revision('rev2a'))
 
497
        revision = md.install_revisions(tree_b.branch.repository)
 
498
        self.assertEqual('rev2a', revision)
 
499
        self.assertTrue(tree_b.branch.repository.has_revision('rev2a'))
 
500
 
 
501
    def test_get_merge_request(self):
 
502
        tree_a, tree_b, branch_c = self.make_trees()
 
503
        md = self.from_objects(tree_a.branch.repository, 'rev2a', 500, 36,
 
504
            tree_b.branch.base, patch_type='bundle',
 
505
            public_branch=tree_a.branch.base)
 
506
        self.assertFalse(tree_b.branch.repository.has_revision('rev2a'))
 
507
        md.install_revisions(tree_b.branch.repository)
 
508
        base, revision, verified = md.get_merge_request(
 
509
            tree_b.branch.repository)
 
510
        if isinstance(md, merge_directive.MergeDirective):
 
511
            self.assertIs(None, base)
 
512
            self.assertEqual('inapplicable', verified)
 
513
        else:
 
514
            self.assertEqual('rev1', base)
 
515
            self.assertEqual('verified', verified)
 
516
        self.assertEqual('rev2a', revision)
 
517
        self.assertTrue(tree_b.branch.repository.has_revision('rev2a'))
 
518
        md = self.from_objects(tree_a.branch.repository, 'rev2a', 500, 36,
 
519
            tree_b.branch.base, patch_type=None,
 
520
            public_branch=tree_a.branch.base)
 
521
        base, revision, verified = md.get_merge_request(
 
522
            tree_b.branch.repository)
 
523
        if isinstance(md, merge_directive.MergeDirective):
 
524
            self.assertIs(None, base)
 
525
            self.assertEqual('inapplicable', verified)
 
526
        else:
 
527
            self.assertEqual('rev1', base)
 
528
            self.assertEqual('inapplicable', verified)
 
529
        md = self.from_objects(tree_a.branch.repository, 'rev2a', 500, 36,
 
530
            tree_b.branch.base, patch_type='diff',
 
531
            public_branch=tree_a.branch.base)
 
532
        base, revision, verified = md.get_merge_request(
 
533
            tree_b.branch.repository)
 
534
        if isinstance(md, merge_directive.MergeDirective):
 
535
            self.assertIs(None, base)
 
536
            self.assertEqual('inapplicable', verified)
 
537
        else:
 
538
            self.assertEqual('rev1', base)
 
539
            self.assertEqual('verified', verified)
 
540
        md.patch='asdf'
 
541
        base, revision, verified = md.get_merge_request(
 
542
            tree_b.branch.repository)
 
543
        if isinstance(md, merge_directive.MergeDirective):
 
544
            self.assertIs(None, base)
 
545
            self.assertEqual('inapplicable', verified)
 
546
        else:
 
547
            self.assertEqual('rev1', base)
 
548
            self.assertEqual('failed', verified)
 
549
 
 
550
    def test_install_revisions_bundle(self):
 
551
        tree_a, tree_b, branch_c = self.make_trees()
 
552
        md = self.from_objects(tree_a.branch.repository, 'rev2a', 500, 36,
 
553
            tree_b.branch.base, patch_type='bundle',
 
554
            public_branch=tree_a.branch.base)
 
555
        self.assertFalse(tree_b.branch.repository.has_revision('rev2a'))
 
556
        revision = md.install_revisions(tree_b.branch.repository)
 
557
        self.assertEqual('rev2a', revision)
 
558
        self.assertTrue(tree_b.branch.repository.has_revision('rev2a'))
 
559
 
 
560
    def test_get_target_revision_nofetch(self):
 
561
        tree_a, tree_b, branch_c = self.make_trees()
 
562
        tree_b.branch.fetch(tree_a.branch)
 
563
        md = self.from_objects( tree_a.branch.repository, 'rev2a', 500, 36,
 
564
            tree_b.branch.base, patch_type=None,
 
565
            public_branch=tree_a.branch.base)
 
566
        md.source_branch = '/dev/null'
 
567
        revision = md.install_revisions(tree_b.branch.repository)
 
568
        self.assertEqual('rev2a', revision)
 
569
 
 
570
    def test_use_submit_for_missing_dependency(self):
 
571
        tree_a, tree_b, branch_c = self.make_trees()
 
572
        branch_c.pull(tree_a.branch)
 
573
        self.build_tree_contents([('tree_a/file', 'content_q\ncontent_r\n')])
 
574
        tree_a.commit('rev3a', rev_id='rev3a')
 
575
        md = self.from_objects(tree_a.branch.repository, 'rev3a', 500, 36,
 
576
            branch_c.base, base_revision_id='rev2a')
 
577
        revision = md.install_revisions(tree_b.branch.repository)
 
578
 
 
579
    def test_handle_target_not_a_branch(self):
 
580
        tree_a, tree_b, branch_c = self.make_trees()
 
581
        branch_c.pull(tree_a.branch)
 
582
        self.build_tree_contents([('tree_a/file', 'content_q\ncontent_r\n')])
 
583
        tree_a.commit('rev3a', rev_id='rev3a')
 
584
        md = self.from_objects(tree_a.branch.repository, 'rev3a', 500, 36,
 
585
            branch_c.base, base_revision_id='rev2a')
 
586
        md.target_branch = self.get_url('not-a-branch')
 
587
        self.assertRaises(errors.TargetNotBranch, md.install_revisions,
 
588
                tree_b.branch.repository)
 
589
 
 
590
 
 
591
class TestMergeDirective1Branch(tests.TestCaseWithTransport,
 
592
    TestMergeDirectiveBranch):
 
593
    """Test merge directive format 1 with a branch"""
 
594
 
 
595
    EMAIL1 = EMAIL1
 
596
 
 
597
    EMAIL2 = EMAIL2
 
598
 
 
599
    def from_objects(self, repository, revision_id, time, timezone,
 
600
        target_branch, patch_type='bundle', local_target_branch=None,
 
601
        public_branch=None, message=None, base_revision_id=None):
 
602
        if base_revision_id is not None:
 
603
            raise tests.TestNotApplicable('This format does not support'
 
604
                                          ' explicit bases.')
 
605
        repository.lock_write()
 
606
        try:
 
607
            return merge_directive.MergeDirective.from_objects( repository,
 
608
                revision_id, time, timezone, target_branch, patch_type,
 
609
                local_target_branch, public_branch, message)
 
610
        finally:
 
611
            repository.unlock()
 
612
 
 
613
    def make_merge_directive(self, revision_id, testament_sha1, time, timezone,
 
614
                 target_branch, patch=None, patch_type=None,
 
615
                 source_branch=None, message=None):
 
616
        return merge_directive.MergeDirective(revision_id, testament_sha1,
 
617
                 time, timezone, target_branch, patch, patch_type,
 
618
                 source_branch, message)
 
619
 
 
620
 
 
621
class TestMergeDirective2Branch(tests.TestCaseWithTransport,
 
622
    TestMergeDirectiveBranch):
 
623
    """Test merge directive format 2 with a branch"""
 
624
 
 
625
    EMAIL1 = EMAIL1_2
 
626
 
 
627
    EMAIL2 = EMAIL2_2
 
628
 
 
629
    def from_objects(self, repository, revision_id, time, timezone,
 
630
        target_branch, patch_type='bundle', local_target_branch=None,
 
631
        public_branch=None, message=None, base_revision_id=None):
 
632
        include_patch = (patch_type in ('bundle', 'diff'))
 
633
        include_bundle = (patch_type == 'bundle')
 
634
        self.assertTrue(patch_type in ('bundle', 'diff', None))
 
635
        return merge_directive.MergeDirective2.from_objects(
 
636
            repository, revision_id, time, timezone, target_branch,
 
637
            include_patch, include_bundle, local_target_branch, public_branch,
 
638
            message, base_revision_id)
 
639
 
 
640
    def make_merge_directive(self, revision_id, testament_sha1, time, timezone,
 
641
                 target_branch, patch=None, patch_type=None,
 
642
                 source_branch=None, message=None, base_revision_id='null:'):
 
643
        if patch_type == 'bundle':
 
644
            bundle = patch
 
645
            patch = None
 
646
        else:
 
647
            bundle = None
 
648
        return merge_directive.MergeDirective2(revision_id, testament_sha1,
 
649
            time, timezone, target_branch, patch, source_branch, message,
 
650
            bundle, base_revision_id)
 
651
 
 
652
    def test_base_revision(self):
 
653
        tree_a, tree_b, branch_c = self.make_trees()
 
654
        md = self.from_objects(tree_a.branch.repository, 'rev2a', 500, 60,
 
655
            tree_b.branch.base, patch_type='bundle',
 
656
            public_branch=tree_a.branch.base, base_revision_id=None)
 
657
        self.assertEqual('rev1', md.base_revision_id)
 
658
        md = self.from_objects(tree_a.branch.repository, 'rev2a', 500, 60,
 
659
            tree_b.branch.base, patch_type='bundle',
 
660
            public_branch=tree_a.branch.base, base_revision_id='null:')
 
661
        self.assertEqual('null:', md.base_revision_id)
 
662
        lines = md.to_lines()
 
663
        md2 = merge_directive.MergeDirective.from_lines(lines)
 
664
        self.assertEqual(md2.base_revision_id, md.base_revision_id)
 
665
 
 
666
    def test_patch_verification(self):
 
667
        tree_a, tree_b, branch_c = self.make_trees()
 
668
        md = self.from_objects(tree_a.branch.repository, 'rev2a', 500, 60,
 
669
            tree_b.branch.base, patch_type='bundle',
 
670
            public_branch=tree_a.branch.base)
 
671
        lines = md.to_lines()
 
672
        md2 = merge_directive.MergeDirective.from_lines(lines)
 
673
        md2._verify_patch(tree_a.branch.repository)
 
674
        # Strip trailing whitespace
 
675
        md2.patch = md2.patch.replace(' \n', '\n')
 
676
        md2._verify_patch(tree_a.branch.repository)
 
677
        # Convert to Mac line-endings
 
678
        md2.patch = re.sub('(\r\n|\r|\n)', '\r', md2.patch)
 
679
        self.assertTrue(md2._verify_patch(tree_a.branch.repository))
 
680
        # Convert to DOS line-endings
 
681
        md2.patch = re.sub('(\r\n|\r|\n)', '\r\n', md2.patch)
 
682
        self.assertTrue(md2._verify_patch(tree_a.branch.repository))
 
683
        md2.patch = md2.patch.replace('content_c', 'content_d')
 
684
        self.assertFalse(md2._verify_patch(tree_a.branch.repository))
 
685
 
 
686
 
 
687
class TestParseOldMergeDirective2(tests.TestCase):
 
688
 
 
689
    def test_parse_old_merge_directive(self):
 
690
        md = merge_directive.MergeDirective.from_lines(INPUT1_2_OLD)
 
691
        self.assertEqual('example:', md.revision_id)
 
692
        self.assertEqual('sha', md.testament_sha1)
 
693
        self.assertEqual('http://example.com', md.target_branch)
 
694
        self.assertEqual('http://example.org', md.source_branch)
 
695
        self.assertEqual(453, md.time)
 
696
        self.assertEqual(120, md.timezone)
 
697
        self.assertEqual('booga', md.patch)
 
698
        self.assertEqual('diff', md.patch_type)
 
699
        self.assertEqual('Hi mom!', md.message)