# Copyright 2012-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). You # may not use this file except in compliance with the License. A copy of # the License is located at # # http://aws.amazon.com/apache2.0/ # # or in the "license" file accompanying this file. This file is # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. import pprint import difflib from botocore.compat import OrderedDict from botocore.model import OperationModel from awscli.clidriver import ( CLIDriver, ServiceCommand, ServiceOperation, CLICommand) from awscli.arguments import BaseCLIArgument, CustomArgument from awscli.help import ProviderHelpCommand from awscli.completer import Completer from awscli.testutils import mock, unittest from awscli.customizations.commands import BasicCommand class BaseCompleterTest(unittest.TestCase): def setUp(self): self.clidriver_creator = MockCLIDriverFactory() def assert_completion(self, completer, cmdline, expected_results, point=None): if point is None: point = len(cmdline) actual = set(completer.complete(cmdline, point)) expected = set(expected_results) if not actual == expected: # Borrowed from assertDictEqual, though this doesn't # handle the case when unicode literals are used in one # dict but not in the other (and we want to consider them # as being equal). pretty_d1 = pprint.pformat(actual, width=1).splitlines() pretty_d2 = pprint.pformat(expected, width=1).splitlines() diff = ('\n' + '\n'.join(difflib.ndiff(pretty_d1, pretty_d2))) raise AssertionError("Results are not equal:\n%s" % diff) self.assertEqual(actual, expected) class TestCompleter(BaseCompleterTest): def test_complete_services(self): commands = { 'subcommands': { 'foo': {}, 'bar': { 'subcommands': { 'baz': {} } } }, 'arguments': [] } completer = Completer( self.clidriver_creator.create_clidriver(commands)) self.assert_completion(completer, 'aws ', ['foo', 'bar']) def test_complete_partial_service_name(self): commands = { 'subcommands': { 'cloudfront': {}, 'cloudformation': {}, 'cloudhsm': {}, 'sts': {} }, 'arguments': [] } completer = Completer( self.clidriver_creator.create_clidriver(commands)) self.assert_completion(completer, 'aws cloud', [ 'cloudfront', 'cloudformation', 'cloudhsm']) self.assert_completion(completer, 'aws cloudf', [ 'cloudfront', 'cloudformation']) self.assert_completion(completer, 'aws cloudfr', ['cloudfront']) self.assert_completion(completer, 'aws cloudfront', []) def test_complete_on_invalid_service(self): commands = { 'subcommands': { 'foo': {}, 'bar': { 'subcommands': { 'baz': {} } } }, 'arguments': [] } completer = Completer( self.clidriver_creator.create_clidriver(commands)) self.assert_completion(completer, 'aws bin', []) def test_complete_top_level_args(self): commands = { 'subcommands': {}, 'arguments': ['foo', 'bar'] } completer = Completer( self.clidriver_creator.create_clidriver(commands)) self.assert_completion(completer, 'aws --', ['--foo', '--bar']) def test_complete_partial_top_level_arg(self): commands = { 'subcommands': {}, 'arguments': ['foo', 'bar', 'foobar', 'fubar'] } completer = Completer( self.clidriver_creator.create_clidriver(commands)) self.assert_completion(completer, 'aws --f', [ '--foo', '--fubar', '--foobar']) self.assert_completion(completer, 'aws --fo', [ '--foo', '--foobar']) self.assert_completion(completer, 'aws --foob', ['--foobar']) self.assert_completion(completer, 'aws --foobar', []) def test_complete_top_level_arg_with_arg_already_used(self): commands = { 'subcommands': { 'baz': {} }, 'arguments': ['foo', 'bar'] } completer = Completer( self.clidriver_creator.create_clidriver(commands)) self.assert_completion(completer, 'aws --foo --f', []) def test_complete_service_commands(self): commands = { 'subcommands': { 'foo': { 'subcommands': { 'bar': { 'arguments': ['bin'] }, 'baz': {} } } }, 'arguments': [] } completer = Completer( self.clidriver_creator.create_clidriver(commands)) self.assert_completion(completer, 'aws foo ', ['bar', 'baz']) def test_complete_partial_service_commands(self): commands = { 'subcommands': { 'foo': { 'subcommands': { 'barb': { 'arguments': ['nil'] }, 'baz': {}, 'biz': {}, 'foobar': {} } } }, 'arguments': [] } completer = Completer( self.clidriver_creator.create_clidriver(commands)) self.assert_completion(completer, 'aws foo b', ['barb', 'baz', 'biz']) self.assert_completion(completer, 'aws foo ba', ['barb', 'baz']) self.assert_completion(completer, 'aws foo bar', ['barb']) self.assert_completion(completer, 'aws foo barb', []) def test_complete_service_arguments(self): commands = { 'subcommands': { 'foo': {} }, 'arguments': ['baz', 'bin'] } completer = Completer( self.clidriver_creator.create_clidriver(commands)) self.assert_completion(completer, 'aws foo --', ['--baz', '--bin']) def test_complete_partial_service_arguments(self): commands = { 'subcommands': { 'biz': {} }, 'arguments': ['foo', 'bar', 'foobar', 'fubar'] } completer = Completer( self.clidriver_creator.create_clidriver(commands)) self.assert_completion(completer, 'aws biz --f', [ '--foo', '--fubar', '--foobar']) self.assert_completion(completer, 'aws biz --fo', [ '--foo', '--foobar']) self.assert_completion(completer, 'aws biz --foob', ['--foobar']) def test_complete_service_arg_with_arg_already_used(self): commands = { 'subcommands': { 'baz': {} }, 'arguments': ['foo', 'bar'] } completer = Completer( self.clidriver_creator.create_clidriver(commands)) self.assert_completion(completer, 'aws baz --foo --f', []) def test_complete_operation_arguments(self): commands = { 'subcommands': { 'foo': {'subcommands': { 'bar': {'arguments': ['baz']} }} }, 'arguments': ['bin'] } completer = Completer( self.clidriver_creator.create_clidriver(commands)) self.assert_completion(completer, 'aws foo bar --', ['--baz', '--bin']) def test_complete_partial_operation_arguments(self): commands = { 'subcommands': { 'foo': {'subcommands': { 'bar': {'arguments': ['base', 'baz', 'air']} }} }, 'arguments': ['bin'] } completer = Completer( self.clidriver_creator.create_clidriver(commands)) self.assert_completion(completer, 'aws foo bar --b', [ '--base', '--baz', '--bin']) self.assert_completion(completer, 'aws foo bar --ba', [ '--base', '--baz']) self.assert_completion(completer, 'aws foo bar --bas', ['--base']) self.assert_completion(completer, 'aws foo bar --base', []) def test_complete_operation_arg_when_arg_already_used(self): commands = { 'subcommands': { 'foo': {'subcommands': { 'bar': {'arguments': ['baz']} }} }, 'arguments': [] } completer = Completer( self.clidriver_creator.create_clidriver(commands)) self.assert_completion(completer, 'aws foo bar --baz --b', []) def test_complete_positional_argument(self): commands = { 'subcommands': { 'foo': {'subcommands': { 'bar': {'arguments': [ 'baz', CustomArgument('bin', positional_arg=True) ]} }} }, 'arguments': [] } completer = Completer( self.clidriver_creator.create_clidriver(commands)) self.assert_completion(completer, 'aws foo bar --bin ', []) self.assert_completion(completer, 'aws foo bar --bin blah --', ['--baz']) def test_complete_undocumented_command(self): class UndocumentedCommand(CLICommand): _UNDOCUMENTED = True commands = { 'subcommands': { 'foo': {}, 'bar': UndocumentedCommand() }, 'arguments': [] } completer = Completer( self.clidriver_creator.create_clidriver(commands)) self.assert_completion(completer, 'aws ', ['foo']) class TestCompleteCustomCommands(BaseCompleterTest): def setUp(self): super(TestCompleteCustomCommands, self).setUp() custom_arguments = [ {'name': 'recursive'}, {'name': 'sse'} ] custom_commands = [ self.create_custom_command('mb'), self.create_custom_command('mv'), self.create_custom_command('cp', arguments=custom_arguments) ] custom_service = self.create_custom_command('s3', custom_commands) clidriver = self.clidriver_creator.create_clidriver({ 'subcommands': { 's3': custom_service['command_class'](mock.Mock()), 'foo': {} }, 'arguments': ['bar'] }) self.completer = Completer(clidriver) def create_custom_command(self, name, sub_commands=None, arguments=None): arg_table = arguments if arg_table is None: arg_table = [] subs = sub_commands if subs is None: subs = [] class CustomCommand(BasicCommand): NAME = name ARG_TABLE = arg_table SUBCOMMANDS = subs return {'name': name, 'command_class': CustomCommand} def test_complete_custom_service(self): self.assert_completion(self.completer, 'aws ', ['s3', 'foo']) def test_complete_custom_command(self): self.assert_completion(self.completer, 'aws s3 ', ['mb', 'mv', 'cp']) def test_complete_partial_custom_command(self): self.assert_completion(self.completer, 'aws s3 m', ['mb', 'mv']) def test_complete_custom_command_arguments(self): self.assert_completion(self.completer, 'aws s3 cp --', [ '--bar', '--recursive', '--sse']) def test_complete_custom_command_arguments_with_arg_already_used(self): self.assert_completion(self.completer, 'aws s3 cp --recursive --', [ '--bar', '--sse']) class MockCLIDriverFactory(object): def create_clidriver(self, commands=None, profiles=None): session = mock.Mock() session.get_data.return_value = None if profiles is not None and isinstance(profiles, list): session.available_profiles = profiles else: session.available_profiles = ['default'] clidriver = mock.Mock(spec=CLIDriver) clidriver.create_help_command.return_value = \ self._create_top_level_help(commands, session) clidriver.session = session return clidriver def _create_top_level_help(self, commands, session): command_table = self.create_command_table( commands.get('subcommands', {}), self._create_service_command) argument_table = self.create_argument_table( commands.get('arguments', [])) return ProviderHelpCommand( session, command_table, argument_table, None, None, None) def _create_service_command(self, name, command): command_table = self.create_command_table( command.get('subcommands', {}), self._create_operation_command) service_command = ServiceCommand(name, None) service_command._service_model = {} service_command._command_table = command_table return service_command def _create_operation_command(self, name, command): argument_table = self.create_argument_table( command.get('arguments', [])) mock_operation = mock.Mock(spec=OperationModel) mock_operation.deprecated = False operation = ServiceOperation(name, 'parent', None, mock_operation, None) operation._arg_table = argument_table return operation def create_command_table(self, commands, command_creator): if not commands: return OrderedDict() command_table = OrderedDict() for name, command in commands.items(): if isinstance(command, CLICommand): # Already a valid command, no need to fake one command_table[name] = command else: command_table[name] = command_creator(name, command) return command_table def create_argument_table(self, arguments): if not arguments: return OrderedDict() argument_table = OrderedDict() for arg in arguments: if isinstance(arg, BaseCLIArgument): argument_table[arg.name] = arg else: argument_table[arg] = BaseCLIArgument(arg) return argument_table