~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_msgeditor.py

(jelmer) Support upgrading between the 2a and development-colo formats.
 (Jelmer Vernooij)

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
 
import bzrlib
24
23
from bzrlib import (
 
24
    commit,
 
25
    config,
25
26
    errors,
26
27
    msgeditor,
27
28
    osutils,
 
29
    tests,
 
30
    trace,
28
31
    )
29
 
from bzrlib.branch import Branch
30
 
from bzrlib.config import ensure_config_dir_exists, config_filename
31
32
from bzrlib.msgeditor import (
32
33
    make_commit_message_template_encoded,
33
34
    edit_commit_message_encoded
34
35
)
35
36
from bzrlib.tests import (
36
 
    iter_suite_tests,
37
 
    probe_bad_non_ascii,
38
 
    split_suite_by_re,
 
37
    features,
 
38
    TestCaseInTempDir,
39
39
    TestCaseWithTransport,
40
40
    TestNotApplicable,
41
41
    TestSkipped,
 
42
    multiply_tests,
 
43
    probe_bad_non_ascii,
 
44
    split_suite_by_re,
42
45
    )
43
 
from bzrlib.tests.EncodingAdapter import EncodingTestAdapter
 
46
from bzrlib.tests.EncodingAdapter import encoding_scenarios
44
47
from bzrlib.trace import mutter
45
48
 
46
49
 
48
51
    """Parameterize the test for tempfile creation with different encodings."""
49
52
    to_adapt, result = split_suite_by_re(standard_tests,
50
53
        "test__create_temp_file_with_commit_template_in_unicode_dir")
51
 
    for test in iter_suite_tests(to_adapt):
52
 
        result.addTests(EncodingTestAdapter().adapt(test))
53
 
    return result
 
54
    return multiply_tests(to_adapt, encoding_scenarios, result)
54
55
 
55
56
 
56
57
class MsgEditorTest(TestCaseWithTransport):
67
68
                "filesystem encoding %s" % sys.getfilesystemencoding())
68
69
        working_tree.add(filename)
69
70
        return working_tree
70
 
    
 
71
 
71
72
    def test_commit_template(self):
72
73
        """Test building a commit message template"""
73
74
        working_tree = self.make_uncommitted_tree()
79
80
  hell\u00d8
80
81
""")
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
 
82
115
    def test_commit_template_encoded(self):
83
116
        """Test building a commit message template"""
84
117
        working_tree = self.make_uncommitted_tree()
109
142
  hell\u00d8
110
143
""".encode('utf8') in template)
111
144
 
112
 
    def test_run_editor(self):
 
145
    def make_do_nothing_editor(self, basename='fed'):
113
146
        if sys.platform == "win32":
114
 
            f = file('fed.bat', 'w')
 
147
            name = basename + '.bat'
 
148
            f = file(name, 'w')
115
149
            f.write('@rem dummy fed')
116
150
            f.close()
117
 
            os.environ['BZR_EDITOR'] = 'fed.bat'
 
151
            return name
118
152
        else:
119
 
            f = file('fed.sh', 'wb')
 
153
            name = basename + '.sh'
 
154
            f = file(name, 'wb')
120
155
            f.write('#!/bin/sh\n')
121
156
            f.close()
122
 
            os.chmod('fed.sh', 0755)
123
 
            os.environ['BZR_EDITOR'] = './fed.sh'
 
157
            os.chmod(name, 0755)
 
158
            return './' + name
124
159
 
 
160
    def test_run_editor(self):
 
161
        self.overrideEnv('BZR_EDITOR', self.make_do_nothing_editor())
125
162
        self.assertEqual(True, msgeditor._run_editor(''),
126
163
                         'Unable to run dummy fake editor')
127
164
 
 
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
 
128
174
    def make_fake_editor(self, message='test message from fed\\n'):
129
175
        """Set up environment so that an editor will be a known script.
130
176
 
155
201
"%s" fed.py %%1
156
202
""" % sys.executable)
157
203
            f.close()
158
 
            os.environ['BZR_EDITOR'] = 'fed.bat'
 
204
            self.overrideEnv('BZR_EDITOR', 'fed.bat')
159
205
        else:
160
206
            # [non-win32] make python script executable and set BZR_EDITOR
161
207
            os.chmod('fed.py', 0755)
162
 
            os.environ['BZR_EDITOR'] = './fed.py'
 
208
            self.overrideEnv('BZR_EDITOR', './fed.py')
163
209
 
164
210
    def test_edit_commit_message(self):
