~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/plugins/bash_completion/tests/test_bashcomp.py

  • Committer: Martin Pool
  • Date: 2010-04-01 04:41:18 UTC
  • mto: This revision was merged to the branch mainline in revision 5128.
  • Revision ID: mbp@sourcefrog.net-20100401044118-shyctqc02ob08ngz
ignore .testrepository

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2010 by 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 bzrlib
18
 
from bzrlib import commands, tests
19
 
from bzrlib.tests import features
20
 
from bzrlib.plugins.bash_completion.bashcomp import *
21
 
 
22
 
import os
23
 
import subprocess
24
 
 
25
 
 
26
 
class BashCompletionMixin(object):
27
 
    """Component for testing execution of a bash completion script."""
28
 
 
29
 
    _test_needs_features = [features.bash_feature]
30
 
 
31
 
    def complete(self, words, cword=-1):
32
 
        """Perform a bash completion.
33
 
 
34
 
        :param words: a list of words representing the current command.
35
 
        :param cword: the current word to complete, defaults to the last one.
36
 
        """
37
 
        if self.script is None:
38
 
            self.script = self.get_script()
39
 
        proc = subprocess.Popen([features.bash_feature.path,
40
 
                                 '--noprofile'],
41
 
                                stdin=subprocess.PIPE,
42
 
                                stdout=subprocess.PIPE,
43
 
                                stderr=subprocess.PIPE)
44
 
        if cword < 0:
45
 
            cword = len(words) + cword
46
 
        input = '%s\n' % self.script
47
 
        input += ('COMP_WORDS=( %s )\n' %
48
 
                  ' '.join(["'"+w.replace("'", "'\\''")+"'" for w in words]))
49
 
        input += 'COMP_CWORD=%d\n' % cword
50
 
        input += '%s\n' % getattr(self, 'script_name', '_bzr')
51
 
        input += 'echo ${#COMPREPLY[*]}\n'
52
 
        input += "IFS=$'\\n'\n"
53
 
        input += 'echo "${COMPREPLY[*]}"\n'
54
 
        (out, err) = proc.communicate(input)
55
 
        if '' != err:
56
 
            raise AssertionError('Unexpected error message:\n%s' % err)
57
 
        self.assertEqual('', err, 'No messages to standard error')
58
 
        #import sys
59
 
        #print >>sys.stdout, '---\n%s\n---\n%s\n---\n' % (input, out)
60
 
        lines = out.split('\n')
61
 
        nlines = int(lines[0])
62
 
        del lines[0]
63
 
        self.assertEqual('', lines[-1], 'Newline at end')
64
 
        del lines[-1]
65
 
        if nlines == 0 and len(lines) == 1 and lines[0] == '':
66
 
            del lines[0]
67
 
        self.assertEqual(nlines, len(lines), 'No newlines in generated words')
68
 
        self.completion_result = set(lines)
69
 
        return self.completion_result
70
 
 
71
 
    def assertCompletionEquals(self, *words):
72
 
        self.assertEqual(set(words), self.completion_result)
73
 
 
74
 
    def assertCompletionContains(self, *words):
75
 
        missing = set(words) - self.completion_result
76
 
        if missing:
77
 
            raise AssertionError('Completion should contain %r but it has %r'
78
 
                                 % (missing, self.completion_result))
79
 
 
80
 
    def assertCompletionOmits(self, *words):
81
 
        surplus = set(words) & self.completion_result
82
 
        if surplus:
83
 
            raise AssertionError('Completion should omit %r but it has %r'
84
 
                                 % (surplus, res, self.completion_result))
85
 
 
86
 
    def get_script(self):
87
 
        commands.install_bzr_command_hooks()
88
 
        dc = DataCollector()
89
 
        data = dc.collect()
90
 
        cg = BashCodeGen(data)
91
 
        res = cg.function()
92
 
        return res
93
 
 
94
 
 
95
 
class TestBashCompletion(tests.TestCase, BashCompletionMixin):
96
 
    """Test bash completions that don't execute bzr."""
97
 
 
98
 
    def __init__(self, methodName='testMethod'):
99
 
        super(TestBashCompletion, self).__init__(methodName)
100
 
        self.script = None
101
 
 
102
 
    def test_simple_scipt(self):
103
 
        """Ensure that the test harness works as expected"""
104
 
        self.script = """
105
 
_bzr() {
106
 
    COMPREPLY=()
107
 
    # add all words in reverse order, with some markup around them
108
 
    for ((i = ${#COMP_WORDS[@]}; i > 0; --i)); do
109
 
        COMPREPLY+=( "-${COMP_WORDS[i-1]}+" )
110
 
    done
111
 
    # and append the current word
112
 
    COMPREPLY+=( "+${COMP_WORDS[COMP_CWORD]}-" )
113
 
}
114
 
"""
115
 
        self.complete(['foo', '"bar', "'baz"], cword=1)
116
 
        self.assertCompletionEquals("-'baz+", '-"bar+', '-foo+', '+"bar-')
117
 
 
118
 
    def test_cmd_ini(self):
119
 
        self.complete(['bzr', 'ini'])
