/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_options.py

  • Committer: Vincent Ladeuil
  • Date: 2012-01-18 14:09:19 UTC
  • mto: This revision was merged to the branch mainline in revision 6468.
  • Revision ID: v.ladeuil+lp@free.fr-20120118140919-rlvdrhpc0nq1lbwi
Change set/remove to require a lock for the branch config files.

This means that tests (or any plugin for that matter) do not requires an
explicit lock on the branch anymore to change a single option. This also
means the optimisation becomes "opt-in" and as such won't be as
spectacular as it may be and/or harder to get right (nothing fails
anymore).

This reduces the diff by ~300 lines.

Code/tests that were updating more than one config option is still taking
a lock to at least avoid some IOs and demonstrate the benefits through
the decreased number of hpss calls.

The duplication between BranchStack and BranchOnlyStack will be removed
once the same sharing is in place for local config files, at which point
the Stack class itself may be able to host the changes.

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
import re
 
18
 
 
19
from bzrlib import (
 
20
    bzrdir,
 
21
    commands,
 
22
    controldir,
 
23
    errors,
 
24
    option,
 
25
    registry,
 
26
    )
 
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
 
31
 
 
32
 
 
33
def parse(options, args):
 
34
    parser = option.get_optparser(dict((o.name, o) for o in options))
 
35
    return parser.parse_args(args)
 
36
 
 
37
 
 
38
class OptionTests(TestCase):
 
39
    """Command-line option tests"""
 
40
 
 
41
    def test_parse_args(self):
 
42
        """Option parser"""
 
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
 
45
        # general.
 
46
        self.assertEqual(
 
47
           ([], {'author': [], 'exclude': [], 'fixes': [], 'help': True}),
 
48
           parse_args(cmd_commit(), ['--help']))
 
49
        self.assertEqual(
 
50
           ([], {'author': [], 'exclude': [], 'fixes': [], 'message': 'biter'}),
 
51
           parse_args(cmd_commit(), ['--message=biter']))
 
52
 
 
53
    def test_no_more_opts(self):
 
54
        """Terminated options"""
 
55
        self.assertEqual(
 
56
            (['-file-with-dashes'], {'author': [], 'exclude': [], 'fixes': []}),
 
57
            parse_args(cmd_commit(), ['--', '-file-with-dashes']))
 
58
 
 
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')
 
65
 
 
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.')
 
70
 
 
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')
 
75
 
 
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')
 
81
 
 
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')
 
85
 
 
86
    def test_set_short_name(self):
 
87
        o = option.Option('wiggle')
 
88
        o.set_short_name('w')
 
89
        self.assertEqual(o.short_name(), 'w')
 
90
 
 
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])
 
94
 
 
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)
 
98
 
 
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,
 
111
                          ['--number'])
 
112
        self.assertRaises(errors.BzrCommandError, self.parse, options,
 
113
                          ['--no-number'])
 
114
 
 
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'))
 
118
 
 
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,
 
134
                          ['--two'])
 
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',
 
142
                                          '--format', 'two'])
 
143
        self.assertEqual({'format':'two'}, opts)
 
144
        options = [option.RegistryOption('format', '', registry, str,
 
145
                   enum_switch=False)]
 
146
        self.assertRaises(errors.BzrCommandError, self.parse, options,
 
147
                          ['--format', 'two'])
 
148
 
 
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)
 
160
 
 
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)
 
167
 
 
168
    def test_lazy_registry(self):
 
169
        options = [option.RegistryOption('format', '',
 
170
                   lazy_registry=('bzrlib.bzrdir','format_registry'),
 
171
                   converter=str)]
 
172
        opts, args = self.parse(options, ['--format', 'knit'])
 
173
        self.assertEqual({'format': 'knit'}, opts)
 
174
        self.assertRaises(
 
175
            errors.BadOptionValue, self.parse, options, ['--format', 'BAD'])
 
176
 
 
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)
 
188
 
 
189
    def test_help(self):
 
190
        registry = controldir.ControlDirFormatRegistry()
 
191
        bzrdir.register_metadir(registry, 'one', 'RepositoryFormat7', 'one help')
 
192
        bzrdir.register_metadir(registry, 'two',
 
193
            'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
 
194
            'two help',
 
195
            )
 
196
        bzrdir.register_metadir(registry, 'hidden', 'RepositoryFormat7', 'hidden help',
 
197
            hidden=True)
 
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')
 
207
 
 
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',
 
222
                'two help',
 
223
                )
 
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,
 
230
                                    value_switches=True)
 
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'),
 
236
                          ])
 
237
 
 
238
    def test_option_callback_bool(self):
 
239
        "Test booleans get True and False passed correctly to a callback."""
 
240
        cb_calls = []
 
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)
 
252
 
 
253
    def test_option_callback_str(self):
 
254
        """Test callbacks work for string options both long and short."""
 
255
        cb_calls = []
 
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,
 
259
            short_name='h')]
 
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)
 
268
 
 
269
 
 
270
class TestListOptions(TestCase):
 
271
    """Tests for ListOption, used to specify lists on the command-line."""
 
272
 
 
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)
 
276
 
 
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)
 
281
 
 
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)
 
287
 
 
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)
 
292
 
 
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)
 
297
 
 
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)
 
