~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_msgeditor.py

  • Committer: John Arbash Meinel
  • Date: 2013-05-19 14:29:37 UTC
  • mfrom: (6437.63.9 2.5)
  • mto: (6437.63.10 2.5)
  • mto: This revision was merged to the branch mainline in revision 6575.
  • Revision ID: john@arbash-meinel.com-20130519142937-21ykz2n2y2f22za9
Merge in the actual 2.5 branch. It seems I failed before

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005 Canonical Ltd
 
1
# Copyright (C) 2005-2011 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
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
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
"""Test commit message editor.
18
18
"""
20
20
import os
21
21
import sys
22
22
 
23
 
from bzrlib.branch import Branch
24
 
from bzrlib.config import ensure_config_dir_exists, config_filename
25
 
import bzrlib.msgeditor 
26
 
from bzrlib.tests import TestCaseWithTransport, TestSkipped
 
23
from bzrlib import (
 
24
    commit,
 
25
    config,
 
26
    errors,
 
27
    msgeditor,
 
28
    osutils,
 
29
    tests,
 
30
    trace,
 
31
    )
 
32
from bzrlib.msgeditor import (
 
33
    make_commit_message_template_encoded,
 
34
    edit_commit_message_encoded
 
35
)
 
36
from bzrlib.tests import (
 
37
    features,
 
38
    TestCaseInTempDir,
 
39
    TestCaseWithTransport,
 
40
    TestNotApplicable,
 
41
    TestSkipped,
 
42
    multiply_tests,
 
43
    probe_bad_non_ascii,
 
44
    split_suite_by_re,
 
45
    )
 
46
from bzrlib.tests.EncodingAdapter import encoding_scenarios
27
47
from bzrlib.trace import mutter
28
48
 
29
49
 
 
50
def load_tests(standard_tests, module, loader):
 
51
    """Parameterize the test for tempfile creation with different encodings."""
 
52
    to_adapt, result = split_suite_by_re(standard_tests,
 
53
        "test__create_temp_file_with_commit_template_in_unicode_dir")
 
54
    return multiply_tests(to_adapt, encoding_scenarios, result)
 
55
 
 
56
 
30
57
class MsgEditorTest(TestCaseWithTransport):
31
58
 
32
59
    def make_uncommitted_tree(self):
41
68
                "filesystem encoding %s" % sys.getfilesystemencoding())
42
69
        working_tree.add(filename)
43
70
        return working_tree
44
 
    
 
71
 
45
72
    def test_commit_template(self):
46
73
        """Test building a commit message template"""
47
74
        working_tree = self.make_uncommitted_tree()
48
 
        template = bzrlib.msgeditor.make_commit_message_template(working_tree, None)
49
 
        self.assertEqualDiff(template,
50
 
u"""\
51
 
added:
52
 
  hell\u00d8
53
 
""")
54
 
 
55
 
    def setUp(self):
56
 
        super(MsgEditorTest, self).setUp()
57
 
        self._bzr_editor = os.environ.get('BZR_EDITOR', None)
58
 
 
59
 
    def tearDown(self):
60
 
        if self._bzr_editor is not None:
61
 
            os.environ['BZR_EDITOR'] = self._bzr_editor
62
 
        else:
63
 
            if os.environ.get('BZR_EDITOR', None) is not None:
64
 
                del os.environ['BZR_EDITOR']
65
 
        super(MsgEditorTest, self).tearDown()
66
 
 
67
 
    def test_run_editor(self):
 
75
        template = msgeditor.make_commit_message_template(working_tree,
 
76
                                                                 None)
 
77
        self.assertEqualDiff(template,
 
78
u"""\
 
79
added:
 
80
  hell\u00d8
 
81
""")
 
82
 
 
83
    def make_multiple_pending_tree(self):
 
84
        from bzrlib import config
 
85
        config.GlobalConfig().set_user_option('email',
 
86
                                              'Bilbo Baggins <bb@hobbit.net>')
 
87
        tree = self.make_branch_and_tree('a')
 
88
        tree.commit('Initial checkin.', timestamp=1230912900, timezone=0)
 
89
        tree2 = tree.bzrdir.clone('b').open_workingtree()
 
