~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_options.py

  • Committer: John Arbash Meinel
  • Date: 2007-07-13 02:23:34 UTC
  • mfrom: (2592 +trunk) (2612 +trunk)
  • mto: This revision was merged to the branch mainline in revision 2614.
  • Revision ID: john@arbash-meinel.com-20070713022334-qb6ewgo6v4251yd9
[merge] bzr.dev 2612

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005, 2006, 2007 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
 
 
17
import re
 
18
 
 
19
from bzrlib import (
 
20
    builtins,
 
21
    bzrdir,
 
22
    commands,
 
23
    errors,
 
24
    option,
 
25
    repository,
 
26
    symbol_versioning,
 
27
    )
 
28
from bzrlib.builtins import cmd_commit, cmd_log, cmd_status
 
29
from bzrlib.commands import Command, parse_args
 
30
from bzrlib.tests import TestCase
 
31
from bzrlib.repofmt import knitrepo
 
32
 
 
33
 
 
34
def parse(options, args):
 
35
    parser = option.get_optparser(dict((o.name, o) for o in options))
 
36
    return parser.parse_args(args)
 
37
 
 
38
 
 
39
class OptionTests(TestCase):
 
40
    """Command-line option tests"""
 
41
 
 
42
    def test_parse_args(self):
 
43
        """Option parser"""
 
44
        eq = self.assertEquals
 
45
        eq(parse_args(cmd_commit(), ['--help']),
 
46
           ([], {'fixes': [], 'help': True}))
 
47
        eq(parse_args(cmd_commit(), ['--message=biter']),
 
48
           ([], {'fixes': [], 'message': 'biter'}))
 
49
 
 
50
    def test_no_more_opts(self):
 
51
        """Terminated options"""
 
52
        self.assertEquals(parse_args(cmd_commit(), ['--', '-file-with-dashes']),
 
53
                          (['-file-with-dashes'], {'fixes': []}))
 
54
 
 
55
    def test_option_help(self):
 
56
        """Options have help strings."""
 
57
        out, err = self.run_bzr('commit --help')
 
58
        self.assertContainsRe(out,
 
59
                r'--file(.|\n)*Take commit message from this file\.')
 
60
        self.assertContainsRe(out, r'-h.*--help')
 
61
 
 
62
    def test_option_help_global(self):
 
63
        """Global options have help strings."""
 
64
        out, err = self.run_bzr('help status')
 
65
        self.assertContainsRe(out, r'--show-ids.*Show internal object.')
 
66
 
 
67
    def test_option_arg_help(self):
 
68
        """Help message shows option arguments."""
 
69
        out, err = self.run_bzr('help commit')
 
70
        self.assertEquals(err, '')
 
71
        self.assertContainsRe(out, r'--file[ =]MSGFILE')
 
72
 
 
73
    def test_unknown_short_opt(self):
 
74
        out, err = self.run_bzr('help -r', retcode=3)
 
75
        self.assertContainsRe(err, r'no such option')
 
76
 
 
77
    def test_set_short_name(self):
 
78
        o = option.Option('wiggle')
 
79
        o.set_short_name('w')
 
80
        self.assertEqual(o.short_name(), 'w')
 
81
 
 
82
    def test_allow_dash(self):
 
83
        """Test that we can pass a plain '-' as an argument."""
 
84
        self.assertEqual(
 
85
            (['-'], {'fixes': []}), parse_args(cmd_commit(), ['-']))
 
86
 
 
87
    def parse(self, options, args):
 
88
        parser = option.get_optparser(dict((o.name, o) for o in options))
 
89
        return parser.parse_args(args)
 
90
 
 
91
    def test_conversion(self):
 
92
        options = [option.Option('hello')]
 
93
        opts, args = self.parse(options, ['--no-hello', '--hello'])
 
94
        self.assertEqual(True, opts.hello)
 
95
        opts, args = self.parse(options, [])
 
96
        self.assertEqual(option.OptionParser.DEFAULT_VALUE, opts.hello)
 
97
        opts, args = self.parse(options, ['--hello', '--no-hello'])
 
98
        self.assertEqual(False, opts.hello)
 
99
        options = [option.Option('number', type=int)]
 
100
        opts, args = self.parse(options, ['--number', '6'])
 
101
        self.assertEqual(6, opts.number)
 
102
        self.assertRaises(errors.BzrCommandError, self.parse, options,
 
103
                          ['--number'])
 
104
        self.assertRaises(errors.BzrCommandError, self.parse, options,
 
105
                          ['--no-number'])
 
106
 
 
107
    def test_registry_conversion(self):
 
108
        registry = bzrdir.BzrDirFormatRegistry()
 
109
        registry.register_metadir('one', 'RepositoryFormat7', 'one help')
 
110
        registry.register_metadir('two', 'RepositoryFormatKnit1', 'two help')
 
111
        registry.register_metadir('hidden', 'RepositoryFormatKnit1',
 
112
            'two help', hidden=True)
 
113
        registry.set_default('one')
 
114
        options = [option.RegistryOption('format', '', registry, str)]
 
115
        opts, args = self.parse(options, ['--format', 'one'])
 
