~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

(lifeless) Replace the unmaintained bzr completion script with gagern's new
 one. (Martin von Gagern)

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