165
211
        working_tree = self.make_uncommitted_tree()
194
240
        working_tree = self.make_uncommitted_tree()
195
241
 
196
242
        if sys.platform == 'win32':
197
 
            os.environ['BZR_EDITOR'] = 'cmd.exe /c del'
 
243
            editor = 'cmd.exe /c del'
198
244
        else:
199
 
            os.environ['BZR_EDITOR'] = 'rm'
 
245
            editor = 'rm'
 
246
        self.overrideEnv('BZR_EDITOR', editor)
200
247
 
201
248
        self.assertRaises((IOError, OSError), msgeditor.edit_commit_message, '')
202
249
 
203
250
    def test__get_editor(self):
204
 
        # Test that _get_editor can return a decent list of items
205
 
        bzr_editor = os.environ.get('BZR_EDITOR')
206
 
        visual = os.environ.get('VISUAL')
207
 
        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.GlobalConfig.from_string('editor = config_editor\n',
 
256
                                               save=True)
 
257
 
 
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
208
291
        try:
209
 
            os.environ['BZR_EDITOR'] = 'bzr_editor'
210
 
            os.environ['VISUAL'] = 'visual'
211
 
            os.environ['EDITOR'] = 'editor'
212
 
 
213
 
            ensure_config_dir_exists()
214
 
            f = open(config_filename(), 'wb')
215
 
            f.write('editor = config_editor\n')
216
 
            f.close()
217
 
 
218
 
            editors = list(msgeditor._get_editor())
219
 
 
220
 
            self.assertEqual(['bzr_editor', 'config_editor', 'visual',
221
 
                              'editor'], editors[:4])
222
 
 
223
 
            if sys.platform == 'win32':
224
 
                self.assertEqual(['wordpad.exe', 'notepad.exe'], editors[4:])
225
 
            else:
226
 
                self.assertEqual(['/usr/bin/editor', 'vi', 'pico', 'nano',
227
 
                                  'joe'], editors[4:])
228
 
 
 
292
            msgeditor._run_editor('')
229
293
        finally:
230
 
            # Restore the environment
231
 
            if bzr_editor is None:
232
 
                del os.environ['BZR_EDITOR']
233
 
            else:
234
 
                os.environ['BZR_EDITOR'] = bzr_editor
235
 
            if visual is None:
236
 
                del os.environ['VISUAL']
237
 
            else:
238
 
                os.environ['VISUAL'] = visual
239
 
            if editor is None:
240
 
                del os.environ['EDITOR']
241
 
            else:
242
 
                os.environ['EDITOR'] = editor
 
294
            trace.warning = _warning
 
295
        self.assertStartsWith(warnings[0], 'Could not start editor "eacces.py"')
243
296
 
244
297
    def test__create_temp_file_with_commit_template(self):
245
298
        # check that commit template written properly
257
310
        self.assertFileEqual(expected, msgfilename)
258
311
 
259
312
    def test__create_temp_file_with_commit_template_in_unicode_dir(self):
260
 
        from bzrlib.tests.test_diff import UnicodeFilename
261
 
        self.requireFeature(UnicodeFilename)
 
313
        self.requireFeature(features.UnicodeFilenameFeature)
262
314
        if hasattr(self, 'info'):
263
 
            os.mkdir(self.info['directory'])
264
 
            os.chdir(self.info['directory'])
265
 
            msgeditor._create_temp_file_with_commit_template('infotext')
 
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)
266
321
        else:
267
322
            raise TestNotApplicable('Test run elsewhere with non-ascii data.')
268
323
 
275
330
        self.assertFileEqual('', msgfilename)
276
331
 
277
332
    def test_unsupported_encoding_commit_message(self):
278
 
        old_env = osutils.set_or_unset_env('LANG', 'C')
279
 
        try:
280
 
            # LANG env variable has no effect on Windows
281
 
            # but some characters anyway cannot be represented
282
 
            # in default user encoding
283
 
            char = probe_bad_non_ascii(bzrlib.user_encoding)
284
 
            if char is None:
285
 
                raise TestSkipped('Cannot find suitable non-ascii character '
286
 
                    'for user_encoding (%s)' % bzrlib.user_encoding)
287
 
 
288
 
            self.make_fake_editor(message=char)
289
 
 
290
 
            working_tree = self.make_uncommitted_tree()
291
 
            self.assertRaises(errors.BadCommitMessageEncoding,
292
 
                              msgeditor.edit_commit_message, '')
293
 
        finally:
294
 
            osutils.set_or_unset_env('LANG', old_env)
 
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)