90
        tree.commit('Minor tweak.', timestamp=1231977840, timezone=0)
 
91
        tree2.commit('Feature X work.', timestamp=1233186240, timezone=0)
 
92
        tree3 = tree2.bzrdir.clone('c').open_workingtree()
 
93
        tree2.commit('Feature X finished.', timestamp=1233187680, timezone=0)
 
94
        tree3.commit('Feature Y, based on initial X work.',
 
95
                     timestamp=1233285960, timezone=0)
 
96
        tree.merge_from_branch(tree2.branch)
 
97
        tree.merge_from_branch(tree3.branch, force=True)
 
98
        return tree
 
99
 
 
100
    def test_commit_template_pending_merges(self):
 
101
        """Test building a commit message template when there are pending
 
102
        merges.  The commit message should show all pending merge revisions,
 
103
        as does 'status -v', not only the merge tips.
 
104
        """
 
105
        working_tree = self.make_multiple_pending_tree()
 
106
        template = msgeditor.make_commit_message_template(working_tree, None)
 
107
        self.assertEqualDiff(template,
 
108
u"""\
 
109
pending merges:
 
110
  Bilbo Baggins 2009-01-29 Feature X finished.
 
111
    Bilbo Baggins 2009-01-28 Feature X work.
 
112
  Bilbo Baggins 2009-01-30 Feature Y, based on initial X work.
 
113
""")
 
114
 
 
115
    def test_commit_template_encoded(self):
 
116
        """Test building a commit message template"""
 
117
        working_tree = self.make_uncommitted_tree()
 
118
        template = make_commit_message_template_encoded(working_tree,
 
119
                                                        None,
 
120
                                                        output_encoding='utf8')
 
121
        self.assertEqualDiff(template,
 
122
u"""\
 
123
added:
 
124
  hell\u00d8
 
125
""".encode("utf8"))
 
126
 
 
127
 
 
128
    def test_commit_template_and_diff(self):
 
129
        """Test building a commit message template"""
 
130
        working_tree = self.make_uncommitted_tree()
 
131
        template = make_commit_message_template_encoded(working_tree,
 
132
                                                        None,
 
133
                                                        diff=True,
 
134
                                                        output_encoding='utf8')
 
135
 
 
136
        self.assertTrue("""\
 
137
@@ -0,0 +1,1 @@
 
138
+contents of hello
 
139
""" in template)
 
140
        self.assertTrue(u"""\
 
141
added:
 
142
  hell\u00d8
 
143
""".encode('utf8') in template)
 
144
 
 
145
    def make_do_nothing_editor(self, basename='fed'):
68
146
        if sys.platform == "win32":
69
 
            f = file('fed.bat', 'w')
 
147
            name = basename + '.bat'
 
148
            f = file(name, 'w')
70
149
            f.write('@rem dummy fed')
71
150
            f.close()
72
 
            os.environ['BZR_EDITOR'] = 'fed.bat'
 
151
            return name
73
152
        else:
74
 
            f = file('fed.sh', 'wb')
 
153
            name = basename + '.sh'
 
154
            f = file(name, 'wb')
75
155
            f.write('#!/bin/sh\n')
76
156
            f.close()
77
 
            os.chmod('fed.sh', 0755)
78
 
            os.environ['BZR_EDITOR'] = './fed.sh'
 
157
            os.chmod(name, 0755)
 
158
            return './' + name
79
159
 
80
 
        self.assertEqual(True, bzrlib.msgeditor._run_editor(''),
 
160
    def test_run_editor(self):
 
161
        self.overrideEnv('BZR_EDITOR', self.make_do_nothing_editor())
 
162
        self.assertEqual(True, msgeditor._run_editor(''),
81
163
                         'Unable to run dummy fake editor')
82
164
 
83
 
    def make_fake_editor(self):
 
165
    def test_parse_editor_name(self):
 
166
        """Correctly interpret names with spaces.
 
167
 
 
168
        See <https://bugs.launchpad.net/bzr/+bug/220331>
 
169
        """
 
170
        self.overrideEnv('BZR_EDITOR',
 
171
            '"%s"' % self.make_do_nothing_editor('name with spaces'))
 
172
        self.assertEqual(True, msgeditor._run_editor('a_filename'))    
 
173
 
 
174
    def make_fake_editor(self, message='test message from fed\\n'):
84
175
        """Set up environment so that an editor will be a known script.
85
176
 
86
177
        Sets up BZR_EDITOR so that if an editor is spawned it will run a
89
180
        f = file('fed.py', 'wb')
90
181
        f.write('#!%s\n' % sys.executable)
91
182
        f.write("""\
 
183
# coding=utf-8
92
184
import sys
93
185
if len(sys.argv) == 2:
94
186
    fn = sys.argv[1]
96
188
    s = f.read()
97
189
    f.close()
98
190
    f = file(fn, 'wb')
99
 
    f.write('test message from fed\\n')
 
191
    f.write('%s')
100
192
    f.write(s)
101
193
    f.close()
102
 
""")
 
194
""" % (message, ))
103
195
        f.close()