120
 
        self.assertCompletionContains('init', 'init-repo', 'init-repository')
121
 
        self.assertCompletionOmits('commit')
122
 
 
123
 
    def test_init_opts(self):
124
 
        self.complete(['bzr', 'init', '-'])
125
 
        self.assertCompletionContains('-h', '--2a', '--format=2a')
126
 
 
127
 
    def test_global_opts(self):
128
 
        self.complete(['bzr', '-', 'init'], cword=1)
129
 
        self.assertCompletionContains('--no-plugins', '--builtin')
130
 
 
131
 
    def test_commit_dashm(self):
132
 
        self.complete(['bzr', 'commit', '-m'])
133
 
        self.assertCompletionEquals('-m')
134
 
 
135
 
    def test_status_negated(self):
136
 
        self.complete(['bzr', 'status', '--n'])
137
 
        self.assertCompletionContains('--no-versioned', '--no-verbose')
138
 
 
139
 
    def test_init_format_any(self):
140
 
        self.complete(['bzr', 'init', '--format', '=', 'directory'], cword=3)
141
 
        self.assertCompletionContains('1.9', '2a')
142
 
 
143
 
    def test_init_format_2(self):
144
 
        self.complete(['bzr', 'init', '--format', '=', '2', 'directory'],
145
 
                      cword=4)
146
 
        self.assertCompletionContains('2a')
147
 
        self.assertCompletionOmits('1.9')
148
 
 
149
 
 
150
 
class TestBashCompletionInvoking(tests.TestCaseWithTransport,
151
 
                                 BashCompletionMixin):
152
 
    """Test bash completions that might execute bzr.
153
 
 
154
 
    Only the syntax ``$(bzr ...`` is supported so far. The bzr command
155
 
    will be replaced by the bzr instance running this selftest.
156
 
    """
157
 
 
158
 
    def __init__(self, methodName='testMethod'):
159
 
        super(TestBashCompletionInvoking, self).__init__(methodName)
160
 
        self.script = None
161
 
 
162
 
    def get_script(self):
163
 
        s = super(TestBashCompletionInvoking, self).get_script()
164
 
        return s.replace("$(bzr ", "$('%s' " % self.get_bzr_path())
165
 
 
166
 
    def test_revspec_tag_all(self):
167
 
        self.requireFeature(features.sed_feature)
168
 
        wt = self.make_branch_and_tree('.', format='dirstate-tags')
169
 
        wt.branch.tags.set_tag('tag1', 'null:')
170
 
        wt.branch.tags.set_tag('tag2', 'null:')
171
 
        wt.branch.tags.set_tag('3tag', 'null:')
172
 
        self.complete(['bzr', 'log', '-r', 'tag', ':'])
173
 
        self.assertCompletionEquals('tag1', 'tag2', '3tag')
174
 
 
175
 
    def test_revspec_tag_prefix(self):
176
 
        self.requireFeature(features.sed_feature)
177
 
        wt = self.make_branch_and_tree('.', format='dirstate-tags')
178
 
        wt.branch.tags.set_tag('tag1', 'null:')
179
 
        wt.branch.tags.set_tag('tag2', 'null:')
180
 
        wt.branch.tags.set_tag('3tag', 'null:')
181
 
        self.complete(['bzr', 'log', '-r', 'tag', ':', 't'])
182
 
        self.assertCompletionEquals('tag1', 'tag2')
183
 
 
184
 
    def test_revspec_tag_spaces(self):
185
 
        self.requireFeature(features.sed_feature)
186
 
        wt = self.make_branch_and_tree('.', format='dirstate-tags')
187
 
        wt.branch.tags.set_tag('tag with spaces', 'null:')
188
 
        self.complete(['bzr', 'log', '-r', 'tag', ':', 't'])
189
 
        self.assertCompletionEquals(r'tag\ with\ spaces')
190
 
        self.complete(['bzr', 'log', '-r', '"tag:t'])
191
 
        self.assertCompletionEquals('tag:tag with spaces')
192
 
        self.complete(['bzr', 'log', '-r', "'tag:t"])
193
 
        self.assertCompletionEquals('tag:tag with spaces')
194
 
 
195
 
    def test_revspec_tag_endrange(self):
196
 
        self.requireFeature(features.sed_feature)
197
 
        wt = self.make_branch_and_tree('.', format='dirstate-tags')
198
 
        wt.branch.tags.set_tag('tag1', 'null:')
199
 
        wt.branch.tags.set_tag('tag2', 'null:')
200
 
        self.complete(['bzr', 'log', '-r', '3..tag', ':', 't'])
201
 
        self.assertCompletionEquals('tag1', 'tag2')
202
 
        self.complete(['bzr', 'log', '-r', '"3..tag:t'])
203
 
        self.assertCompletionEquals('3..tag:tag1', '3..tag:tag2')
204
 
        self.complete(['bzr', 'log', '-r', "'3..tag:t"])
205
 
        self.assertCompletionEquals('3..tag:tag1', '3..tag:tag2')
206
 
 
207
 
 
208
 
