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
27
from bzrlib.builtins import cmd_commit
28
from bzrlib.commands import parse_args
29
from bzrlib.tests import TestCase
30
from bzrlib.repofmt import knitrepo
33
def parse(options, args):
34
parser = option.get_optparser(dict((o.name, o) for o in options))
35
return parser.parse_args(args)
38
class OptionTests(TestCase):
39
"""Command-line option tests"""
41
def test_parse_args(self):
43
# XXX: Using cmd_commit makes these tests overly sensitive to changes
44
# to cmd_commit, when they are meant to be about option parsing in
47
([], {'author': [], 'exclude': [], 'fixes': [], 'help': True}),
48
parse_args(cmd_commit(), ['--help']))
50
([], {'author': [], 'exclude': [], 'fixes': [], 'message': 'biter'}),
51
parse_args(cmd_commit(), ['--message=biter']))
53
def test_no_more_opts(self):
54
"""Terminated options"""
56
(['-file-with-dashes'], {'author': [], 'exclude': [], 'fixes': []}),
57
parse_args(cmd_commit(), ['--', '-file-with-dashes']))
59
def test_option_help(self):
60
"""Options have help strings."""
61
out, err = self.run_bzr('commit --help')
62
self.assertContainsRe(out,
63
r'--file(.|\n)*Take commit message from this file\.')
64
self.assertContainsRe(out, r'-h.*--help')
66
def test_option_help_global(self):
67
"""Global options have help strings."""
68
out, err = self.run_bzr('help status')
69
self.assertContainsRe(out, r'--show-ids.*Show internal object.')
71
def test_option_help_global_hidden(self):
72
"""Hidden global options have no help strings."""
73
out, err = self.run_bzr('help log')
74
self.assertNotContainsRe(out, r'--message')
76
def test_option_arg_help(self):
77
"""Help message shows option arguments."""
78
out, err = self.run_bzr('help commit')
79
self.assertEqual(err, '')
80
self.assertContainsRe(out, r'--file[ =]MSGFILE')
82
def test_unknown_short_opt(self):
83
out, err = self.run_bzr('help -r', retcode=3)
84
self.assertContainsRe(err, r'no such option')
86
def test_set_short_name(self):
87
o = option.Option('wiggle')
89
self.assertEqual(o.short_name(), 'w')
91
def test_allow_dash(self):
92
"""Test that we can pass a plain '-' as an argument."""
93
self.assertEqual((['-']), parse_args(cmd_commit(), ['-'])[0])
95
def parse(self, options, args):
96
parser = option.get_optparser(dict((o.name, o) for o in options))
97
return parser.parse_args(args)
99
def test_conversion(self):
100
options = [option.Option('hello')]
101
opts, args = self.parse(options, ['--no-hello', '--hello'])
102
self.assertEqual(True, opts.hello)
103
opts, args = self.parse(options, [])
104
self.assertFalse(hasattr(opts, 'hello'))
105
opts, args = self.parse(options, ['--hello', '--no-hello'])
106
self.assertEqual(False, opts.hello)
107
options = [option.Option('number', type=int)]
108
opts, args = self.parse(options, ['--number', '6'])
109
self.assertEqual(6, opts.number)
110
self.assertRaises(errors.BzrCommandError, self.parse, options,
112
self.assertRaises(errors.BzrCommandError, self.parse, options,
115
def test_is_hidden(self):
116
self.assertTrue(option.Option('foo', hidden=True).is_hidden('foo'))
117
self.assertFalse(option.Option('foo', hidden=False).is_hidden('foo'))
119
def test_registry_conversion(self):
120
registry = controldir.ControlDirFormatRegistry()
121
bzrdir.register_metadir(registry, 'one', 'RepositoryFormat7', 'one help')
122
bzrdir.register_metadir(registry, 'two', 'RepositoryFormatKnit1', 'two help')
123
bzrdir.register_metadir(registry, 'hidden', 'RepositoryFormatKnit1',
124
'two help', hidden=True)
125
registry.set_default('one')
126
options = [option.RegistryOption('format', '', registry, str)]
127
opts, args = self.parse(options, ['--format', 'one'])
128
self.assertEqual({'format':'one'}, opts)
129
opts, args = self.parse(options, ['--format', 'two'])
130
self.assertEqual({'format':'two'}, opts)
131
self.assertRaises(errors.BadOptionValue, self.parse, options,
132
['--format', 'three'])
133
self.assertRaises(errors.BzrCommandError, self.parse, options,
135
options = [option.RegistryOption('format', '', registry, str,
136
value_switches=True)]
137
opts, args = self.parse(options, ['--two'])
138
self.assertEqual({'format':'two'}, opts)
139
opts, args = self.parse(options, ['--two', '--one'])
140
self.assertEqual({'format':'one'}, opts)
141
opts, args = self.parse(options, ['--two', '--one',
143
self.assertEqual({'format':'two'}, opts)
144
options = [option.RegistryOption('format', '', registry, str,
146
self.assertRaises(errors.BzrCommandError, self.parse, options,
149
def test_override(self):
150
options = [option.Option('hello', type=str),
151
option.Option('hi', type=str, param_name='hello')]
152
opts, args = self.parse(options, ['--hello', 'a', '--hello', 'b'])
153
self.assertEqual('b', opts.hello)
154
opts, args = self.parse(options, ['--hello', 'b', '--hello', 'a'])
155
self.assertEqual('a', opts.hello)
156
opts, args = self.parse(options, ['--hello', 'a', '--hi', 'b'])
157
self.assertEqual('b', opts.hello)
158
opts, args = self.parse(options, ['--hi', 'b', '--hello', 'a'])
159
self.assertEqual('a', opts.hello)
161
def test_registry_converter(self):
162
options = [option.RegistryOption('format', '',
163
bzrdir.format_registry, bzrdir.format_registry.make_bzrdir)]
164
opts, args = self.parse(options, ['--format', 'knit'])
165
self.assertIsInstance(opts.format.repository_format,
166
knitrepo.RepositoryFormatKnit1)
168
def test_lazy_registry(self):
169
options = [option.RegistryOption('format', '',
170
lazy_registry=('bzrlib.bzrdir','format_registry'),
172
opts, args = self.parse(options, ['--format', 'knit'])
173
self.assertEqual({'format': 'knit'}, opts)
175
errors.BadOptionValue, self.parse, options, ['--format', 'BAD'])
177
def test_from_kwargs(self):
178
my_option = option.RegistryOption.from_kwargs('my-option',
179
help='test option', short='be short', be_long='go long')
180
self.assertEqual(['my-option'],
181
[x[0] for x in my_option.iter_switches()])
182
my_option = option.RegistryOption.from_kwargs('my-option',
183
help='test option', title="My option", short='be short',
184
be_long='go long', value_switches=True)
185
self.assertEqual(['my-option', 'be-long', 'short'],
186
[x[0] for x in my_option.iter_switches()])
187
self.assertEqual('test option', my_option.help)
190
registry = controldir.ControlDirFormatRegistry()
191
bzrdir.register_metadir(registry, 'one', 'RepositoryFormat7', 'one help')
192
bzrdir.register_metadir(registry, 'two',
193
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
196
bzrdir.register_metadir(registry, 'hidden', 'RepositoryFormat7', 'hidden help',
198
registry.set_default('one')
199
options = [option.RegistryOption('format', 'format help', registry,
200
str, value_switches=True, title='Formats')]
201
parser = option.get_optparser(dict((o.name, o) for o in options))
202
value = parser.format_option_help()
203
self.assertContainsRe(value, 'format.*format help')
204
self.assertContainsRe(value, 'one.*one help')
205
self.assertContainsRe(value, 'Formats:\n *--format')
206
self.assertNotContainsRe(value, 'hidden help')
208
def test_iter_switches(self):
209
opt = option.Option('hello', help='fg')
210
self.assertEqual(list(opt.iter_switches()),
211
[('hello', None, None, 'fg')])
212
opt = option.Option('hello', help='fg', type=int)
213
self.assertEqual(list(opt.iter_switches()),
214
[('hello', None, 'ARG', 'fg')])
215
opt = option.Option('hello', help='fg', type=int, argname='gar')
216
self.assertEqual(list(opt.iter_switches()),
217
[('hello', None, 'GAR', 'fg')])
218
registry = controldir.ControlDirFormatRegistry()
219
bzrdir.register_metadir(registry, 'one', 'RepositoryFormat7', 'one help')
220
bzrdir.register_metadir(registry, 'two',
221
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
224
registry.set_default('one')
225
opt = option.RegistryOption('format', 'format help', registry,
226
value_switches=False)
227
self.assertEqual(list(opt.iter_switches()),
228
[('format', None, 'ARG', 'format help')])
229
opt = option.RegistryOption('format', 'format help', registry,
231
self.assertEqual(list(opt.iter_switches()),
232
[('format', None, 'ARG', 'format help'),
233
('default', None, None, 'one help'),
234
('one', None, None, 'one help'),
235
('two', None, None, 'two help'),
238
def test_option_callback_bool(self):
239
"Test booleans get True and False passed correctly to a callback."""
241
def cb(option, name, value, parser):
242
cb_calls.append((option,name,value,parser))
243
options = [option.Option('hello', custom_callback=cb)]
244
opts, args = self.parse(options, ['--hello', '--no-hello'])
245
self.assertEqual(2, len(cb_calls))
246
opt,name,value,parser = cb_calls[0]
247
self.assertEqual('hello', name)
248
self.assertTrue(value)
249
opt,name,value,parser = cb_calls[1]
250
self.assertEqual('hello', name)
251
self.assertFalse(value)
253
def test_option_callback_str(self):
254
"""Test callbacks work for string options both long and short."""
256
def cb(option, name, value, parser):
257
cb_calls.append((option,name,value,parser))
258
options = [option.Option('hello', type=str, custom_callback=cb,
260
opts, args = self.parse(options, ['--hello', 'world', '-h', 'mars'])
261
self.assertEqual(2, len(cb_calls))
262
opt,name,value,parser = cb_calls[0]
263
self.assertEqual('hello', name)
264
self.assertEqual('world', value)
265
opt,name,value,parser = cb_calls[1]
266
self.assertEqual('hello', name)
267
self.assertEqual('mars', value)
270
class TestListOptions(TestCase):
271
"""Tests for ListOption, used to specify lists on the command-line."""
273
def parse(self, options, args):
274
parser = option.get_optparser(dict((o.name, o) for o in options))
275
return parser.parse_args(args)
277
def test_list_option(self):
278
options = [option.ListOption('hello', type=str)]
279
opts, args = self.parse(options, ['--hello=world', '--hello=sailor'])
280
self.assertEqual(['world', 'sailor'], opts.hello)
282
def test_list_option_with_dash(self):
283
options = [option.ListOption('with-dash', type=str)]
284
opts, args = self.parse(options, ['--with-dash=world',
285
'--with-dash=sailor'])
286
self.assertEqual(['world', 'sailor'], opts.with_dash)
288
def test_list_option_no_arguments(self):
289
options = [option.ListOption('hello', type=str)]
290
opts, args = self.parse(options, [])
291
self.assertEqual([], opts.hello)
293
def test_list_option_with_int_type(self):
294
options = [option.ListOption('hello', type=int)]
295
opts, args = self.parse(options, ['--hello=2', '--hello=3'])
296
self.assertEqual([2, 3], opts.hello)
298
def test_list_option_with_int_type_can_be_reset(self):
299
options = [option.ListOption('hello', type=int)]
300
opts, args = self.parse(options, ['--hello=2', '--hello=3',
301
'--hello=-', '--hello=5'])
302
self.assertEqual([5], opts.hello)
304
def test_list_option_can_be_reset(self):
305
"""Passing an option of '-' to a list option should reset the list."""
306
options = [option.ListOption('hello', type=str)]
307
opts, args = self.parse(
308
options, ['--hello=a', '--hello=b', '--hello=-', '--hello=c'])
309
self.assertEqual(['c'], opts.hello)
311
def test_option_callback_list(self):
312
"""Test callbacks work for list options."""
314
def cb(option, name, value, parser):
315
# Note that the value is a reference so copy to keep it
316
cb_calls.append((option,name,value[:],parser))
317
options = [option.ListOption('hello', type=str, custom_callback=cb)]
318
opts, args = self.parse(options, ['--hello=world', '--hello=mars',
320
self.assertEqual(3, len(cb_calls))
321
opt,name,value,parser = cb_calls[0]
322
self.assertEqual('hello', name)
323
self.assertEqual(['world'], value)
324
opt,name,value,parser = cb_calls[1]
325
self.assertEqual('hello', name)
326
self.assertEqual(['world', 'mars'], value)
327
opt,name,value,parser = cb_calls[2]
328
self.assertEqual('hello', name)
329
self.assertEqual([], value)
331
def test_list_option_param_name(self):
332
"""Test list options can have their param_name set."""
333
options = [option.ListOption('hello', type=str, param_name='greeting')]
334
opts, args = self.parse(
335
options, ['--hello=world', '--hello=sailor'])
336
self.assertEqual(['world', 'sailor'], opts.greeting)
339
class TestOptionDefinitions(TestCase):
340
"""Tests for options in the Bazaar codebase."""
342
def get_builtin_command_options(self):
344
for cmd_name in sorted(commands.all_command_names()):
345
cmd = commands.get_cmd_object(cmd_name)
346
for opt_name, opt in sorted(cmd.options().items()):
347
g.append((cmd_name, opt))
350
def test_global_options_used(self):
351
# In the distant memory, options could only be declared globally. Now
352
# we prefer to declare them in the command, unless like -r they really
353
# are used very widely with the exact same meaning. So this checks
354
# for any that should be garbage collected.
355
g = dict(option.Option.OPTIONS.items())
358
for cmd_name in sorted(commands.all_command_names()):
359
cmd = commands.get_cmd_object(cmd_name)
360
for option_or_name in sorted(cmd.takes_options):
361
if not isinstance(option_or_name, basestring):
362
self.assertIsInstance(option_or_name, option.Option)
363
elif not option_or_name in g:
364
msgs.append("apparent reference to undefined "
365
"global option %r from %r"
366
% (option_or_name, cmd))
368
used_globals.setdefault(option_or_name, []).append(cmd_name)
369
unused_globals = set(g.keys()) - set(used_globals.keys())
370
# not enforced because there might be plugins that use these globals
371
## for option_name in sorted(unused_globals):
372
## msgs.append("unused global option %r" % option_name)
373
## for option_name, cmds in sorted(used_globals.items()):
374
## if len(cmds) <= 1:
375
## msgs.append("global option %r is only used by %r"
376
## % (option_name, cmds))
378
self.fail("problems with global option definitions:\n"
381
def test_option_grammar(self):
383
# Option help should be written in sentence form, and have a final
384
# period and be all on a single line, because the display code will
386
option_re = re.compile(r'^[A-Z][^\n]+\.$')
387
for scope, opt in self.get_builtin_command_options():
389
msgs.append('%-16s %-16s %s' %
390
((scope or 'GLOBAL'), opt.name, 'NO HELP'))
391
elif not option_re.match(opt.help):
392
msgs.append('%-16s %-16s %s' %
393
((scope or 'GLOBAL'), opt.name, opt.help))
395
self.fail("The following options don't match the style guide:\n"
398
def test_is_hidden(self):
399
registry = controldir.ControlDirFormatRegistry()
400
bzrdir.register_metadir(registry, 'hidden', 'HiddenFormat',
401
'hidden help text', hidden=True)
402
bzrdir.register_metadir(registry, 'visible', 'VisibleFormat',
403
'visible help text', hidden=False)
404
format = option.RegistryOption('format', '', registry, str)
405
self.assertTrue(format.is_hidden('hidden'))
406
self.assertFalse(format.is_hidden('visible'))
408
def test_short_name(self):
409
registry = controldir.ControlDirFormatRegistry()
410
opt = option.RegistryOption('format', help='', registry=registry)
411
self.assertEquals(None, opt.short_name())
412
opt = option.RegistryOption('format', short_name='F', help='',
414
self.assertEquals('F', opt.short_name())
416
def test_option_custom_help(self):
417
the_opt = option.Option.OPTIONS['help']
418
orig_help = the_opt.help[:]
419
my_opt = option.custom_help('help', 'suggest lottery numbers')
420
# Confirm that my_opt has my help and the original is unchanged
421
self.assertEqual('suggest lottery numbers', my_opt.help)
422
self.assertEqual(orig_help, the_opt.help)
424
def test_short_value_switches(self):
425
reg = registry.Registry()
426
reg.register('short', 'ShortChoice')
427
reg.register('long', 'LongChoice')
428
ropt = option.RegistryOption('choice', '', reg, value_switches=True,
429
short_value_switches={'short': 's'})
430
opts, args = parse([ropt], ['--short'])
431
self.assertEqual('ShortChoice', opts.choice)
432
opts, args = parse([ropt], ['-s'])
433
self.assertEqual('ShortChoice', opts.choice)
436
class TestVerboseQuietLinkage(TestCase):
438
def check(self, parser, level, args):
439
option._verbosity_level = 0
440
opts, args = parser.parse_args(args)
441
self.assertEqual(level, option._verbosity_level)
443
def test_verbose_quiet_linkage(self):
444
parser = option.get_optparser(option.Option.STD_OPTIONS)
445
self.check(parser, 0, [])
446
self.check(parser, 1, ['-v'])
447
self.check(parser, 2, ['-v', '-v'])
448
self.check(parser, -1, ['-q'])
449
self.check(parser, -2, ['-qq'])
450
self.check(parser, -1, ['-v', '-v', '-q'])
451
self.check(parser, 2, ['-q', '-v', '-v'])
452
self.check(parser, 0, ['--no-verbose'])
453
self.check(parser, 0, ['-v', '-q', '--no-quiet'])