116
        self.assertEqual({'format':'one'}, opts)
 
117
        opts, args = self.parse(options, ['--format', 'two'])
 
118
        self.assertEqual({'format':'two'}, opts)
 
119
        self.assertRaises(errors.BadOptionValue, self.parse, options,
 
120
                          ['--format', 'three'])
 
121
        self.assertRaises(errors.BzrCommandError, self.parse, options,
 
122
                          ['--two'])
 
123
        options = [option.RegistryOption('format', '', registry, str,
 
124
                   value_switches=True)]
 
125
        opts, args = self.parse(options, ['--two'])
 
126
        self.assertEqual({'format':'two'}, opts)
 
127
        opts, args = self.parse(options, ['--two', '--one'])
 
128
        self.assertEqual({'format':'one'}, opts)
 
129
        opts, args = self.parse(options, ['--two', '--one',
 
130
                                          '--format', 'two'])
 
131
        self.assertEqual({'format':'two'}, opts)
 
132
        options = [option.RegistryOption('format', '', registry, str,
 
133
                   enum_switch=False)]
 
134
        self.assertRaises(errors.BzrCommandError, self.parse, options,
 
135
                          ['--format', 'two'])
 
136
 
 
137
    def test_registry_converter(self):
 
138
        options = [option.RegistryOption('format', '',
 
139
                   bzrdir.format_registry, bzrdir.format_registry.make_bzrdir)]
 
140
        opts, args = self.parse(options, ['--format', 'knit'])
 
141
        self.assertIsInstance(opts.format.repository_format,
 
142
                              knitrepo.RepositoryFormatKnit1)
 
143
 
 
144
    def test_from_kwargs(self):
 
145
        my_option = option.RegistryOption.from_kwargs('my-option',
 
146
            help='test option', short='be short', be_long='go long')
 
147
        self.assertEqual(['my-option'],
 
148
            [x[0] for x in my_option.iter_switches()])
 
149
        my_option = option.RegistryOption.from_kwargs('my-option',
 
150
            help='test option', title="My option", short='be short',
 
151
            be_long='go long', value_switches=True)
 
152
        self.assertEqual(['my-option', 'be-long', 'short'],
 
153
            [x[0] for x in my_option.iter_switches()])
 
154
 
 
155
    def test_help(self):
 
156
        registry = bzrdir.BzrDirFormatRegistry()
 
157
        registry.register_metadir('one', 'RepositoryFormat7', 'one help')
 
158
        registry.register_metadir('two',
 
159
            'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
 
160
            'two help',
 
161
            )
 
162
        registry.register_metadir('hidden', 'RepositoryFormat7', 'hidden help',
 
163
            hidden=True)
 
164
        registry.set_default('one')
 
165
        options = [option.RegistryOption('format', 'format help', registry,
 
166
                   str, value_switches=True, title='Formats')]
 
167
        parser = option.get_optparser(dict((o.name, o) for o in options))
 
168
        value = parser.format_option_help()
 
169
        self.assertContainsRe(value, 'format.*format help')
 
170
        self.assertContainsRe(value, 'one.*one help')
 
171
        self.assertContainsRe(value, 'Formats:\n *--format')
 
172
        self.assertNotContainsRe(value, 'hidden help')
 
173
 
 
174
    def test_iter_switches(self):
 
175
        opt = option.Option('hello', help='fg')
 
176
        self.assertEqual(list(opt.iter_switches()),
 
177
                         [('hello', None, None, 'fg')])
 
178
        opt = option.Option('hello', help='fg', type=int)
 
179
        self.assertEqual(list(opt.iter_switches()),
 
180
                         [('hello', None, 'ARG', 'fg')])
 
181
        opt = option.Option('hello', help='fg', type=int, argname='gar')
 
182
        self.assertEqual(list(opt.iter_switches()),
 
183
                         [('hello', None, 'GAR', 'fg')])
 
184
        registry = bzrdir.BzrDirFormatRegistry()
 
185
        registry.register_metadir('one', 'RepositoryFormat7', 'one help')
 
186
        registry.register_metadir('two',
 
187
                'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
 
188
                'two help',
 
189
                )
 
190
        registry.set_default('one')
 
191
        opt = option.RegistryOption('format', 'format help', registry,
 
192
                                    value_switches=False)
 
193
        self.assertEqual(list(opt.iter_switches()),
 
194
                         [('format', None, 'ARG', 'format help')])
 
195
        opt = option.RegistryOption('format', 'format help', registry,
 
196
                                    value_switches=True)
 
197
        self.assertEqual(list(opt.iter_switches()),
 
198
                         [('format', None, 'ARG', 'format help'),
 
199
                          ('default', None, None, 'one help'),
 
200
                          ('one', None, None, 'one help'),
 
201
                          ('two', None, None, 'two help'),
 
202
                          ])
 
203
 
 
204
 
 
205
class TestListOptions(TestCase):
 
206
    """Tests for ListOption, used to specify lists on the command-line."""
 
207
 
 
208
    def parse(self, options, args):
 
209
        parser = option.get_optparser(dict((o.name, o) for o in options))
 
210
        return parser.parse_args(args)
 
