1
# Copyright (C) 2010 by 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
18
from bzrlib import commands, tests
19
from bzrlib.plugins.bash_completion.bashcomp import *
25
class _BashFeature(tests.Feature):
26
"""Feature testing whether a bash executable is available."""
28
bash_paths = ['/bin/bash', '/usr/bin/bash']
31
super(_BashFeature, self).__init__()
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):
41
self.bash_path = False
44
def feature_name(self):
47
BashFeature = _BashFeature()
50
class BashCompletionMixin(object):
51
"""Component for testing execution of a bash completion script."""
53
_test_needs_features = [BashFeature]
55
def complete(self, words, cword=-1):
56
"""Perform a bash completion.
58
:param words: a list of words representing the current command.
59
:param cword: the current word to complete, defaults to the last one.
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)
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)
79
raise AssertionError('Unexpected error message:\n%s' % err)
80
self.assertEqual('', err, 'No messages to standard error')
82
#print >>sys.stdout, '---\n%s\n---\n%s\n---\n' % (input, out)
83
lines = out.split('\n')
84
nlines = int(lines[0])
86
self.assertEqual('', lines[-1], 'Newline at end')
88
if nlines == 0 and len(lines) == 1 and lines[0] == '':
90
self.assertEqual(nlines, len(lines), 'No newlines in generated words')
91
self.completion_result = set(lines)
92
return self.completion_result
94
def assertCompletionEquals(self, *words):
95
self.assertEqual(set(words), self.completion_result)
97
def assertCompletionContains(self, *words):
98
missing = set(words) - self.completion_result
100
raise AssertionError('Completion should contain %r but it has %r'
101
% (missing, self.completion_result))
103
def assertCompletionOmits(self, *words):
104
surplus = set(words) & self.completion_result
106
raise AssertionError('Completion should omit %r but it has %r'
107
% (surplus, res, self.completion_result))
109
def get_script(self):
110
commands.install_bzr_command_hooks()
113
cg = BashCodeGen(data)
118
class TestBashCompletion(tests.TestCase, BashCompletionMixin):
119
"""Test bash completions that don't execute bzr."""
121
def __init__(self, methodName='testMethod'):
122
super(TestBashCompletion, self).__init__(methodName)
125
def test_simple_scipt(self):
126
"""Ensure that the test harness works as expected"""
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]}+" )
134
# and append the current word
135
COMPREPLY+=( "+${COMP_WORDS[COMP_CWORD]}-" )
138
self.complete(['foo', '"bar', "'baz"], cword=1)
139
self.assertCompletionEquals("-'baz+", '-"bar+', '-foo+', '+"bar-')
141
def test_cmd_ini(self):
142
self.complete(['bzr', 'ini'])
143
self.assertCompletionContains('init', 'init-repo', 'init-repository')
144
self.assertCompletionOmits('commit')
146
def test_init_opts(self):
147
self.complete(['bzr', 'init', '-'])
148
self.assertCompletionContains('-h', '--2a', '--format=2a')
150
def test_global_opts(self):
151
self.complete(['bzr', '-', 'init'], cword=1)
152
self.assertCompletionContains('--no-plugins', '--builtin')
154
def test_commit_dashm(self):
155
self.complete(['bzr', 'commit', '-m'])
156
self.assertCompletionEquals('-m')
158
def test_status_negated(self):
159
self.complete(['bzr', 'status', '--n'])
160
self.assertCompletionContains('--no-versioned', '--no-verbose')
162
def test_init_format_any(self):
163
self.complete(['bzr', 'init', '--format', '=', 'directory'], cword=3)
164
self.assertCompletionContains('1.9', '2a')
166
def test_init_format_2(self):
167
self.complete(['bzr', 'init', '--format', '=', '2', 'directory'],
169
self.assertCompletionContains('2a')
170
self.assertCompletionOmits('1.9')
173
class TestBashCompletionInvoking(tests.TestCaseWithTransport,
174
BashCompletionMixin):
175
"""Test bash completions that might execute bzr.
177
Only the syntax ``$(bzr ...`` is supported so far. The bzr command
178
will be replaced by the bzr instance running this selftest.
181
def __init__(self, methodName='testMethod'):
182
super(TestBashCompletionInvoking, self).__init__(methodName)
185
def get_script(self):
186
s = super(TestBashCompletionInvoking, self).get_script()
187
return s.replace("$(bzr ", "$('%s' " % self.get_bzr_path())
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')
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')
206
class TestBashCodeGen(tests.TestCase):
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())
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())
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:
232
# foo 1.0''' % bzrlib.version_string, cg.bzr_version())
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())
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('''\
256
''', cg.command_cases())
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('''\
271
\t\t# plugin "plugger 1.0"
272
\t\t# Some error message
273
\t\tcmdOpts='--bar=that --bar=this --foo'
275
\t\t\t--bar) optEnums='that this' ;;
278
''', cg.command_case(cmd))
281
class TestDataCollector(tests.TestCase):
284
super(TestDataCollector, self).setUp()
285
commands.install_bzr_command_hooks()
287
def test_global_options(self):
290
self.assertSubset(['--no-plugins', '--builtin'],
291
dc.data.global_options)
293
def test_commands(self):
296
self.assertSubset(['init', 'init-repo', 'init-repository'],
297
dc.data.all_command_aliases())
299
def test_commit_dashm(self):
301
cmd = dc.command('commit')
302
self.assertSubset(['-m'],
303
[str(o) for o in cmd.options])
305
def test_status_negated(self):
307
cmd = dc.command('status')
308
self.assertSubset(['--no-versioned', '--no-verbose'],
309
[str(o) for o in cmd.options])
311
def test_init_format(self):
313
cmd = dc.command('init')
314
for opt in cmd.options:
315
if opt.name == '--format':
316
self.assertSubset(['2a'], opt.registry_keys)
318
raise AssertionError('Option --format not found')