class TestBashCodeGen(tests.TestCase):
209
 
 
210
 
    def test_command_names(self):
211
 
        data = CompletionData()
212
 
        bar = CommandData('bar')
213
 
        bar.aliases.append('baz')
214
 
        data.commands.append(bar)
215
 
        data.commands.append(CommandData('foo'))
216
 
        cg = BashCodeGen(data)
217
 
        self.assertEqual('bar baz foo', cg.command_names())
218
 
 
219
 
    def test_debug_output(self):
220
 
        data = CompletionData()
221
 
        self.assertEqual('', BashCodeGen(data, debug=False).debug_output())
222
 
        self.assertTrue(BashCodeGen(data, debug=True).debug_output())
223
 
 
224
 
    def test_bzr_version(self):
225
 
        data = CompletionData()
226
 
        cg = BashCodeGen(data)
227
 
        self.assertEqual('%s.' % bzrlib.version_string, cg.bzr_version())
228
 
        data.plugins['foo'] = PluginData('foo', '1.0')
229
 
        data.plugins['bar'] = PluginData('bar', '2.0')
230
 
        cg = BashCodeGen(data)
231
 
        self.assertEqual('''\
232
 
%s and the following plugins:
233
 
# bar 2.0
234
 
# foo 1.0''' % bzrlib.version_string, cg.bzr_version())
235
 
 
236
 
    def test_global_options(self):
237
 
        data = CompletionData()
238
 
        data.global_options.add('--foo')
239
 
        data.global_options.add('--bar')
240
 
        cg = BashCodeGen(data)
241
 
        self.assertEqual('--bar --foo', cg.global_options())
242
 
 
243
 
    def test_command_cases(self):
244
 
        data = CompletionData()
245
 
        bar = CommandData('bar')
246
 
        bar.aliases.append('baz')
247
 
        bar.options.append(OptionData('--opt'))
248
 
        data.commands.append(bar)
249
 
        data.commands.append(CommandData('foo'))
250
 
        cg = BashCodeGen(data)
251
 
        self.assertEqualDiff('''\
252
 
\tbar|baz)
253
 
\t\tcmdOpts=( --opt )
254
 
\t\t;;
255
 
\tfoo)
256
 
\t\tcmdOpts=(  )
257
 
\t\t;;
258
 
''', cg.command_cases())
259
 
 
260
 
    def test_command_case(self):
261
 
        cmd = CommandData('cmd')
262
 
        cmd.plugin = PluginData('plugger', '1.0')
263
 
        bar = OptionData('--bar')
264
 
        bar.registry_keys = ['that', 'this']
265
 
        bar.error_messages.append('Some error message')
266
 
        cmd.options.append(bar)
267
 
        cmd.options.append(OptionData('--foo'))
268
 
        data = CompletionData()
269
 
        data.commands.append(cmd)
270
 
        cg = BashCodeGen(data)
271
 
        self.assertEqualDiff('''\
272
 
\tcmd)
273
 
\t\t# plugin "plugger 1.0"
274
 
\t\t# Some error message
275
 
\t\tcmdOpts=( --bar=that --bar=this --foo )
276
 
\t\tcase $curOpt in
277
 
\t\t\t--bar) optEnums=( that this ) ;;
278
 
\t\tesac
279
 
\t\t;;
280
 
''', cg.command_case(cmd))
281
 
 
282
 
 
283
 
class TestDataCollector(tests.TestCase):
284
 
 
285
 
    def setUp(self):
286
 
        super(TestDataCollector, self).setUp()
287
 
        commands.install_bzr_command_hooks()
288
 
 
289
 
    def test_global_options(self):
290
 
        dc = DataCollector()
291
 
        dc.global_options()
292
 
        self.assertSubset(['--no-plugins', '--builtin'],
293
 
                           dc.data.global_options)
294
 
 
295
 
    def test_commands(self):
296
 
        dc = DataCollector()
297
 
        dc.commands()
298
 
        self.assertSubset(['init', 'init-repo', 'init-repository'],
299
 
                           dc.data.all_command_aliases())
300
 
 
301
 
    def test_commands_from_plugins(self):
302
 
        dc = DataCollector()
303
 
        dc.commands()
304
 
        self.assertSubset(['bash-completion'],
305
 
                           dc.data.all_command_aliases())
306
 
 
307
 
    def test_commit_dashm(self):
308
 
        dc = DataCollector()
309
 
        cmd = dc.command('commit')
310
 
        self.assertSubset(['-m'],
311
 
                           [str(o) for o in cmd.options])
312
 
 
313
 
    def test_status_negated(self):
314
 
        dc = DataCollector()
315
 
        cmd = dc.command('status')
316
 
        self.assertSubset(['--no-versioned', '--no-verbose'],
317
 
                           [str(o) for o in cmd.options])
318
 
 
319
 
    def test_init_format(self):
320
 
        dc = DataCollector()
321
 
        cmd = dc.command('init')
322
 
        for opt in cmd.options:
323
 
            if opt.name == '--format':
324
 
                self.assertSubset(['2a'], opt.registry_keys)
325
 
                return
326
 
        raise AssertionError('Option --format not found')