~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_msgeditor.py

Turn completion assertions into separate methods.

Many common assertions used to be expressed as arguments to the complete
method.  This makes the checks more explicit, and the code easier to read.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2011 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
 
 
17
 
"""Test commit message editor.
18
 
"""
19
 
 
20
 
import os
21
 
import sys
22
 
 
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
46
 
from bzrlib.trace import mutter
47
 
 
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
 
 
56
 
class MsgEditorTest(TestCaseWithTransport):
57
 
 
58
 
    def make_uncommitted_tree(self):
59
 
        """Build a branch with uncommitted unicode named changes in the cwd."""
60
 
        working_tree = self.make_branch_and_tree('.')
61
 
        b = working_tree.branch
62
 
        filename = u'hell\u00d8'
63
 
        try:
64
 
            self.build_tree_contents([(filename, 'contents of hello')])
65
 
        except UnicodeEncodeError:
66
 
            raise TestSkipped("can't build unicode working tree in "
67
 
                "filesystem encoding %s" % sys.getfilesystemencoding())
68
 
        working_tree.add(filename)
69
 
        return working_tree
70
 
 
71
 
    def test_commit_template(self):
72
 
        """Test building a commit message template"""
73
 
        working_tree = self.make_uncommitted_tree()
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'):
145
 
        if sys.platform == "win32":
146
 
            name = basename + '.bat'
147
 
            f = file(name, 'w')
148
 
            f.write('@rem dummy fed')
149
 
            f.close()
150
 
            return name
151
 
        else:
152
 
            name = basename + '.sh'
153
 
            f = file(name, 'wb')
154
 
            f.write('#!/bin/sh\n')
155
 
            f.close()
156
 
            os.chmod(name, 0755)
157
 
            return './' + name
158
 
 
159
 
    def test_run_editor(self):
160
 
        self.overrideEnv('BZR_EDITOR', self.make_do_nothing_editor())
161
 
        self.assertEqual(True, msgeditor._run_editor(''),
162
 
                         'Unable to run dummy fake editor')
163
 
 
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'):
174
 
        """Set up environment so that an editor will be a known script.
175
 
 
176
 
        Sets up BZR_EDITOR so that if an editor is spawned it will run a
177
 
        script that just adds a known message to the start of the file.
178
 
        """
179
 
        f = file('fed.py', 'wb')
180
 
        f.write('#!%s\n' % sys.executable)
181
 
        f.write("""\
182
 
# coding=utf-8
183
 
import sys
184
 
if len(sys.argv) == 2:
185
 
    fn = sys.argv[1]
186
 
    f = file(fn, 'rb')
187
 
    s = f.read()
188
 
    f.close()
189
 
    f = file(fn, 'wb')
190
 
    f.write('%s')
191
 
    f.write(s)
192
 
    f.close()
193
 
""" % (message, ))
194
 
        f.close()
195
 
        if sys.platform == "win32":
196
 
            # [win32] make batch file and set BZR_EDITOR
197
 
            f = file('fed.bat', 'w')
198
 
            f.write("""\
199
 
@echo off
200
 
"%s" fed.py %%1
201
 
""" % sys.executable)
202
 
            f.close()
203
 
            self.overrideEnv('BZR_EDITOR', 'fed.bat')
204
 
        else:
205
 
            # [non-win32] make python script executable and set BZR_EDITOR
206
 
            os.chmod('fed.py', 0755)
207
 
            self.overrideEnv('BZR_EDITOR', './fed.py')
208
 
 
209
 
    def test_edit_commit_message(self):
210
 
        working_tree = self.make_uncommitted_tree()
211
 
        self.make_fake_editor()
212
 
 
213
 
        mutter('edit_commit_message without infotext')
214
 
        self.assertEqual('test message from fed\n',
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'))
220
 
 
221
 
        mutter('edit_commit_message with unicode infotext')
222
 
        self.assertEqual('test message from fed\n',
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)
227
 
 
228
 
    def test_start_message(self):
229
 
        self.make_uncommitted_tree()
230
 
        self.make_fake_editor()
231
 
        self.assertEqual('test message from fed\nstart message\n',
232
 
                         msgeditor.edit_commit_message('',
233
 
                                              start_message='start message\n'))
234
 
        self.assertEqual('test message from fed\n',
235
 
                         msgeditor.edit_commit_message('',
236
 
                                              start_message=''))
237
 
 
238
 
    def test_deleted_commit_message(self):
239
 
        working_tree = self.make_uncommitted_tree()
240
 
 
241
 
        if sys.platform == 'win32':
242
 
            editor = 'cmd.exe /c del'
243
 
        else:
244
 
            editor = 'rm'
245
 
        self.overrideEnv('BZR_EDITOR', editor)
246
 
 
247
 
        self.assertRaises((IOError, OSError), msgeditor.edit_commit_message, '')
248
 
 
249
 
    def test__get_editor(self):
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
290
 
        try:
291
 
            msgeditor._run_editor('')
292
 
        finally:
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_set_commit_message_no_hooks(self):
348
 
        commit_obj = commit.Commit()
349
 
        self.assertIs(None,
350
 
            msgeditor.set_commit_message(commit_obj))
351
 
 
352
 
    def test_set_commit_message_hook(self):
353
 
        msgeditor.hooks.install_named_hook("set_commit_message",
354
 
                lambda commit_obj, existing_message: "save me some typing\n", None)
355
 
        commit_obj = commit.Commit()
356
 
        self.assertEquals("save me some typing\n",
357
 
            msgeditor.set_commit_message(commit_obj))
358
 
 
359
 
    def test_generate_commit_message_template_no_hooks(self):
360
 
        commit_obj = commit.Commit()
361
 
        self.assertIs(None,
362
 
            msgeditor.generate_commit_message_template(commit_obj))
363
 
 
364
 
    def test_generate_commit_message_template_hook(self):
365
 
        msgeditor.hooks.install_named_hook("commit_message_template",
366
 
                lambda commit_obj, msg: "save me some typing\n", None)
367
 
        commit_obj = commit.Commit()
368
 
        self.assertEquals("save me some typing\n",
369
 
            msgeditor.generate_commit_message_template(commit_obj))
370
 
 
371
 
 
372
 
# GZ 2009-11-17: This wants moving to osutils when the errno checking code is
373
 
class TestPlatformErrnoWorkarounds(TestCaseInTempDir):
374
 
    """Ensuring workarounds enshrined in code actually serve a purpose"""
375
 
 
376
 
    def test_subprocess_call_bad_file(self):
377
 
        if sys.platform != "win32":
378
 
            raise TestNotApplicable("Workarounds for windows only")
379
 
        import subprocess, errno
380
 
        ERROR_BAD_EXE_FORMAT = 193
381
 
        file("textfile.txt", "w").close()
382
 
        e = self.assertRaises(WindowsError, subprocess.call, "textfile.txt")
383
 
        self.assertEqual(e.errno, errno.ENOEXEC)
384
 
        self.assertEqual(e.winerror, ERROR_BAD_EXE_FORMAT)