diff options
Diffstat (limited to 'cliff/tests')
-rw-r--r-- | cliff/tests/test_app.py | 229 | ||||
-rw-r--r-- | cliff/tests/test_command.py | 22 | ||||
-rw-r--r-- | cliff/tests/test_commandmanager.py | 121 | ||||
-rw-r--r-- | cliff/tests/test_help.py | 116 | ||||
-rw-r--r-- | cliff/tests/test_lister.py | 52 | ||||
-rw-r--r-- | cliff/tests/test_show.py | 54 |
6 files changed, 594 insertions, 0 deletions
diff --git a/cliff/tests/test_app.py b/cliff/tests/test_app.py new file mode 100644 index 0000000..db80bbc --- /dev/null +++ b/cliff/tests/test_app.py @@ -0,0 +1,229 @@ +from argparse import ArgumentError + +from cliff.app import App +from cliff.command import Command +from cliff.commandmanager import CommandManager + +import mock + + +def make_app(): + cmd_mgr = CommandManager('cliff.tests') + + # Register a command that succeeds + command = mock.MagicMock(spec=Command) + command_inst = mock.MagicMock(spec=Command) + command_inst.run.return_value = 0 + command.return_value = command_inst + cmd_mgr.add_command('mock', command) + + # Register a command that fails + err_command = mock.Mock(name='err_command', spec=Command) + err_command_inst = mock.Mock(spec=Command) + err_command_inst.run = mock.Mock( + side_effect=RuntimeError('test exception') + ) + err_command.return_value = err_command_inst + cmd_mgr.add_command('error', err_command) + + app = App('testing interactive mode', + '1', + cmd_mgr, + stderr=mock.Mock(), # suppress warning messages + ) + return app, command + + +def test_no_args_triggers_interactive_mode(): + app, command = make_app() + app.interact = mock.MagicMock(name='inspect') + app.run([]) + app.interact.assert_called_once_with() + + +def test_interactive_mode_cmdloop(): + app, command = make_app() + app.interactive_app_factory = mock.MagicMock( + name='interactive_app_factory' + ) + assert app.interpreter is None + app.run([]) + assert app.interpreter is not None + app.interactive_app_factory.return_value.cmdloop.assert_called_once_with() + + +def test_initialize_app(): + app, command = make_app() + app.initialize_app = mock.MagicMock(name='initialize_app') + app.run(['mock']) + app.initialize_app.assert_called_once_with(['mock']) + + +def test_prepare_to_run_command(): + app, command = make_app() + app.prepare_to_run_command = mock.MagicMock(name='prepare_to_run_command') + app.run(['mock']) + app.prepare_to_run_command.assert_called_once_with(command()) + + +def test_clean_up_success(): + app, command = make_app() + app.clean_up = mock.MagicMock(name='clean_up') + app.run(['mock']) + app.clean_up.assert_called_once_with(command.return_value, 0, None) + + +def test_clean_up_error(): + app, command = make_app() + + app.clean_up = mock.MagicMock(name='clean_up') + app.run(['error']) + + app.clean_up.assert_called_once() + call_args = app.clean_up.call_args_list[0] + assert call_args == mock.call(mock.ANY, 1, mock.ANY) + args, kwargs = call_args + assert isinstance(args[2], RuntimeError) + assert args[2].args == ('test exception',) + + +def test_clean_up_error_debug(): + app, command = make_app() + + app.clean_up = mock.MagicMock(name='clean_up') + try: + app.run(['--debug', 'error']) + except RuntimeError as err: + assert app.clean_up.call_args_list[0][0][2] is err + else: + assert False, 'Should have had an exception' + + app.clean_up.assert_called_once() + call_args = app.clean_up.call_args_list[0] + assert call_args == mock.call(mock.ANY, 1, mock.ANY) + args, kwargs = call_args + assert isinstance(args[2], RuntimeError) + assert args[2].args == ('test exception',) + + +def test_error_handling_clean_up_raises_exception(): + app, command = make_app() + + app.clean_up = mock.MagicMock( + name='clean_up', + side_effect=RuntimeError('within clean_up'), + ) + app.run(['error']) + + app.clean_up.assert_called_once() + call_args = app.clean_up.call_args_list[0] + assert call_args == mock.call(mock.ANY, 1, mock.ANY) + args, kwargs = call_args + assert isinstance(args[2], RuntimeError) + assert args[2].args == ('test exception',) + + +def test_error_handling_clean_up_raises_exception_debug(): + app, command = make_app() + + app.clean_up = mock.MagicMock( + name='clean_up', + side_effect=RuntimeError('within clean_up'), + ) + try: + app.run(['--debug', 'error']) + except RuntimeError as err: + if not hasattr(err, '__context__'): + # The exception passed to clean_up is not the exception + # caused *by* clean_up. This test is only valid in python + # 2 because under v3 the original exception is re-raised + # with the new one as a __context__ attribute. + assert app.clean_up.call_args_list[0][0][2] is not err + else: + assert False, 'Should have had an exception' + + app.clean_up.assert_called_once() + call_args = app.clean_up.call_args_list[0] + assert call_args == mock.call(mock.ANY, 1, mock.ANY) + args, kwargs = call_args + assert isinstance(args[2], RuntimeError) + assert args[2].args == ('test exception',) + + +def test_normal_clean_up_raises_exception(): + app, command = make_app() + + app.clean_up = mock.MagicMock( + name='clean_up', + side_effect=RuntimeError('within clean_up'), + ) + app.run(['mock']) + + app.clean_up.assert_called_once() + call_args = app.clean_up.call_args_list[0] + assert call_args == mock.call(mock.ANY, 0, None) + + +def test_normal_clean_up_raises_exception_debug(): + app, command = make_app() + + app.clean_up = mock.MagicMock( + name='clean_up', + side_effect=RuntimeError('within clean_up'), + ) + app.run(['--debug', 'mock']) + + app.clean_up.assert_called_once() + call_args = app.clean_up.call_args_list[0] + assert call_args == mock.call(mock.ANY, 0, None) + + +def test_build_option_parser_conflicting_option_should_throw(): + class MyApp(App): + def __init__(self): + super(MyApp, self).__init__( + description='testing', + version='0.1', + command_manager=CommandManager('tests'), + ) + + def build_option_parser(self, description, version): + parser = super(MyApp, self).build_option_parser(description, + version) + parser.add_argument( + '-h', '--help', + default=self, # tricky + help="show this help message and exit", + ) + + # TODO: tests should really use unittest2. + try: + MyApp() + except ArgumentError: + pass + else: + raise Exception('Exception was not thrown') + + +def test_option_parser_conflicting_option_custom_arguments_should_not_throw(): + class MyApp(App): + def __init__(self): + super(MyApp, self).__init__( + description='testing', + version='0.1', + command_manager=CommandManager('tests'), + ) + + def build_option_parser(self, description, version): + argparse_kwargs = {'conflict_handler': 'resolve'} + parser = super(MyApp, self).build_option_parser( + description, + version, + argparse_kwargs=argparse_kwargs) + parser.add_argument( + '-h', '--help', + default=self, # tricky + help="show this help message and exit", + ) + + MyApp() diff --git a/cliff/tests/test_command.py b/cliff/tests/test_command.py new file mode 100644 index 0000000..39fde51 --- /dev/null +++ b/cliff/tests/test_command.py @@ -0,0 +1,22 @@ + +from cliff.command import Command + + +class TestCommand(Command): + """Description of command. + """ + + def take_action(self, parsed_args): + return + + +def test_get_description(): + cmd = TestCommand(None, None) + desc = cmd.get_description() + assert desc == "Description of command.\n " + + +def test_get_parser(): + cmd = TestCommand(None, None) + parser = cmd.get_parser('NAME') + assert parser.prog == 'NAME' diff --git a/cliff/tests/test_commandmanager.py b/cliff/tests/test_commandmanager.py new file mode 100644 index 0000000..9a50a5e --- /dev/null +++ b/cliff/tests/test_commandmanager.py @@ -0,0 +1,121 @@ + +import mock + +from cliff.commandmanager import CommandManager + + +class TestCommand(object): + @classmethod + def load(cls): + return cls + + def __init__(self): + return + + +class TestCommandManager(CommandManager): + def _load_commands(self): + self.commands = { + 'one': TestCommand, + 'two words': TestCommand, + 'three word command': TestCommand, + } + + +def test_lookup_and_find(): + def check(mgr, argv): + cmd, name, remaining = mgr.find_command(argv) + assert cmd + assert name == ' '.join(argv) + assert not remaining + mgr = TestCommandManager('test') + for expected in [['one'], + ['two', 'words'], + ['three', 'word', 'command'], + ]: + yield check, mgr, expected + return + + +def test_lookup_with_remainder(): + def check(mgr, argv): + cmd, name, remaining = mgr.find_command(argv) + assert cmd + assert remaining == ['--opt'] + mgr = TestCommandManager('test') + for expected in [['one', '--opt'], + ['two', 'words', '--opt'], + ['three', 'word', 'command', '--opt'], + ]: + yield check, mgr, expected + return + + +def test_find_invalid_command(): + mgr = TestCommandManager('test') + + def check_one(argv): + try: + mgr.find_command(argv) + except ValueError as err: + assert '-b' in ('%s' % err) + else: + assert False, 'expected a failure' + for argv in [['a', '-b'], + ['-b'], + ]: + yield check_one, argv + + +def test_find_unknown_command(): + mgr = TestCommandManager('test') + try: + mgr.find_command(['a', 'b']) + except ValueError as err: + assert "['a', 'b']" in ('%s' % err) + else: + assert False, 'expected a failure' + + +def test_add_command(): + mgr = TestCommandManager('test') + mock_cmd = mock.Mock() + mgr.add_command('mock', mock_cmd) + found_cmd, name, args = mgr.find_command(['mock']) + assert found_cmd is mock_cmd + + +def test_load_commands(): + testcmd = mock.Mock(name='testcmd') + testcmd.name.replace.return_value = 'test' + mock_pkg_resources = mock.Mock(return_value=[testcmd]) + with mock.patch('pkg_resources.iter_entry_points', + mock_pkg_resources) as iter_entry_points: + mgr = CommandManager('test') + assert iter_entry_points.called_once_with('test') + names = [n for n, v in mgr] + assert names == ['test'] + + +def test_load_commands_keep_underscores(): + testcmd = mock.Mock() + testcmd.name = 'test_cmd' + mock_pkg_resources = mock.Mock(return_value=[testcmd]) + with mock.patch('pkg_resources.iter_entry_points', + mock_pkg_resources) as iter_entry_points: + mgr = CommandManager('test', convert_underscores=False) + assert iter_entry_points.called_once_with('test') + names = [n for n, v in mgr] + assert names == ['test_cmd'] + + +def test_load_commands_replace_underscores(): + testcmd = mock.Mock() + testcmd.name = 'test_cmd' + mock_pkg_resources = mock.Mock(return_value=[testcmd]) + with mock.patch('pkg_resources.iter_entry_points', + mock_pkg_resources) as iter_entry_points: + mgr = CommandManager('test', convert_underscores=True) + assert iter_entry_points.called_once_with('test') + names = [n for n, v in mgr] + assert names == ['test cmd'] diff --git a/cliff/tests/test_help.py b/cliff/tests/test_help.py new file mode 100644 index 0000000..bdf9d71 --- /dev/null +++ b/cliff/tests/test_help.py @@ -0,0 +1,116 @@ +try: + from StringIO import StringIO +except: + from io import StringIO + +import mock + +from cliff.app import App +from cliff.command import Command +from cliff.commandmanager import CommandManager +from cliff.help import HelpCommand + + +class TestParser(object): + + def print_help(self, stdout): + stdout.write('TestParser') + + +class TestCommand(Command): + + @classmethod + def load(cls): + return cls + + def get_parser(self, ignore): + # Make it look like this class is the parser + # so parse_args() is called. + return TestParser() + + def take_action(self, args): + return + + +class TestCommandManager(CommandManager): + def _load_commands(self): + self.commands = { + 'one': TestCommand, + 'two words': TestCommand, + 'three word command': TestCommand, + } + + +def test_show_help_for_command(): + # FIXME(dhellmann): Are commands tied too closely to the app? Or + # do commands know too much about apps by using them to get to the + # command manager? + stdout = StringIO() + app = App('testing', '1', TestCommandManager('cliff.test'), stdout=stdout) + app.NAME = 'test' + help_cmd = HelpCommand(app, mock.Mock()) + parser = help_cmd.get_parser('test') + parsed_args = parser.parse_args(['one']) + try: + help_cmd.run(parsed_args) + except SystemExit: + pass + assert stdout.getvalue() == 'TestParser' + + +def test_list_matching_commands(): + # FIXME(dhellmann): Are commands tied too closely to the app? Or + # do commands know too much about apps by using them to get to the + # command manager? + stdout = StringIO() + app = App('testing', '1', TestCommandManager('cliff.test'), stdout=stdout) + app.NAME = 'test' + help_cmd = HelpCommand(app, mock.Mock()) + parser = help_cmd.get_parser('test') + parsed_args = parser.parse_args(['t']) + try: + help_cmd.run(parsed_args) + except SystemExit: + pass + help_output = stdout.getvalue() + assert 'Command "t" matches:' in help_output + assert 'two' in help_output + assert 'three' in help_output + + +def test_list_matching_commands_no_match(): + # FIXME(dhellmann): Are commands tied too closely to the app? Or + # do commands know too much about apps by using them to get to the + # command manager? + stdout = StringIO() + app = App('testing', '1', TestCommandManager('cliff.test'), stdout=stdout) + app.NAME = 'test' + help_cmd = HelpCommand(app, mock.Mock()) + parser = help_cmd.get_parser('test') + parsed_args = parser.parse_args(['z']) + try: + help_cmd.run(parsed_args) + except SystemExit: + pass + except ValueError: + pass + else: + assert False, 'Should have seen a ValueError' + + +def test_show_help_for_help(): + # FIXME(dhellmann): Are commands tied too closely to the app? Or + # do commands know too much about apps by using them to get to the + # command manager? + stdout = StringIO() + app = App('testing', '1', TestCommandManager('cliff.test'), stdout=stdout) + app.NAME = 'test' + help_cmd = HelpCommand(app, mock.Mock()) + parser = help_cmd.get_parser('test') + parsed_args = parser.parse_args([]) + try: + help_cmd.run(parsed_args) + except SystemExit: + pass + help_text = stdout.getvalue() + assert 'usage: test help [-h]' in help_text diff --git a/cliff/tests/test_lister.py b/cliff/tests/test_lister.py new file mode 100644 index 0000000..c0452d5 --- /dev/null +++ b/cliff/tests/test_lister.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python + +from cliff.lister import Lister + +import mock + + +class FauxFormatter(object): + + def __init__(self): + self.args = [] + + def emit_list(self, columns, data, stdout, args): + self.args.append((columns, data)) + + +class ExerciseLister(Lister): + + def load_formatter_plugins(self): + self.formatters = { + 'test': FauxFormatter(), + } + return + + def take_action(self, parsed_args): + return ( + parsed_args.columns, + [('a', 'A'), ('b', 'B')], + ) + + +# def run(self, parsed_args): +# self.formatter = self.formatters[parsed_args.formatter] +# column_names, data = self.take_action(parsed_args) +# self.produce_output(parsed_args, column_names, data) +# return 0 + +def test_formatter_args(): + app = mock.Mock() + test_lister = ExerciseLister(app, []) + + parsed_args = mock.Mock() + parsed_args.columns = ('Col1', 'Col2') + parsed_args.formatter = 'test' + + test_lister.run(parsed_args) + f = test_lister.formatters['test'] + assert len(f.args) == 1 + args = f.args[0] + assert args[0] == list(parsed_args.columns) + data = list(args[1]) + assert data == [['a', 'A'], ['b', 'B']] diff --git a/cliff/tests/test_show.py b/cliff/tests/test_show.py new file mode 100644 index 0000000..41df5e1 --- /dev/null +++ b/cliff/tests/test_show.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python + +from cliff.show import ShowOne + +import mock + + +class FauxFormatter(object): + + def __init__(self): + self.args = [] + + def emit_list(self, columns, data, stdout, args): + self.args.append((columns, data)) + + +class ExerciseShowOne(ShowOne): + + def load_formatter_plugins(self): + self.formatters = { + 'test': FauxFormatter(), + } + return + + def take_action(self, parsed_args): + return ( + parsed_args.columns, + [('a', 'A'), ('b', 'B')], + ) + + +# def test_formatter_args(): +# app = mock.Mock() +# test_lister = ExerciseLister(app, []) + +# parsed_args = mock.Mock() +# parsed_args.columns = ('Col1', 'Col2') +# parsed_args.formatter = 'test' + +# test_lister.run(parsed_args) +# f = test_lister.formatters['test'] +# assert len(f.args) == 1 +# args = f.args[0] +# assert args[0] == list(parsed_args.columns) +# data = list(args[1]) +# assert data == [['a', 'A'], ['b', 'B']] + +def test_dict2columns(): + app = mock.Mock() + test_show = ExerciseShowOne(app, []) + d = {'a': 'A', 'b': 'B', 'c': 'C'} + expected = [('a', 'b', 'c'), ('A', 'B', 'C')] + actual = list(test_show.dict2columns(d)) + assert expected == actual |