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.tests import features
20
from bzrlib.plugins.bash_completion.bashcomp import *
26
class BashCompletionMixin(object):
27
"""Component for testing execution of a bash completion script."""
29
_test_needs_features = [features.bash_feature]
31
def complete(self, words, cword=-1):
32
"""Perform a bash completion.
34
:param words: a list of words representing the current command.
35
:param cword: the current word to complete, defaults to the last one.
37
if self.script is None:
38
self.script = self.get_script()
39
proc = subprocess.Popen([features.bash_feature.path,
41
stdin=subprocess.PIPE,
42
stdout=subprocess.PIPE,
43
stderr=subprocess.PIPE)
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)
56
raise AssertionError('Unexpected error message:\n%s' % err)
57
self.assertEqual('', err, 'No messages to standard error')
59
#print >>sys.stdout, '---\n%s\n---\n%s\n---\n' % (input, out)
60
lines = out.split('\n')
61
nlines = int(lines[0])
63
self.assertEqual('', lines[-1], 'Newline at end')
65
if nlines == 0 and len(lines) == 1 and lines[0] == '':
67
self.assertEqual(nlines, len(lines), 'No newlines in generated words')
68
self.completion_result = set(lines)
69
return self.completion_result
71
def assertCompletionEquals(self, *words):
72
self.assertEqual(set(words), self.completion_result)
74
def assertCompletionContains(self, *words):
75
missing = set(words) - self.completion_result
77
raise AssertionError('Completion should contain %r but it has %r'
78
% (missing, self.completion_result))
80
def assertCompletionOmits(self, *words):
81
surplus = set(words) & self.completion_result
83
raise AssertionError('Completion should omit %r but it has %r'
84
% (surplus, res, self.completion_result))
87
commands.install_bzr_command_hooks()
90
cg = BashCodeGen(data)
95
class TestBashCompletion(tests.TestCase, BashCompletionMixin):
96
"""Test bash completions that don't execute bzr."""
98
def __init__(self, methodName='testMethod'):
99
super(TestBashCompletion, self).__init__(methodName)
102
def test_simple_scipt(self):
103
"""Ensure that the test harness works as expected"""
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]}+" )
111
# and append the current word
112
COMPREPLY+=( "+${COMP_WORDS[COMP_CWORD]}-" )
115
self.complete(['foo', '"bar', "'baz"], cword=1)
116
self.assertCompletionEquals("-'baz+", '-"bar+', '-foo+', '+"bar-')
118
def test_cmd_ini(self):
119
self.complete(['bzr', 'ini'])
120
self.assertCompletionContains('init', 'init-repo', 'init-repository')
121
self.assertCompletionOmits('commit')
123
def test_init_opts(self):
124
self.complete(['bzr', 'init', '-'])
125
self.assertCompletionContains('-h', '--2a', '--format=2a')
127
def test_global_opts(self):
128
self.complete(['bzr', '-', 'init'], cword=1)
129
self.assertCompletionContains('--no-plugins', '--builtin')
131
def test_commit_dashm(self):
132
self.complete(['bzr', 'commit', '-m'])
133
self.assertCompletionEquals('-m')
135
def test_status_negated(self):
136
self.complete(['bzr', 'status', '--n'])
137
self.assertCompletionContains('--no-versioned', '--no-verbose')
139
def test_init_format_any(self):
140
self.complete(['bzr', 'init', '--format', '=', 'directory'], cword=3)
141
self.assertCompletionContains('1.9', '2a')
143
def test_init_format_2(self):
144
self.complete(['bzr', 'init', '--format', '=', '2', 'directory'],
146
self.assertCompletionContains('2a')
147
self.assertCompletionOmits('1.9')
150
class TestBashCompletionInvoking(tests.TestCaseWithTransport,
151
BashCompletionMixin):
152
"""Test bash completions that might execute bzr.
154
Only the syntax ``$(bzr ...`` is supported so far. The bzr command
155
will be replaced by the bzr instance running this selftest.
158
def __init__(self, methodName='testMethod'):
159
super(TestBashCompletionInvoking, self).__init__(methodName)
162
def get_script(self):
163
s = super(TestBashCompletionInvoking, self).get_script()
164
return s.replace("$(bzr ", "$('%s' " % self.get_bzr_path())
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')
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')
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')
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')
208
class TestBashCodeGen(tests.TestCase):
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())
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())
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:
234
# foo 1.0''' % bzrlib.version_string, cg.bzr_version())
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())
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('''\
253
\t\tcmdOpts=( --opt )
258
''', cg.command_cases())
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('''\
273
\t\t# plugin "plugger 1.0"
274
\t\t# Some error message
275
\t\tcmdOpts=( --bar=that --bar=this --foo )
277
\t\t\t--bar) optEnums=( that this ) ;;
280
''', cg.command_case(cmd))
283
class TestDataCollector(tests.TestCase):
286
super(TestDataCollector, self).setUp()
287
commands.install_bzr_command_hooks()
289
def test_global_options(self):
292
self.assertSubset(['--no-plugins', '--builtin'],
293
dc.data.global_options)
295
def test_commands(self):
298
self.assertSubset(['init', 'init-repo', 'init-repository'],
299
dc.data.all_command_aliases())
301
def test_commands_from_plugins(self):
304
self.assertSubset(['bash-completion'],
305
dc.data.all_command_aliases())
307
def test_commit_dashm(self):
309
cmd = dc.command('commit')
310
self.assertSubset(['-m'],
311
[str(o) for o in cmd.options])
313
def test_status_negated(self):
315
cmd = dc.command('status')
316
self.assertSubset(['--no-versioned', '--no-verbose'],
317
[str(o) for o in cmd.options])
319
def test_init_format(self):
321
cmd = dc.command('init')
322
for opt in cmd.options:
323
if opt.name == '--format':
324
self.assertSubset(['2a'], opt.registry_keys)
326
raise AssertionError('Option --format not found')