104
196
        if sys.platform == "win32":
105
197
            # [win32] make batch file and set BZR_EDITOR
109
201
"%s" fed.py %%1
110
202
""" % sys.executable)
111
203
            f.close()
112
 
            os.environ['BZR_EDITOR'] = 'fed.bat'
 
204
            self.overrideEnv('BZR_EDITOR', 'fed.bat')
113
205
        else:
114
206
            # [non-win32] make python script executable and set BZR_EDITOR
115
207
            os.chmod('fed.py', 0755)
116
 
            os.environ['BZR_EDITOR'] = './fed.py'
 
208
            self.overrideEnv('BZR_EDITOR', './fed.py')
117
209
 
118
210
    def test_edit_commit_message(self):
119
211
        working_tree = self.make_uncommitted_tree()
121
213
 
122
214
        mutter('edit_commit_message without infotext')
123
215
        self.assertEqual('test message from fed\n',
124
 
                         bzrlib.msgeditor.edit_commit_message(''))
 
216
                         msgeditor.edit_commit_message(''))
 
217
 
 
218
        mutter('edit_commit_message with ascii string infotext')
 
219
        self.assertEqual('test message from fed\n',
 
220
                         msgeditor.edit_commit_message('spam'))
125
221
 
126
222
        mutter('edit_commit_message with unicode infotext')
127
223
        self.assertEqual('test message from fed\n',
128
 
                         bzrlib.msgeditor.edit_commit_message(u'\u1234'))
 
224
                         msgeditor.edit_commit_message(u'\u1234'))
 
225
 
 
226
        tmpl = edit_commit_message_encoded(u'\u1234'.encode("utf8"))
 
227
        self.assertEqual('test message from fed\n', tmpl)
129
228
 
130
229
    def test_start_message(self):
131
230
        self.make_uncommitted_tree()
132
231
        self.make_fake_editor()
133
232
        self.assertEqual('test message from fed\nstart message\n',
134
 
                         bzrlib.msgeditor.edit_commit_message('',
 
233
                         msgeditor.edit_commit_message('',
135
234
                                              start_message='start message\n'))
136
235
        self.assertEqual('test message from fed\n',
137
 
                         bzrlib.msgeditor.edit_commit_message('',
 
236
                         msgeditor.edit_commit_message('',
138
237
                                              start_message=''))
139
238
 
140
239
    def test_deleted_commit_message(self):
141
240
        working_tree = self.make_uncommitted_tree()
142
241
 
143
242
        if sys.platform == 'win32':
144
 
            os.environ['BZR_EDITOR'] = 'cmd.exe /c del'
 
243
            editor = 'cmd.exe /c del'
145
244
        else:
146
 
            os.environ['BZR_EDITOR'] = 'rm'
 
245
            editor = 'rm'
 
246
        self.overrideEnv('BZR_EDITOR', editor)
147
247
 
148
 
        self.assertRaises((IOError, OSError), bzrlib.msgeditor.edit_commit_message, '')
 
248
        self.assertRaises((IOError, OSError), msgeditor.edit_commit_message, '')
149
249
 
150
250
    def test__get_editor(self):
151
 
        # Test that _get_editor can return a decent list of items
152
 
        bzr_editor = os.environ.get('BZR_EDITOR')
153
 
        visual = os.environ.get('VISUAL')
154
 
        editor = os.environ.get('EDITOR')
 
251
        self.overrideEnv('BZR_EDITOR', 'bzr_editor')
 
252
        self.overrideEnv('VISUAL', 'visual')
 
253
        self.overrideEnv('EDITOR', 'editor')
 
254
 
 
255
        conf = config.GlobalStack()
 
256
        conf.store._load_from_string('[DEFAULT]\neditor = config_editor\n')
 
257
        conf.store.save()
 
258
        editors = list(msgeditor._get_editor())
 
259
        editors = [editor for (editor, cfg_src) in editors]
 
260
 
 
261
        self.assertEqual(['bzr_editor', 'config_editor', 'visual', 'editor'],
 
262
                         editors[:4])
 
263
 
 
264
        if sys.platform == 'win32':
 
265
            self.assertEqual(['wordpad.exe', 'notepad.exe'], editors[4:])
 
266
        else:
 
267
            self.assertEqual(['/usr/bin/editor', 'vi', 'pico', 'nano', 'joe'],
 
268
                             editors[4:])
 
269
 
 
270
 
 
271
    def test__run_editor_EACCES(self):
 
272
        """If running a configured editor raises EACESS, the user is warned."""
 
273
        self.overrideEnv('BZR_EDITOR', 'eacces.py')
 
274
        f = file('eacces.py', 'wb')
 
275
        f.write('# Not a real editor')
 
276
        f.close()
 
277
        # Make the fake editor unreadable (and unexecutable)
 
278
        os.chmod('eacces.py', 0)
 
279
        # Set $EDITOR so that _run_editor will terminate before trying real
 
280
        # editors.
 
281
        self.overrideEnv('EDITOR', self.make_do_nothing_editor())
 
282
        # Call _run_editor, capturing mutter.warning calls.
 
283
        warnings = []
 
284
        def warning(*args):
 
285
            if len(args) > 1:
 
286
                warnings.append(args[0] % args[1:])
 
287
            else:
 
288
                warnings.append(args[0])
 
289
        _warning = trace.warning
 
290
        trace.warning = warning
155
291
        try:
156
 
            os.environ['BZR_EDITOR'] = 'bzr_editor'
157
 
            os.environ['VISUAL'] = 'visual'
158
 
            os.environ['EDITOR'] = 'editor'
159
 
 
160
 
            ensure_config_dir_exists()
161
 
            f = open(config_filename(), 'wb')
162
 
            f.write('editor = config_editor\n')
163
 
            f.close()
164
 
 
165
 
            editors = list(bzrlib.msgeditor._get_editor())
166
 
 
167
 
            self.assertEqual(['bzr_editor', 'config_editor', 'visual',
168
 
                              'editor'], editors[:4])
169
 
 
170
 
            if sys.platform == 'win32':
171
 
                self.assertEqual(['wordpad.exe', 'notepad.exe'], editors[4:])
172
 
            else:
173
 
                self.assertEqual(['/usr/bin/editor', 'vi', 'pico', 'nano',
174
 
                                  'joe'], editors[4:])
175
 
 
 
292
            msgeditor._run_editor('')
176
293
        finally:
177
 
            # Restore the environment
178
 
            if bzr_editor is None:
179
 
                del os.environ['BZR_EDITOR']
180
 
            else:
181
 
                os.environ['BZR_EDITOR'] = bzr_editor
182
 
            if visual is None:
183
 
                del os.environ['VISUAL']
184
 
            else:
185
 
                os.environ['VISUAL'] = visual
186
 
            if editor is None:
187
 
                del os.environ['EDITOR']
188
 
            else:
189
 
                os.environ['EDITOR'] = editor
 
294
            trace.warning = _warning
 
295
        self.assertStartsWith(warnings[0], 'Could not start editor "eacces.py"')
 
296
 
 
297
    def test__create_temp_file_with_commit_template(self):
 
298
        # check that commit template written properly
 
299
        # and has platform native line-endings (CRLF on win32)
 
300
        create_file = msgeditor._create_temp_file_with_commit_template
 
301
        msgfilename, hasinfo = create_file('infotext','----','start message')
 
302
        self.assertNotEqual(None, msgfilename)
 
303
        self.assertTrue(hasinfo)
 
304
        expected = os.linesep.join(['start message',
 
305
                                    '',
 
306
                                    '',
 
307
                                    '----',
 
308
                                    '',
 
309
                                    'infotext'])
 
310
        self.assertFileEqual(expected, msgfilename)
 
311
 
 
312
    def test__create_temp_file_with_commit_template_in_unicode_dir(self):
 
313
        self.requireFeature(features.UnicodeFilenameFeature)
 
314
        if hasattr(self, 'info'):
 
315
            tmpdir = self.info['directory']
 
316
            os.mkdir(tmpdir)
 
317
            # Force the creation of temp file in a directory whose name
 
318
            # requires some encoding support
 
319
            msgeditor._create_temp_file_with_commit_template('infotext',
 
320
                                                             tmpdir=tmpdir)
 
321
        else:
 
322
            raise TestNotApplicable('Test run elsewhere with non-ascii data.')
 
323
 
 
324
    def test__create_temp_file_with_empty_commit_template(self):
 
325
        # empty file
 
326
        create_file = msgeditor._create_temp_file_with_commit_template
 
327
        msgfilename, hasinfo = create_file('')
 
328
        self.assertNotEqual(None, msgfilename)
 
329
        self.assertFalse(hasinfo)
 
330
        self.assertFileEqual('', msgfilename)
 
331
 
 
332
    def test_unsupported_encoding_commit_message(self):
 
333
        self.overrideEnv('LANG', 'C')
 
334
        # LANG env variable has no effect on Windows
 
335
        # but some characters anyway cannot be represented
 
336
        # in default user encoding
 
337
        char = probe_bad_non_ascii(osutils.get_user_encoding())
 
338
        if char is None:
 
339
            raise TestSkipped('Cannot find suitable non-ascii character '
 
340
                'for user_encoding (%s)' % osutils.get_user_encoding())
 
341
 
 
342
        self.make_fake_editor(message=char)
 
343
 
 
344
        working_tree = self.make_uncommitted_tree()
 
345
        self.assertRaises(errors.BadCommitMessageEncoding,
 
346
                          msgeditor.edit_commit_message, '')
 
347
 
 
348
    def test_set_commit_message_no_hooks(self):
 
349
        commit_obj = commit.Commit()
 
350
        self.assertIs(None,
 
351
            msgeditor.set_commit_message(commit_obj))
 
352
 
 
353
    def test_set_commit_message_hook(self):
 
354
        msgeditor.hooks.install_named_hook("set_commit_message",
 
355
                lambda commit_obj, existing_message: "save me some typing\n", None)
 
356
        commit_obj = commit.Commit()
 
357
        self.assertEquals("save me some typing\n",
 
358
            msgeditor.set_commit_message(commit_obj))
 
359
 
 
360
    def test_generate_commit_message_template_no_hooks(self):
 
361
        commit_obj = commit.Commit()
 
362
        self.assertIs(None,
 
363
            msgeditor.generate_commit_message_template(commit_obj))
 
364
 
 
365
    def test_generate_commit_message_template_hook(self):
 
366
        msgeditor.hooks.install_named_hook("commit_message_template",
 
367
                lambda commit_obj, msg: "save me some typing\n", None)
 
368
        commit_obj = commit.Commit()
 
369
        self.assertEquals("save me some typing\n",
 
370
            msgeditor.generate_commit_message_template(commit_obj))
 
371
 
 
372
 
 
373
# GZ 2009-11-17: This wants moving to osutils when the errno checking code is
 
374
class TestPlatformErrnoWorkarounds(TestCaseInTempDir):
 
375
    """Ensuring workarounds enshrined in code actually serve a purpose"""
 
376
 
 
377
    def test_subprocess_call_bad_file(self):
 
378
        if sys.platform != "win32":
 
379
            raise TestNotApplicable("Workarounds for windows only")
 
380
        import subprocess, errno
 
381
        ERROR_BAD_EXE_FORMAT = 193
 
382
        file("textfile.txt", "w").close()
 
383
        e = self.assertRaises(WindowsError, subprocess.call, "textfile.txt")
 
384
        self.assertEqual(e.errno, errno.ENOEXEC)
 
385
        self.assertEqual(e.winerror, ERROR_BAD_EXE_FORMAT)