diff options
-rw-r--r-- | cliff/app.py | 5 | ||||
-rw-r--r-- | tests/test_app.py | 115 | ||||
-rw-r--r-- | tox.ini | 3 |
3 files changed, 111 insertions, 12 deletions
diff --git a/cliff/app.py b/cliff/app.py index d1ddf84..119f78a 100644 --- a/cliff/app.py +++ b/cliff/app.py @@ -192,10 +192,9 @@ class App(object): parsed_args = cmd_parser.parse_args(sub_argv) result = cmd.run(parsed_args) except Exception as err: + LOG.error('ERROR: %s', err) if self.options.debug: LOG.exception(err) - raise - LOG.error('ERROR: %s', err) try: self.clean_up(cmd, result, err) except Exception as err2: @@ -203,6 +202,8 @@ class App(object): LOG.exception(err2) else: LOG.error('Could not clean up: %s', err2) + if self.options.debug: + raise else: try: self.clean_up(cmd, result, None) diff --git a/tests/test_app.py b/tests/test_app.py index 15c5471..cae2c32 100644 --- a/tests/test_app.py +++ b/tests/test_app.py @@ -7,12 +7,21 @@ 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, @@ -28,6 +37,13 @@ def test_no_args_triggers_interactive_mode(): 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') + app.run([]) + 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') @@ -52,17 +68,43 @@ def test_clean_up_success(): def test_clean_up_error(): app, command = make_app() - # Register a command that fails - err_command = mock.Mock(name='err_command', spec=Command) - err_command_inst = mock.Mock(spec=Command) - def raise_exception(*args): - #raise RuntimeError('test exception %s' % args[0]) - raise RuntimeError('test exception') - err_command_inst.run = mock.Mock(side_effect=raise_exception) - err_command.return_value = err_command_inst - app.command_manager.add_command('error', err_command) + 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() @@ -71,3 +113,58 @@ def test_clean_up_error(): 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) @@ -2,7 +2,8 @@ envlist = py27,py32 [testenv] -commands = nosetests -d [] +commands = nosetests -d --with-coverage --cover-package=cliff [] deps = nose mock + coverage |