summaryrefslogtreecommitdiff
path: root/cliff/tests
diff options
context:
space:
mode:
Diffstat (limited to 'cliff/tests')
-rw-r--r--cliff/tests/test_app.py229
-rw-r--r--cliff/tests/test_command.py22
-rw-r--r--cliff/tests/test_commandmanager.py121
-rw-r--r--cliff/tests/test_help.py116
-rw-r--r--cliff/tests/test_lister.py52
-rw-r--r--cliff/tests/test_show.py54
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