303
 
 
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)
 
310
 
 
311
    def test_option_callback_list(self):
 
312
        """Test callbacks work for list options."""
 
313
        cb_calls = []
 
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',
 
319
            '--hello=-'])
 
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)
 
330
 
 
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)
 
337
 
 
338
 
 
339
class TestOptionDefinitions(TestCase):
 
340
    """Tests for options in the Bazaar codebase."""
 
341
 
 
342
    def get_builtin_command_options(self):
 
343
        g = []
 
344
        commands.install_bzr_command_hooks()
 
345
        for cmd_name in sorted(commands.builtin_command_names()):
 
346
            cmd = commands.get_cmd_object(cmd_name)
 
347
            for opt_name, opt in sorted(cmd.options().items()):
 
348
                g.append((cmd_name, opt))
 
349
        self.assert_(g)
 
350
        return g
 
351
 
 
352
    def test_option_grammar(self):
 
353
        msgs = []
 
354
        # Option help should be written in sentence form, and have a final
 
355
        # period with an optional bracketed suffix. All the text should be on
 
356
        # one line, because the display code will wrap it.
 
357
        option_re = re.compile(r'^[A-Z][^\n]+\.(?: \([^\n]+\))?$')
 
358
        for scope, opt in self.get_builtin_command_options():
 
359
            for name, _, _, helptxt in opt.iter_switches():
 
360
                if name != opt.name:
 
361
                    name = "/".join([opt.name, name])
 
362
                if not helptxt:
 
363
                    msgs.append('%-16s %-16s %s' %
 
364
                           ((scope or 'GLOBAL'), name, 'NO HELP'))
 
365
                elif not option_re.match(helptxt):
 
366
                    if name.startswith("format/"):
 
367
                        # Don't complain about the odd format registry help
 
368
                        continue
 
369
                    msgs.append('%-16s %-16s %s' %
 
370
                            ((scope or 'GLOBAL'), name, helptxt))
 
371
        if msgs:
 
372
            self.fail("The following options don't match the style guide:\n"
 
373
                    + '\n'.join(msgs))
 
374
 
 
375
 
 
376
class TestOptionMisc(TestCase):
 
377
 
 
378
    def test_is_hidden(self):
 
379
        registry = controldir.ControlDirFormatRegistry()
 
380
        bzrdir.register_metadir(registry, 'hidden', 'HiddenFormat',
 
381
            'hidden help text', hidden=True)
 
382
        bzrdir.register_metadir(registry, 'visible', 'VisibleFormat',
 
383
            'visible help text', hidden=False)
 
384
        format = option.RegistryOption('format', '', registry, str)
 
385
        self.assertTrue(format.is_hidden('hidden'))
 
386
        self.assertFalse(format.is_hidden('visible'))
 
387
 
 
388
    def test_short_name(self):
 
389
        registry = controldir.ControlDirFormatRegistry()
 
390
        opt = option.RegistryOption('format', help='', registry=registry)
 
391
        self.assertEquals(None, opt.short_name())
 
392
        opt = option.RegistryOption('format', short_name='F', help='',
 
393
            registry=registry)
 
394
        self.assertEquals('F', opt.short_name())
 
395
 
 
396
    def test_option_custom_help(self):
 
397
        the_opt = option.Option.OPTIONS['help']
 
398
        orig_help = the_opt.help[:]
 
399
        my_opt = option.custom_help('help', 'suggest lottery numbers')
 
400
        # Confirm that my_opt has my help and the original is unchanged
 
401
        self.assertEqual('suggest lottery numbers', my_opt.help)
 
402
        self.assertEqual(orig_help, the_opt.help)
 
403
 
 
404
    def test_short_value_switches(self):
 
405
        reg = registry.Registry()
 
406
        reg.register('short', 'ShortChoice')
 
407
        reg.register('long', 'LongChoice')
 
408
        ropt = option.RegistryOption('choice', '', reg, value_switches=True,
 
409
            short_value_switches={'short': 's'})
 
410
        opts, args = parse([ropt], ['--short'])
 
411
        self.assertEqual('ShortChoice', opts.choice)
 
412
        opts, args = parse([ropt], ['-s'])
 
413
        self.assertEqual('ShortChoice', opts.choice)
 
414
 
 
415
 
 
416
class TestVerboseQuietLinkage(TestCase):
 
417
 
 
418
    def check(self, parser, level, args):
 
419
        option._verbosity_level = 0
 
420
        opts, args = parser.parse_args(args)
 
421
        self.assertEqual(level, option._verbosity_level)
 
422
 
 
423
    def test_verbose_quiet_linkage(self):
 
424
        parser = option.get_optparser(option.Option.STD_OPTIONS)
 
425
        self.check(parser, 0, [])
 
426
        self.check(parser, 1, ['-v'])
 
427
        self.check(parser, 2, ['-v', '-v'])
 
428
        self.check(parser, -1, ['-q'])
 
429
        self.check(parser, -2, ['-qq'])
 
430
        self.check(parser, -1, ['-v', '-v', '-q'])
 
431
        self.check(parser, 2, ['-q', '-v', '-v'])
 
432
        self.check(parser, 0, ['--no-verbose'])
 
433
        self.check(parser, 0, ['-v', '-q', '--no-quiet'])