1
# Copyright (C) 2005-2011 Canonical Ltd
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.
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.
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
29
from bzrlib.commands import display_command
30
from bzrlib.tests import TestSkipped
33
class TestCommands(tests.TestCase):
35
def test_all_commands_have_help(self):
36
commands._register_builtin_commands()
37
commands_without_help = set()
38
base_doc = inspect.getdoc(commands.Command)
39
for cmd_name in commands.all_command_names():
40
cmd = commands.get_cmd_object(cmd_name)
42
if not cmd_help or cmd_help == base_doc:
43
commands_without_help.append(cmd_name)
44
self.assertLength(0, commands_without_help)
46
def test_display_command(self):
47
"""EPIPE message is selectively suppressed"""
49
raise IOError(errno.EPIPE, "Bogus pipe error")
50
self.assertRaises(IOError, pipe_thrower)
57
raise IOError(errno.ESPIPE, "Bogus pipe error")
58
self.assertRaises(IOError, other_thrower)
60
def test_unicode_command(self):
61
# This error is thrown when we can't find the command in the
62
# list of available commands
63
self.assertRaises(errors.BzrCommandError,
64
commands.run_bzr, [u'cmd\xb5'])
66
def test_unicode_option(self):
67
# This error is actually thrown by optparse, when it
68
# can't find the given option
70
if optparse.__version__ == "1.5.3":
71
raise TestSkipped("optparse 1.5.3 can't handle unicode options")
72
self.assertRaises(errors.BzrCommandError,
73
commands.run_bzr, ['log', u'--option\xb5'])
76
def get_command(options):
77
class cmd_foo(commands.Command):
80
takes_options = options
84
def test_help_hidden(self):
85
c = self.get_command([option.Option('foo', hidden=True)])
86
self.assertNotContainsRe(c.get_help_text(), '--foo')
88
def test_help_not_hidden(self):
89
c = self.get_command([option.Option('foo', hidden=False)])
90
self.assertContainsRe(c.get_help_text(), '--foo')
93
class TestInsideCommand(tests.TestCaseInTempDir):
95
def test_command_see_config_overrides(self):
97
# We override the run() command method so we can observe the
98
# overrides from inside.
99
c = config.GlobalStack()
100
self.assertEquals('12', c.get('xx'))
101
self.assertEquals('foo', c.get('yy'))
102
self.overrideAttr(builtins.cmd_rocks, 'run', run)
103
self.run_bzr(['rocks', '-Oxx=12', '-Oyy=foo'])
104
c = config.GlobalStack()
105
# Ensure that we don't leak outside of the command
106
self.assertEquals(None, c.get('xx'))
107
self.assertEquals(None, c.get('yy'))
110
class TestInvokedAs(tests.TestCase):
112
def test_invoked_as(self):
113
"""The command object knows the actual name used to invoke it."""
114
commands.install_bzr_command_hooks()
115
commands._register_builtin_commands()
116
# get one from the real get_cmd_object.
117
c = commands.get_cmd_object('ci')
118
self.assertIsInstance(c, builtins.cmd_commit)
119
self.assertEquals(c.invoked_as, 'ci')
122
class TestGetAlias(tests.TestCase):
124
def _get_config(self, config_text):
125
my_config = config.GlobalConfig.from_string(config_text)
128
def test_simple(self):
129
my_config = self._get_config("[ALIASES]\n"
130
"diff=diff -r -2..-1\n")
131
self.assertEqual([u'diff', u'-r', u'-2..-1'],
132
commands.get_alias("diff", config=my_config))
134
def test_single_quotes(self):
135
my_config = self._get_config("[ALIASES]\n"
136
"diff=diff -r -2..-1 --diff-options "
137
"'--strip-trailing-cr -wp'\n")
138
self.assertEqual([u'diff', u'-r', u'-2..-1', u'--diff-options',
139
u'--strip-trailing-cr -wp'],
140
commands.get_alias("diff", config=my_config))
142
def test_double_quotes(self):
143
my_config = self._get_config("[ALIASES]\n"
144
"diff=diff -r -2..-1 --diff-options "
145
"\"--strip-trailing-cr -wp\"\n")
146
self.assertEqual([u'diff', u'-r', u'-2..-1', u'--diff-options',
147
u'--strip-trailing-cr -wp'],
148
commands.get_alias("diff", config=my_config))
150
def test_unicode(self):
151
my_config = self._get_config("[ALIASES]\n"
152
u'iam=whoami "Erik B\u00e5gfors <erik@bagfors.nu>"\n')
153
self.assertEqual([u'whoami', u'Erik B\u00e5gfors <erik@bagfors.nu>'],
154
commands.get_alias("iam", config=my_config))
157
class TestSeeAlso(tests.TestCase):
158
"""Tests for the see also functional of Command."""
161
def _get_command_with_see_also(see_also):
162
class ACommand(commands.Command):
163
__doc__ = """A sample command."""
167
def test_default_subclass_no_see_also(self):
168
command = self._get_command_with_see_also([])
169
self.assertEqual([], command.get_see_also())
171
def test__see_also(self):
172
"""When _see_also is defined, it sets the result of get_see_also()."""
173
command = self._get_command_with_see_also(['bar', 'foo'])
174
self.assertEqual(['bar', 'foo'], command.get_see_also())
176
def test_deduplication(self):
177
"""Duplicates in _see_also are stripped out."""
178
command = self._get_command_with_see_also(['foo', 'foo'])
179
self.assertEqual(['foo'], command.get_see_also())
181
def test_sorted(self):
182
"""_see_also is sorted by get_see_also."""
183
command = self._get_command_with_see_also(['foo', 'bar'])
184
self.assertEqual(['bar', 'foo'], command.get_see_also())
186
def test_additional_terms(self):
187
"""Additional terms can be supplied and are deduped and sorted."""
188
command = self._get_command_with_see_also(['foo', 'bar'])
189
self.assertEqual(['bar', 'foo', 'gam'],
190
command.get_see_also(['gam', 'bar', 'gam']))
193
class TestRegisterLazy(tests.TestCase):
196
tests.TestCase.setUp(self)
197
import bzrlib.tests.fake_command
198
del sys.modules['bzrlib.tests.fake_command']
199
global lazy_command_imported
200
lazy_command_imported = False
201
commands.install_bzr_command_hooks()
205
commands.plugin_cmds.remove('fake')
207
def assertIsFakeCommand(self, cmd_obj):
208
from bzrlib.tests.fake_command import cmd_fake
209
self.assertIsInstance(cmd_obj, cmd_fake)
211
def test_register_lazy(self):
212
"""Ensure lazy registration works"""
213
commands.plugin_cmds.register_lazy('cmd_fake', [],
214
'bzrlib.tests.fake_command')
215
self.addCleanup(self.remove_fake)
216
self.assertFalse(lazy_command_imported)
217
fake_instance = commands.get_cmd_object('fake')
218
self.assertTrue(lazy_command_imported)
219
self.assertIsFakeCommand(fake_instance)
221
def test_get_unrelated_does_not_import(self):
222
commands.plugin_cmds.register_lazy('cmd_fake', [],
223
'bzrlib.tests.fake_command')
224
self.addCleanup(self.remove_fake)
225
commands.get_cmd_object('status')
226
self.assertFalse(lazy_command_imported)
228
def test_aliases(self):
229
commands.plugin_cmds.register_lazy('cmd_fake', ['fake_alias'],
230
'bzrlib.tests.fake_command')
231
self.addCleanup(self.remove_fake)
232
fake_instance = commands.get_cmd_object('fake_alias')
233
self.assertIsFakeCommand(fake_instance)
236
class TestExtendCommandHook(tests.TestCase):
238
def test_fires_on_get_cmd_object(self):
239
# The extend_command(cmd) hook fires when commands are delivered to the
240
# ui, not simply at registration (because lazy registered plugin
241
# commands are registered).
242
# when they are simply created.
244
commands.install_bzr_command_hooks()
245
commands.Command.hooks.install_named_hook(
246
"extend_command", hook_calls.append, None)
247
# create a command, should not fire
248
class cmd_test_extend_command_hook(commands.Command):
249
__doc__ = """A sample command."""
250
self.assertEqual([], hook_calls)
252
# register the command class, should not fire
254
commands.builtin_command_registry.register(cmd_test_extend_command_hook)
255
self.assertEqual([], hook_calls)
256
# and ask for the object, should fire
257
cmd = commands.get_cmd_object('test-extend-command-hook')
258
# For resilience - to ensure all code paths hit it - we
259
# fire on everything returned in the 'cmd_dict', which is currently
260
# all known commands, so assert that cmd is in hook_calls
261
self.assertSubset([cmd], hook_calls)
264
commands.builtin_command_registry.remove('test-extend-command-hook')
265
# -- as a plugin lazy registration
267
# register the command class, should not fire
268
commands.plugin_cmds.register_lazy('cmd_fake', [],
269
'bzrlib.tests.fake_command')
270
self.assertEqual([], hook_calls)
271
# and ask for the object, should fire
272
cmd = commands.get_cmd_object('fake')
273
self.assertEqual([cmd], hook_calls)
275
commands.plugin_cmds.remove('fake')
278
class TestGetCommandHook(tests.TestCase):
280
def test_fires_on_get_cmd_object(self):
281
# The get_command(cmd) hook fires when commands are delivered to the
283
commands.install_bzr_command_hooks()
285
class ACommand(commands.Command):
286
__doc__ = """A sample command."""
287
def get_cmd(cmd_or_None, cmd_name):
288
hook_calls.append(('called', cmd_or_None, cmd_name))
289
if cmd_name in ('foo', 'info'):
291
commands.Command.hooks.install_named_hook(
292
"get_command", get_cmd, None)
293
# create a command directly, should not fire
295
self.assertEqual([], hook_calls)
296
# ask by name, should fire and give us our command
297
cmd = commands.get_cmd_object('foo')
298
self.assertEqual([('called', None, 'foo')], hook_calls)
299
self.assertIsInstance(cmd, ACommand)
301
# ask by a name that is supplied by a builtin - the hook should still
302
# fire and we still get our object, but we should see the builtin
303
# passed to the hook.
304
cmd = commands.get_cmd_object('info')
305
self.assertIsInstance(cmd, ACommand)
306
self.assertEqual(1, len(hook_calls))
307
self.assertEqual('info', hook_calls[0][2])
308
self.assertIsInstance(hook_calls[0][1], builtins.cmd_info)
311
class TestGetMissingCommandHook(tests.TestCase):
313
def hook_missing(self):
314
"""Hook get_missing_command for testing."""
316
class ACommand(commands.Command):
317
__doc__ = """A sample command."""
318
def get_missing_cmd(cmd_name):
319
self.hook_calls.append(('called', cmd_name))
320
if cmd_name in ('foo', 'info'):
322
commands.Command.hooks.install_named_hook(
323
"get_missing_command", get_missing_cmd, None)
324
self.ACommand = ACommand
326
def test_fires_on_get_cmd_object(self):
327
# The get_missing_command(cmd) hook fires when commands are delivered to the
330
# create a command directly, should not fire
331
self.cmd = self.ACommand()
332
self.assertEqual([], self.hook_calls)
333
# ask by name, should fire and give us our command
334
cmd = commands.get_cmd_object('foo')
335
self.assertEqual([('called', 'foo')], self.hook_calls)
336
self.assertIsInstance(cmd, self.ACommand)
337
del self.hook_calls[:]
338
# ask by a name that is supplied by a builtin - the hook should not
339
# fire and we still get our object.
340
commands.install_bzr_command_hooks()
341
cmd = commands.get_cmd_object('info')
342
self.assertNotEqual(None, cmd)
343
self.assertEqual(0, len(self.hook_calls))
345
def test_skipped_on_HelpCommandIndex_get_topics(self):
346
# The get_missing_command(cmd_name) hook is not fired when
347
# looking up help topics.
349
topic = commands.HelpCommandIndex()
350
topics = topic.get_topics('foo')
351
self.assertEqual([], self.hook_calls)
354
class TestListCommandHook(tests.TestCase):
356
def test_fires_on_all_command_names(self):
357
# The list_commands() hook fires when all_command_names() is invoked.
359
commands.install_bzr_command_hooks()
360
def list_my_commands(cmd_names):
361
hook_calls.append('called')
362
cmd_names.update(['foo', 'bar'])
364
commands.Command.hooks.install_named_hook(
365
"list_commands", list_my_commands, None)
366
# Get a command, which should not trigger the hook.
367
cmd = commands.get_cmd_object('info')
368
self.assertEqual([], hook_calls)
369
# Get all command classes (for docs and shell completion).
370
cmds = list(commands.all_command_names())
371
self.assertEqual(['called'], hook_calls)
372
self.assertSubset(['foo', 'bar'], cmds)