211
 
 
212
    def test_list_option(self):
 
213
        options = [option.ListOption('hello', type=str)]
 
214
        opts, args = self.parse(options, ['--hello=world', '--hello=sailor'])
 
215
        self.assertEqual(['world', 'sailor'], opts.hello)
 
216
 
 
217
    def test_list_option_no_arguments(self):
 
218
        options = [option.ListOption('hello', type=str)]
 
219
        opts, args = self.parse(options, [])
 
220
        self.assertEqual([], opts.hello)
 
221
 
 
222
    def test_list_option_with_int_type(self):
 
223
        options = [option.ListOption('hello', type=int)]
 
224
        opts, args = self.parse(options, ['--hello=2', '--hello=3'])
 
225
        self.assertEqual([2, 3], opts.hello)
 
226
 
 
227
    def test_list_option_with_int_type_can_be_reset(self):
 
228
        options = [option.ListOption('hello', type=int)]
 
229
        opts, args = self.parse(options, ['--hello=2', '--hello=3',
 
230
                                          '--hello=-', '--hello=5'])
 
231
        self.assertEqual([5], opts.hello)
 
232
 
 
233
    def test_list_option_can_be_reset(self):
 
234
        """Passing an option of '-' to a list option should reset the list."""
 
235
        options = [option.ListOption('hello', type=str)]
 
236
        opts, args = self.parse(
 
237
            options, ['--hello=a', '--hello=b', '--hello=-', '--hello=c'])
 
238
        self.assertEqual(['c'], opts.hello)
 
239
 
 
240
 
 
241
class TestOptionDefinitions(TestCase):
 
242
    """Tests for options in the Bazaar codebase."""
 
243
 
 
244
    def get_all_options(self):
 
245
        """Return a list of all options used by Bazaar, both global and command.
 
246
        
 
247
        The list returned contains elements of (scope, option) where 'scope' 
 
248
        is either None for global options, or a command name.
 
249
 
 
250
        This includes options provided by plugins.
 
251
        """
 
252
        g = [(None, opt) for name, opt
 
253
             in sorted(option.Option.OPTIONS.items())]
 
254
        for cmd_name, cmd_class in sorted(commands.get_all_cmds()):
 
255
            cmd = cmd_class()
 
256
            for opt_name, opt in sorted(cmd.options().items()):
 
257
                g.append((cmd_name, opt))
 
258
        return g
 
259
 
 
260
    def test_get_all_options(self):
 
261
        all = self.get_all_options()
 
262
        self.assertTrue(len(all) > 100,
 
263
                "too few options found: %r" % all)
 
264
 
 
265
    def test_global_options_used(self):
 
266
        # In the distant memory, options could only be declared globally.  Now
 
267
        # we prefer to declare them in the command, unless like -r they really
 
268
        # are used very widely with the exact same meaning.  So this checks
 
269
        # for any that should be garbage collected.
 
270
        g = dict(option.Option.OPTIONS.items())
 
271
        used_globals = {}
 
272
        msgs = []
 
273
        for cmd_name, cmd_class in sorted(commands.get_all_cmds()):
 
274
            for option_or_name in sorted(cmd_class.takes_options):
 
275
                if not isinstance(option_or_name, basestring):
 
276
                    self.assertIsInstance(option_or_name, option.Option)
 
277
                elif not option_or_name in g:
 
278
                    msgs.append("apparent reference to undefined "
 
279
                        "global option %r from %r"
 
280
                        % (option_or_name, cmd_class))
 
281
                else:
 
282
                    used_globals.setdefault(option_or_name, []).append(cmd_name)
 
283
        unused_globals = set(g.keys()) - set(used_globals.keys())
 
284
        for option_name in sorted(unused_globals):
 
285
            msgs.append("unused global option %r" % option_name)
 
286
        for option_name, cmds in sorted(used_globals.items()):
 
287
            if len(cmds) <= 1:
 
288
                msgs.append("global option %r is only used by %r"
 
289
                        % (option_name, cmds))
 
290
        if msgs:
 
291
            self.fail("problems with global option definitions:\n"
 
292
                    + '\n'.join(msgs))
 
293
 
 
294
    def test_option_grammar(self):
 
295
        msgs = []
 
296
        # Option help should be written in sentence form, and have a final
 
297
        # period and be all on a single line, because the display code will
 
298
        # wrap it.
 
299
        option_re = re.compile(r'^[A-Z][^\n]+\.$')
 
300
        for scope, option in self.get_all_options():
 
301
            if not option.help:
 
302
                msgs.append('%-16s %-16s %s' %
 
303
                       ((scope or 'GLOBAL'), option.name, 'NO HELP'))
 
304
            elif not option_re.match(option.help):
 
305
                msgs.append('%-16s %-16s %s' %
 
306
                        ((scope or 'GLOBAL'), option.name, option.help))
 
307
        if msgs:
 
308
            self.fail("The following options don't match the style guide:\n"
 
309
                    + '\n'.join(msgs))
 
310
 
 
311
    # TODO: Scan for global options that aren't used by any command?
 
312
    #
 
313
    # TODO: Check that there are two spaces between sentences.