1# Copyright (C) 2007, 2009-2012 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 17import os 18import smtplib 19 20from breezy import ( 21 gpg, 22 merge_directive, 23 tests, 24 workingtree, 25 ) 26 27 28EMAIL1 = """From: "J. Random Hacker" <jrandom@example.com> 29Subject: bar 30To: pqm@example.com 31User-Agent: Bazaar \\(.*\\) 32 33# Bazaar merge directive format 2 \\(Bazaar 0.90\\) 34# revision_id: bar-id 35# target_branch: ../tree2 36# testament_sha1: .* 37# timestamp: .* 38# source_branch: . 39#""" 40 41 42class TestMergeDirective(tests.TestCaseWithTransport): 43 44 def prepare_merge_directive(self): 45 self.tree1 = self.make_branch_and_tree('tree1') 46 self.build_tree_contents([('tree1/file', b'a\nb\nc\nd\n')]) 47 self.tree1.branch.get_config_stack().set( 48 'email', 'J. Random Hacker <jrandom@example.com>') 49 self.tree1.add('file') 50 self.tree1.commit('foo', rev_id=b'foo-id') 51 self.tree2 = self.tree1.controldir.sprout('tree2').open_workingtree() 52 self.build_tree_contents([('tree1/file', b'a\nb\nc\nd\ne\n')]) 53 self.tree1.commit('bar', rev_id=b'bar-id') 54 os.chdir('tree1') 55 return self.tree1, self.tree2 56 57 def test_merge_directive(self): 58 self.prepare_merge_directive() 59 md_text = self.run_bzr('merge-directive ../tree2')[0] 60 self.assertContainsRe(md_text, "\\+e") 61 md_text = self.run_bzr('merge-directive -r -2 ../tree2')[0] 62 self.assertNotContainsRe(md_text, "\\+e") 63 md_text = self.run_bzr( 64 'merge-directive -r -1..-2 ../tree2')[0].encode('utf-8') 65 md2 = merge_directive.MergeDirective.from_lines( 66 md_text.splitlines(True)) 67 self.assertEqual(b'foo-id', md2.revision_id) 68 self.assertEqual(b'bar-id', md2.base_revision_id) 69 70 def test_submit_branch(self): 71 self.prepare_merge_directive() 72 self.run_bzr_error(('No submit branch',), 'merge-directive', retcode=3) 73 self.run_bzr('merge-directive ../tree2') 74 75 def test_public_branch(self): 76 self.prepare_merge_directive() 77 self.run_bzr_error(('No public branch',), 78 'merge-directive --diff ../tree2', retcode=3) 79 md_text = self.run_bzr('merge-directive ../tree2')[0] 80 self.assertNotContainsRe(md_text, 'source_branch:') 81 self.run_bzr('merge-directive --diff ../tree2 .') 82 self.run_bzr('merge-directive --diff')[0] 83 self.assertNotContainsRe(md_text, 'source_branch:') 84 85 def test_patch_types(self): 86 self.prepare_merge_directive() 87 md_text = self.run_bzr('merge-directive ../tree2')[0] 88 self.assertContainsRe(md_text, "# Begin bundle") 89 self.assertContainsRe(md_text, "\\+e") 90 md_text = self.run_bzr('merge-directive ../tree2 --diff .')[0] 91 self.assertNotContainsRe(md_text, "# Begin bundle") 92 self.assertContainsRe(md_text, "\\+e") 93 md_text = self.run_bzr('merge-directive --plain')[0] 94 self.assertNotContainsRe(md_text, "\\+e") 95 96 def test_message(self): 97 self.prepare_merge_directive() 98 md_text = self.run_bzr('merge-directive ../tree2')[0] 99 self.assertNotContainsRe(md_text, 'message: Message for merge') 100 md_text = self.run_bzr('merge-directive -m Message_for_merge')[0] 101 self.assertContainsRe(md_text, 'message: Message_for_merge') 102 103 def test_signing(self): 104 self.prepare_merge_directive() 105 old_strategy = gpg.GPGStrategy 106 gpg.GPGStrategy = gpg.LoopbackGPGStrategy 107 try: 108 md_text = self.run_bzr('merge-directive --sign ../tree2')[0] 109 finally: 110 gpg.GPGStrategy = old_strategy 111 self.assertContainsRe(md_text, '^-----BEGIN PSEUDO-SIGNED CONTENT') 112 113 def run_bzr_fakemail(self, *args, **kwargs): 114 sendmail_calls = [] 115 116 def sendmail(self, from_, to, message): 117 sendmail_calls.append((self, from_, to, message)) 118 connect_calls = [] 119 120 def connect(self, host='localhost', port=0): 121 connect_calls.append((self, host, port)) 122 123 def has_extn(self, extension): 124 return False 125 126 def ehlo(self): 127 return (200, 'Ok') 128 old_sendmail = smtplib.SMTP.sendmail 129 smtplib.SMTP.sendmail = sendmail 130 old_connect = smtplib.SMTP.connect 131 smtplib.SMTP.connect = connect 132 old_ehlo = smtplib.SMTP.ehlo 133 smtplib.SMTP.ehlo = ehlo 134 old_has_extn = smtplib.SMTP.has_extn 135 smtplib.SMTP.has_extn = has_extn 136 try: 137 result = self.run_bzr(*args, **kwargs) 138 finally: 139 smtplib.SMTP.sendmail = old_sendmail 140 smtplib.SMTP.connect = old_connect 141 smtplib.SMTP.ehlo = old_ehlo 142 smtplib.SMTP.has_extn = old_has_extn 143 return result + (connect_calls, sendmail_calls) 144 145 def test_mail_default(self): 146 tree1, tree2 = self.prepare_merge_directive() 147 md_text, errr, connect_calls, sendmail_calls =\ 148 self.run_bzr_fakemail(['merge-directive', '--mail-to', 149 'pqm@example.com', '--plain', '../tree2', 150 '.']) 151 self.assertEqual('', md_text) 152 self.assertEqual(1, len(connect_calls)) 153 call = connect_calls[0] 154 self.assertEqual(('localhost', 0), call[1:3]) 155 self.assertEqual(1, len(sendmail_calls)) 156 call = sendmail_calls[0] 157 self.assertEqual(('jrandom@example.com', ['pqm@example.com']), 158 call[1:3]) 159 self.assertContainsRe(call[3], EMAIL1) 160 161 def test_pull_raw(self): 162 self.prepare_merge_directive() 163 self.tree1.commit('baz', rev_id=b'baz-id') 164 md_text = self.run_bzr(['merge-directive', self.tree2.basedir, 165 '-r', '2', self.tree1.basedir, '--plain'])[0] 166 self.build_tree_contents([('../directive', md_text)]) 167 os.chdir('../tree2') 168 self.run_bzr('pull ../directive') 169 wt = workingtree.WorkingTree.open('.') 170 self.assertEqual(b'bar-id', wt.last_revision()) 171 172 def test_pull_user_r(self): 173 """If the user supplies -r, an error is emitted""" 174 self.prepare_merge_directive() 175 self.tree1.commit('baz', rev_id=b'baz-id') 176 md_text = self.run_bzr(['merge-directive', self.tree2.basedir, 177 self.tree1.basedir, '--plain'])[0] 178 self.build_tree_contents([('../directive', md_text)]) 179 os.chdir('../tree2') 180 self.run_bzr_error( 181 ('Cannot use -r with merge directives or bundles',), 182 'pull -r 2 ../directive') 183 184 def test_pull_bundle(self): 185 self.prepare_merge_directive() 186 self.tree1.commit('baz', rev_id=b'baz-id') 187 md_text = self.run_bzr(['merge-directive', self.tree2.basedir, 188 '-r', '2', '/dev/null', '--bundle'])[0] 189 self.build_tree_contents([('../directive', md_text)]) 190 os.chdir('../tree2') 191 self.run_bzr('pull ../directive') 192 wt = workingtree.WorkingTree.open('.') 193 self.assertEqual(b'bar-id', wt.last_revision()) 194 195 def test_merge_raw(self): 196 self.prepare_merge_directive() 197 self.tree1.commit('baz', rev_id=b'baz-id') 198 md_text = self.run_bzr(['merge-directive', self.tree2.basedir, 199 '-r', '2', self.tree1.basedir, '--plain'])[0] 200 self.build_tree_contents([('../directive', md_text)]) 201 os.chdir('../tree2') 202 self.run_bzr('merge ../directive') 203 wt = workingtree.WorkingTree.open('.') 204 self.assertEqual(b'bar-id', wt.get_parent_ids()[1]) 205 206 def test_merge_user_r(self): 207 """If the user supplies -r, an error is emitted""" 208 self.prepare_merge_directive() 209 self.tree1.commit('baz', rev_id=b'baz-id') 210 md_text = self.run_bzr(['merge-directive', self.tree2.basedir, 211 self.tree1.basedir, '--plain'])[0] 212 self.build_tree_contents([('../directive', md_text)]) 213 os.chdir('../tree2') 214 self.run_bzr_error( 215 ('Cannot use -r with merge directives or bundles',), 216 'merge -r 2 ../directive') 217 218 def test_merge_bundle(self): 219 self.prepare_merge_directive() 220 self.tree1.commit('baz', rev_id=b'baz-id') 221 md_text = self.run_bzr(['merge-directive', self.tree2.basedir, 222 '-r', '2', '/dev/null', '--bundle'])[0] 223 self.build_tree_contents([('../directive', md_text)]) 224 os.chdir('../tree2') 225 self.run_bzr('merge ../directive') 226 wt = workingtree.WorkingTree.open('.') 227 self.assertEqual(b'bar-id', wt.get_parent_ids()[1]) 228 229 def test_mail_uses_config(self): 230 tree1, tree2 = self.prepare_merge_directive() 231 br = tree1.branch 232 br.get_config_stack().set('smtp_server', 'bogushost') 233 md_text, errr, connect_calls, sendmail_calls =\ 234 self.run_bzr_fakemail('merge-directive --mail-to' 235 ' pqm@example.com --plain ../tree2 .') 236 call = connect_calls[0] 237 self.assertEqual(('bogushost', 0), call[1:3]) 238 239 def test_no_common_ancestor(self): 240 foo = self.make_branch_and_tree('foo') 241 foo.commit('rev1') 242 bar = self.make_branch_and_tree('bar') 243 self.run_bzr('merge-directive ../bar', working_dir='foo') 244 245 def test_no_commits(self): 246 foo = self.make_branch_and_tree('foo') 247 bar = self.make_branch_and_tree('bar') 248 self.run_bzr_error(('No revisions to bundle.', ), 249 'merge-directive ../bar', working_dir='foo') 250 251 def test_encoding_exact(self): 252 tree1, tree2 = self.prepare_merge_directive() 253 tree1.commit(u'messag\xe9') 254 self.run_bzr('merge-directive ../tree2') # no exception raised 255 256 def test_merge_directive_directory(self): 257 """Test --directory option""" 258 import re 259 re_timestamp = re.compile(r'^# timestamp: .*', re.M) 260 self.prepare_merge_directive() 261 md1 = self.run_bzr('merge-directive ../tree2')[0] 262 md1 = re_timestamp.sub('# timestamp: XXX', md1) 263 os.chdir('..') 264 md2 = self.run_bzr('merge-directive --directory tree1 tree2')[0] 265 md2 = re_timestamp.sub('# timestamp: XXX', md2) 266 self.assertEqualDiff(md1.replace('../tree2', 'tree2'), md2) 267