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
17
from bzrlib.tests import TestCase, TestCaseWithTransport, Feature
18
from bzrlib import commands
19
from StringIO import StringIO
20
from ..bashcomp import *
26
class _BashFeature(Feature):
27
"""Feature testing whether a bash executable is available."""
29
bash_paths = ['/bin/bash', '/usr/bin/bash']
32
super(_BashFeature, self).__init__()
36
if self.bash_path is not None:
37
return self.bash_path is not False
38
for path in self.bash_paths:
39
if os.access(path, os.X_OK):
42
self.bash_path = False
45
def feature_name(self):
48
BashFeature = _BashFeature()
51
class BashCompletionMixin(object):
52
"""Component for testing execution of a bash completion script."""
54
_test_needs_features = [BashFeature]
56
def complete(self, words, cword=-1):
57
"""Perform a bash completion.
59
:param words: a list of words representing the current command.
60
:param cword: the current word to complete, defaults to the last one.
62
if self.script is None:
63
self.script = self.get_script()
64
proc = subprocess.Popen([BashFeature.bash_path, '--noprofile'],
65
stdin=subprocess.PIPE,
66
stdout=subprocess.PIPE,
67
stderr=subprocess.PIPE)
69
cword = len(words) + cword
70
input = '%s\n' % self.script
71
input += ('COMP_WORDS=( %s )\n' %
72
' '.join(["'"+w.replace("'", "'\\''")+"'" for w in words]))
73
input += 'COMP_CWORD=%d\n' % cword
74
input += '%s\n' % getattr(self, 'script_name', '_bzr')
75
input += 'echo ${#COMPREPLY[*]}\n'
76
input += "IFS=$'\\n'\n"
77
input += 'echo "${COMPREPLY[*]}"\n'
78
(out, err) = proc.communicate(input)
80
raise AssertionError('Unexpected error message:\n%s' % err)
81
self.assertEqual('', err, 'No messages to standard error')
83
#print >>sys.stdout, '---\n%s\n---\n%s\n---\n' % (input, out)
84
lines = out.split('\n')
85
nlines = int(lines[0])
87
self.assertEqual('', lines[-1], 'Newline at end')
89
if nlines == 0 and len(lines) == 1 and lines[0] == '':
91
self.assertEqual(nlines, len(lines), 'No newlines in generated words')
92
self.completion_result = set(lines)
93
return self.completion_result
95
def assertCompletionEquals(self, *words):
96
self.assertEqual(set(words), self.completion_result)
98
def assertCompletionContains(self, *words):
99
missing = set(words) - self.completion_result
101
raise AssertionError('Completion should contain %r but it has %r'
102
% (missing, self.completion_result))
104
def assertCompletionOmits(self, *words):
105
surplus = set(words) & self.completion_result
107
raise AssertionError('Completion should omit %r but it has %r'
108
% (surplus, res, self.completion_result))
110
def get_script(self):
112
bash_completion_function(out, function_only=True)
113
return out.getvalue()
116
class TestBashCompletion(TestCase, BashCompletionMixin):
117
"""Test bash completions that don't execute bzr."""
119
def __init__(self, methodName='testMethod'):
120
super(TestBashCompletion, self).__init__(methodName)
124
super(TestBashCompletion, self).setUp()
125
commands.install_bzr_command_hooks()
127
def test_simple_scipt(self):
128
"""Ensure that the test harness works as expected"""
132
# add all words in reverse order, with some markup around them
133
for ((i = ${#COMP_WORDS[@]}; i > 0; --i)); do
134
COMPREPLY+=( "-${COMP_WORDS[i-1]}+" )
136
# and append the current word
137
COMPREPLY+=( "+${COMP_WORDS[COMP_CWORD]}-" )
140
self.complete(['foo', '"bar', "'baz"], cword=1)
141
self.assertCompletionEquals("-'baz+", '-"bar+', '-foo+', '+"bar-')
143
def test_cmd_ini(self):
144
self.complete(['bzr', 'ini'])
145
self.assertCompletionContains('init', 'init-repo', 'init-repository')
146
self.assertCompletionOmits('commit')
148
def test_init_opts(self):
149
self.complete(['bzr', 'init', '-'])
150
self.assertCompletionContains('-h', '--2a', '--format=2a')
152
def test_global_opts(self):
153
self.complete(['bzr', '-', 'init'], cword=1)
154
self.assertCompletionContains('--no-plugins', '--builtin')
156
def test_commit_dashm(self):
157
self.complete(['bzr', 'commit', '-m'])
158
self.assertCompletionEquals('-m')
160
def test_status_negated(self):
161
self.complete(['bzr', 'status', '--n'])
162
self.assertCompletionContains('--no-versioned', '--no-verbose')
164
def test_init_format_any(self):
165
self.complete(['bzr', 'init', '--format', '=', 'directory'], cword=3)
166
self.assertCompletionContains('1.9', '2a')
168
def test_init_format_2(self):
169
self.complete(['bzr', 'init', '--format', '=', '2', 'directory'],
171
self.assertCompletionContains('2a')
172
self.assertCompletionOmits('1.9')
175
class TestBashCompletionInvoking(TestCaseWithTransport, BashCompletionMixin):
176
"""Test bash completions that might execute bzr.
178
Only the syntax ``$(bzr ...`` is supported so far. The bzr command
179
will be replaced by the bzr instance running this selftest.
182
def __init__(self, methodName='testMethod'):
183
super(TestBashCompletionInvoking, self).__init__(methodName)
187
super(TestBashCompletionInvoking, self).setUp()
188
commands.install_bzr_command_hooks()
190
def get_script(self):
191
s = super(TestBashCompletionInvoking, self).get_script()
192
return s.replace("$(bzr ", "$('%s' " % self.get_bzr_path())
194
def test_revspec_tag_all(self):
195
wt = self.make_branch_and_tree('.', format='dirstate-tags')
196
wt.branch.tags.set_tag('tag1', 'null:')
197
wt.branch.tags.set_tag('tag2', 'null:')
198
wt.branch.tags.set_tag('3tag', 'null:')
199
self.complete(['bzr', 'log', '-r', 'tag', ':'])
200
self.assertCompletionEquals('tag1', 'tag2', '3tag')
202
def test_revspec_tag_prefix(self):
203
wt = self.make_branch_and_tree('.', format='dirstate-tags')
204
wt.branch.tags.set_tag('tag1', 'null:')
205
wt.branch.tags.set_tag('tag2', 'null:')
206
wt.branch.tags.set_tag('3tag', 'null:')
207
self.complete(['bzr', 'log', '-r', 'tag', ':', 't'])
208
self.assertCompletionEquals('tag1', 'tag2')
211
class TestBashCodeGen(TestCase):
213
def test_command_names(self):
214
data = CompletionData()
215
bar = CommandData('bar')
216
bar.aliases.append('baz')
217
data.commands.append(bar)
218
data.commands.append(CommandData('foo'))
219
cg = BashCodeGen(data)
220
self.assertEqual('bar baz foo', cg.command_names())
222
def test_debug_output(self):
223
data = CompletionData()
224
self.assertEqual('', BashCodeGen(data, debug=False).debug_output())
225
self.assertTrue(BashCodeGen(data, debug=True).debug_output())
227
def test_bzr_version(self):
228
data = CompletionData()
229
cg = BashCodeGen(data)
230
self.assertEqual('%s.' % bzrlib.version_string, cg.bzr_version())
231
data.plugins['foo'] = PluginData('foo', '1.0')
232
data.plugins['bar'] = PluginData('bar', '2.0')
233
cg = BashCodeGen(data)
234
self.assertEqual('''\
235
%s and the following plugins:
237
# foo 1.0''' % bzrlib.version_string, cg.bzr_version())
239
def test_global_options(self):
240
data = CompletionData()
241
data.global_options.add('--foo')
242
data.global_options.add('--bar')
243
cg = BashCodeGen(data)
244
self.assertEqual('--bar --foo', cg.global_options())
246
def test_command_cases(self):
247
data = CompletionData()
248
bar = CommandData('bar')
249
bar.aliases.append('baz')
250
bar.options.append(OptionData('--opt'))
251
data.commands.append(bar)
252
data.commands.append(CommandData('foo'))
253
cg = BashCodeGen(data)
254
self.assertEqualDiff('''\
261
''', cg.command_cases())
263
def test_command_case(self):
264
cmd = CommandData('cmd')
265
cmd.plugin = PluginData('plugger', '1.0')
266
bar = OptionData('--bar')
267
bar.registry_keys = ['that', 'this']
268
bar.error_messages.append('Some error message')
269
cmd.options.append(bar)
270
cmd.options.append(OptionData('--foo'))
271
data = CompletionData()
272
data.commands.append(cmd)
273
cg = BashCodeGen(data)
274
self.assertEqualDiff('''\
276
\t\t# plugin "plugger 1.0"
277
\t\t# Some error message
278
\t\tcmdOpts='--bar=that --bar=this --foo'
280
\t\t\t--bar) optEnums='that this' ;;
283
''', cg.command_case(cmd))
286
class TestDataCollector(TestCase):
289
super(TestDataCollector, self).setUp()
290
commands.install_bzr_command_hooks()
292
def test_global_options(self):
295
self.assertSubset(['--no-plugins', '--builtin'],
296
dc.data.global_options)
298
def test_commands(self):
301
self.assertSubset(['init', 'init-repo', 'init-repository'],
302
dc.data.all_command_aliases())
304
def test_commit_dashm(self):
306
cmd = dc.command('commit')
307
self.assertSubset(['-m'],
308
[str(o) for o in cmd.options])
310
def test_status_negated(self):
312
cmd = dc.command('status')
313
self.assertSubset(['--no-versioned', '--no-verbose'],
314
[str(o) for o in cmd.options])
316
def test_init_format(self):
318
cmd = dc.command('init')
319
for opt in cmd.options:
320
if opt.name == '--format':
321
self.assertSubset(['2a'], opt.registry_keys)
323
raise AssertionError('Option --format not found')