~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_msgeditor.py

  • Committer: INADA Naoki
  • Date: 2011-05-05 13:03:12 UTC
  • mto: This revision was merged to the branch mainline in revision 5891.
  • Revision ID: songofacandy@gmail.com-20110505130312-ozvqmfjt7cuwi72k
Run build_mo when build is called.

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