summaryrefslogtreecommitdiff
path: root/tests/test_plugin.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/test_plugin.py')
-rw-r--r--tests/test_plugin.py823
1 files changed, 823 insertions, 0 deletions
diff --git a/tests/test_plugin.py b/tests/test_plugin.py
new file mode 100644
index 00000000..e401e837
--- /dev/null
+++ b/tests/test_plugin.py
@@ -0,0 +1,823 @@
+# coding=utf-8
+"""
+Test plugin infrastructure and hooks.
+
+Copyright 2018 Jared Crapo <jared@kotfu.net>
+Released under MIT license, see LICENSE file
+"""
+
+from typing import Tuple
+
+import pytest
+
+import cmd2
+from cmd2 import plugin
+
+from .conftest import StdOut
+
+class Plugin:
+ "A mixin class for testing hook registration and calling"
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.reset_counters()
+
+ def reset_counters(self):
+ self.called_preparse = 0
+ self.called_postparsing = 0
+ self.called_precmd = 0
+ self.called_postcmd = 0
+ self.called_cmdfinalization = 0
+
+ ###
+ #
+ # preloop and postloop hooks
+ # which share the same signature and are thus interchangable
+ #
+ ###
+ def prepost_hook_one(self) -> None:
+ "Method used for preloop or postloop hooks"
+ self.poutput("one")
+
+ def prepost_hook_two(self) -> None:
+ "Another method used for preloop or postloop hooks"
+ self.poutput("two")
+
+ def prepost_hook_too_many_parameters(self, param) -> None:
+ "A preloop or postloop hook with too many parameters"
+ pass
+
+ def prepost_hook_with_wrong_return_annotation(self) -> bool:
+ "A preloop or postloop hook with incorrect return type"
+ pass
+
+ ###
+ #
+ # preparse hook
+ #
+ ###
+ def preparse(self, line: str) -> str:
+ "Preparsing hook"
+ self.called_preparse += 1
+ return line
+
+ ###
+ #
+ # Postparsing hooks
+ #
+ ###
+ def postparse_hook(self, data: cmd2.plugin.PostparsingData) -> cmd2.plugin.PostparsingData:
+ "A postparsing hook"
+ self.called_postparsing += 1
+ return data
+
+ def postparse_hook_stop(self, data: cmd2.plugin.PostparsingData) -> cmd2.plugin.PostparsingData:
+ "A postparsing hook with requests application exit"
+ self.called_postparsing += 1
+ data.stop = True
+ return data
+
+ def postparse_hook_emptystatement(self, data: cmd2.plugin.PostparsingData) -> cmd2.plugin.PostparsingData:
+ "A postparsing hook with raises an EmptyStatement exception"
+ self.called_postparsing += 1
+ raise cmd2.EmptyStatement
+
+ def postparse_hook_exception(self, data: cmd2.plugin.PostparsingData) -> cmd2.plugin.PostparsingData:
+ "A postparsing hook which raises an exception"
+ self.called_postparsing += 1
+ raise ValueError
+
+ def postparse_hook_too_many_parameters(self, data1, data2) -> cmd2.plugin.PostparsingData:
+ "A postparsing hook with too many parameters"
+ pass
+
+ def postparse_hook_undeclared_parameter_annotation(self, data) -> cmd2.plugin.PostparsingData:
+ "A postparsing hook with an undeclared parameter type"
+ pass
+
+ def postparse_hook_wrong_parameter_annotation(self, data: str) -> cmd2.plugin.PostparsingData:
+ "A postparsing hook with the wrong parameter type"
+ pass
+
+ def postparse_hook_undeclared_return_annotation(self, data: cmd2.plugin.PostparsingData):
+ "A postparsing hook with an undeclared return type"
+ pass
+
+ def postparse_hook_wrong_return_annotation(self, data: cmd2.plugin.PostparsingData) -> str:
+ "A postparsing hook with the wrong return type"
+ pass
+
+ ###
+ #
+ # precommand hooks, some valid, some invalid
+ #
+ ###
+ def precmd(self, statement: cmd2.Statement) -> cmd2.Statement:
+ "Override cmd.Cmd method"
+ self.called_precmd += 1
+ return statement
+
+ def precmd_hook(self, data: plugin.PrecommandData) -> plugin.PrecommandData:
+ "A precommand hook"
+ self.called_precmd += 1
+ return data
+
+ def precmd_hook_emptystatement(self, data: plugin.PrecommandData) -> plugin.PrecommandData:
+ "A precommand hook which raises an EmptyStatement exception"
+ self.called_precmd += 1
+ raise cmd2.EmptyStatement
+
+ def precmd_hook_exception(self, data: plugin.PrecommandData) -> plugin.PrecommandData:
+ "A precommand hook which raises an exception"
+ self.called_precmd += 1
+ raise ValueError
+
+ def precmd_hook_not_enough_parameters(self) -> plugin.PrecommandData:
+ "A precommand hook with no parameters"
+ pass
+
+ def precmd_hook_too_many_parameters(self, one: plugin.PrecommandData, two: str) -> plugin.PrecommandData:
+ "A precommand hook with too many parameters"
+ return one
+
+ def precmd_hook_no_parameter_annotation(self, data) -> plugin.PrecommandData:
+ "A precommand hook with no type annotation on the parameter"
+ return data
+
+ def precmd_hook_wrong_parameter_annotation(self, data: str) -> plugin.PrecommandData:
+ "A precommand hook with the incorrect type annotation on the parameter"
+ return data
+
+ def precmd_hook_no_return_annotation(self, data: plugin.PrecommandData):
+ "A precommand hook with no type annotation on the return value"
+ return data
+
+ def precmd_hook_wrong_return_annotation(self, data: plugin.PrecommandData) -> cmd2.Statement:
+ return self.statement_parser.parse('hi there')
+
+ ###
+ #
+ # postcommand hooks, some valid, some invalid
+ #
+ ###
+ def postcmd(self, stop: bool, statement: cmd2.Statement) -> bool:
+ "Override cmd.Cmd method"
+ self.called_postcmd += 1
+ return stop
+
+ def postcmd_hook(self, data: plugin.PostcommandData) -> plugin.PostcommandData:
+ "A postcommand hook"
+ self.called_postcmd += 1
+ return data
+
+ def postcmd_hook_exception(self, data: plugin.PostcommandData) -> plugin.PostcommandData:
+ "A postcommand hook with raises an exception"
+ self.called_postcmd += 1
+ raise ZeroDivisionError
+
+ def postcmd_hook_not_enough_parameters(self) -> plugin.PostcommandData:
+ "A precommand hook with no parameters"
+ pass
+
+ def postcmd_hook_too_many_parameters(self, one: plugin.PostcommandData, two: str) -> plugin.PostcommandData:
+ "A precommand hook with too many parameters"
+ return one
+
+ def postcmd_hook_no_parameter_annotation(self, data) -> plugin.PostcommandData:
+ "A precommand hook with no type annotation on the parameter"
+ return data
+
+ def postcmd_hook_wrong_parameter_annotation(self, data: str) -> plugin.PostcommandData:
+ "A precommand hook with the incorrect type annotation on the parameter"
+ return data
+
+ def postcmd_hook_no_return_annotation(self, data: plugin.PostcommandData):
+ "A precommand hook with no type annotation on the return value"
+ return data
+
+ def postcmd_hook_wrong_return_annotation(self, data: plugin.PostcommandData) -> cmd2.Statement:
+ return self.statement_parser.parse('hi there')
+
+ ###
+ #
+ # command finalization hooks, some valid, some invalid
+ #
+ ###
+ def cmdfinalization_hook(self, data: plugin.CommandFinalizationData) -> plugin.CommandFinalizationData:
+ """A command finalization hook."""
+ self.called_cmdfinalization += 1
+ return data
+
+ def cmdfinalization_hook_stop(self, data: cmd2.plugin.CommandFinalizationData) -> cmd2.plugin.CommandFinalizationData:
+ "A postparsing hook which requests application exit"
+ self.called_cmdfinalization += 1
+ data.stop = True
+ return data
+
+ def cmdfinalization_hook_exception(self, data: cmd2.plugin.CommandFinalizationData) -> cmd2.plugin.CommandFinalizationData:
+ "A postparsing hook which raises an exception"
+ self.called_cmdfinalization += 1
+ raise ValueError
+
+ def cmdfinalization_hook_not_enough_parameters(self) -> plugin.CommandFinalizationData:
+ """A command finalization hook with no parameters."""
+ pass
+
+ def cmdfinalization_hook_too_many_parameters(self, one: plugin.CommandFinalizationData, two: str) -> plugin.CommandFinalizationData:
+ """A command finalization hook with too many parameters."""
+ return one
+
+ def cmdfinalization_hook_no_parameter_annotation(self, data) -> plugin.CommandFinalizationData:
+ """A command finalization hook with no type annotation on the parameter."""
+ return data
+
+ def cmdfinalization_hook_wrong_parameter_annotation(self, data: str) -> plugin.CommandFinalizationData:
+ """A command finalization hook with the incorrect type annotation on the parameter."""
+ return data
+
+ def cmdfinalization_hook_no_return_annotation(self, data: plugin.CommandFinalizationData):
+ """A command finalizationhook with no type annotation on the return value."""
+ return data
+
+ def cmdfinalization_hook_wrong_return_annotation(self, data: plugin.CommandFinalizationData) -> cmd2.Statement:
+ """A command finalization hook with the wrong return type annotation."""
+ return self.statement_parser.parse('hi there')
+
+
+class PluggedApp(Plugin, cmd2.Cmd):
+ "A sample app with a plugin mixed in"
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+
+ def do_say(self, statement):
+ """Repeat back the arguments"""
+ self.poutput(statement)
+
+###
+#
+# test pre and postloop hooks
+#
+###
+def test_register_preloop_hook_too_many_parameters():
+ app = PluggedApp()
+ with pytest.raises(TypeError):
+ app.register_preloop_hook(app.prepost_hook_too_many_parameters)
+
+def test_register_preloop_hook_with_return_annotation():
+ app = PluggedApp()
+ with pytest.raises(TypeError):
+ app.register_preloop_hook(app.prepost_hook_with_wrong_return_annotation)
+
+def test_preloop_hook(capsys):
+ app = PluggedApp()
+ app.register_preloop_hook(app.prepost_hook_one)
+ app.cmdqueue.append('say hello')
+ app.cmdqueue.append('quit')
+ app.cmdloop()
+ out, err = capsys.readouterr()
+ assert out == 'one\nhello\n'
+ assert not err
+
+def test_preloop_hooks(capsys):
+ app = PluggedApp()
+ app.register_preloop_hook(app.prepost_hook_one)
+ app.register_preloop_hook(app.prepost_hook_two)
+ app.cmdqueue.append('say hello')
+ app.cmdqueue.append('quit')
+ app.cmdloop()
+ out, err = capsys.readouterr()
+ assert out == 'one\ntwo\nhello\n'
+ assert not err
+
+def test_register_postloop_hook_too_many_parameters():
+ app = PluggedApp()
+ with pytest.raises(TypeError):
+ app.register_postloop_hook(app.prepost_hook_too_many_parameters)
+
+def test_register_postloop_hook_with_wrong_return_annotation():
+ app = PluggedApp()
+ with pytest.raises(TypeError):
+ app.register_postloop_hook(app.prepost_hook_with_wrong_return_annotation)
+
+def test_postloop_hook(capsys):
+ app = PluggedApp()
+ app.register_postloop_hook(app.prepost_hook_one)
+ app.cmdqueue.append('say hello')
+ app.cmdqueue.append('quit')
+ app.cmdloop()
+ out, err = capsys.readouterr()
+ assert out == 'hello\none\n'
+ assert not err
+
+def test_postloop_hooks(capsys):
+ app = PluggedApp()
+ app.register_postloop_hook(app.prepost_hook_one)
+ app.register_postloop_hook(app.prepost_hook_two)
+ app.cmdqueue.append('say hello')
+ app.cmdqueue.append('quit')
+ app.cmdloop()
+ out, err = capsys.readouterr()
+ assert out == 'hello\none\ntwo\n'
+ assert not err
+
+###
+#
+# test preparse hook
+#
+###
+def test_preparse(capsys):
+ app = PluggedApp()
+ app.onecmd_plus_hooks('say hello')
+ out, err = capsys.readouterr()
+ assert out == 'hello\n'
+ assert not err
+ assert app.called_preparse == 1
+
+###
+#
+# test postparsing hooks
+#
+###
+def test_postparsing_hook_too_many_parameters():
+ app = PluggedApp()
+ with pytest.raises(TypeError):
+ app.register_postparsing_hook(app.postparse_hook_too_many_parameters)
+
+def test_postparsing_hook_undeclared_parameter_annotation():
+ app = PluggedApp()
+ with pytest.raises(TypeError):
+ app.register_postparsing_hook(app.postparse_hook_undeclared_parameter_annotation)
+
+def test_postparsing_hook_wrong_parameter_annotation():
+ app = PluggedApp()
+ with pytest.raises(TypeError):
+ app.register_postparsing_hook(app.postparse_hook_wrong_parameter_annotation)
+
+def test_postparsing_hook_undeclared_return_annotation():
+ app = PluggedApp()
+ with pytest.raises(TypeError):
+ app.register_postparsing_hook(app.postparse_hook_undeclared_return_annotation)
+
+def test_postparsing_hook_wrong_return_annotation():
+ app = PluggedApp()
+ with pytest.raises(TypeError):
+ app.register_postparsing_hook(app.postparse_hook_wrong_return_annotation)
+
+def test_postparsing_hook(capsys):
+ app = PluggedApp()
+ app.onecmd_plus_hooks('say hello')
+ out, err = capsys.readouterr()
+ assert out == 'hello\n'
+ assert not err
+ assert not app.called_postparsing
+
+ app.reset_counters()
+ app.register_postparsing_hook(app.postparse_hook)
+ app.onecmd_plus_hooks('say hello')
+ out, err = capsys.readouterr()
+ assert out == 'hello\n'
+ assert not err
+ assert app.called_postparsing == 1
+
+ # register the function again, so it should be called twice
+ app.reset_counters()
+ app.register_postparsing_hook(app.postparse_hook)
+ app.onecmd_plus_hooks('say hello')
+ out, err = capsys.readouterr()
+ assert out == 'hello\n'
+ assert not err
+ assert app.called_postparsing == 2
+
+def test_postparsing_hook_stop_first(capsys):
+ app = PluggedApp()
+ app.register_postparsing_hook(app.postparse_hook_stop)
+ stop = app.onecmd_plus_hooks('say hello')
+ assert app.called_postparsing == 1
+ assert stop
+
+ # register another function but it shouldn't be called
+ app.reset_counters()
+ app.register_postparsing_hook(app.postparse_hook)
+ stop = app.onecmd_plus_hooks('say hello')
+ assert app.called_postparsing == 1
+ assert stop
+
+def test_postparsing_hook_stop_second(capsys):
+ app = PluggedApp()
+ app.register_postparsing_hook(app.postparse_hook)
+ stop = app.onecmd_plus_hooks('say hello')
+ assert app.called_postparsing == 1
+ assert not stop
+
+ # register another function and make sure it gets called
+ app.reset_counters()
+ app.register_postparsing_hook(app.postparse_hook_stop)
+ stop = app.onecmd_plus_hooks('say hello')
+ assert app.called_postparsing == 2
+ assert stop
+
+ # register a third function which shouldn't be called
+ app.reset_counters()
+ app.register_postparsing_hook(app.postparse_hook)
+ stop = app.onecmd_plus_hooks('say hello')
+ assert app.called_postparsing == 2
+ assert stop
+
+def test_postparsing_hook_emptystatement_first(capsys):
+ app = PluggedApp()
+ app.register_postparsing_hook(app.postparse_hook_emptystatement)
+ stop = app.onecmd_plus_hooks('say hello')
+ out, err = capsys.readouterr()
+ assert not stop
+ assert not out
+ assert not err
+ assert app.called_postparsing == 1
+
+ # register another function but it shouldn't be called
+ app.reset_counters()
+ stop = app.register_postparsing_hook(app.postparse_hook)
+ app.onecmd_plus_hooks('say hello')
+ out, err = capsys.readouterr()
+ assert not stop
+ assert not out
+ assert not err
+ assert app.called_postparsing == 1
+
+def test_postparsing_hook_emptystatement_second(capsys):
+ app = PluggedApp()
+ app.register_postparsing_hook(app.postparse_hook)
+ stop = app.onecmd_plus_hooks('say hello')
+ out, err = capsys.readouterr()
+ assert not stop
+ assert out == 'hello\n'
+ assert not err
+ assert app.called_postparsing == 1
+
+ # register another function and make sure it gets called
+ app.reset_counters()
+ app.register_postparsing_hook(app.postparse_hook_emptystatement)
+ stop = app.onecmd_plus_hooks('say hello')
+ out, err = capsys.readouterr()
+ assert not stop
+ assert not out
+ assert not err
+ assert app.called_postparsing == 2
+
+ # register a third function which shouldn't be called
+ app.reset_counters()
+ app.register_postparsing_hook(app.postparse_hook)
+ stop = app.onecmd_plus_hooks('say hello')
+ out, err = capsys.readouterr()
+ assert not stop
+ assert not out
+ assert not err
+ assert app.called_postparsing == 2
+
+def test_postparsing_hook_exception(capsys):
+ app = PluggedApp()
+ app.register_postparsing_hook(app.postparse_hook_exception)
+ stop = app.onecmd_plus_hooks('say hello')
+ out, err = capsys.readouterr()
+ assert not stop
+ assert not out
+ assert err
+ assert app.called_postparsing == 1
+
+ # register another function, but it shouldn't be called
+ app.reset_counters()
+ app.register_postparsing_hook(app.postparse_hook)
+ stop = app.onecmd_plus_hooks('say hello')
+ out, err = capsys.readouterr()
+ assert not stop
+ assert not out
+ assert err
+ assert app.called_postparsing == 1
+
+###
+#
+# test precmd hooks
+#
+#####
+def test_register_precmd_hook_parameter_count():
+ app = PluggedApp()
+ with pytest.raises(TypeError):
+ app.register_precmd_hook(app.precmd_hook_not_enough_parameters)
+ with pytest.raises(TypeError):
+ app.register_precmd_hook(app.precmd_hook_too_many_parameters)
+
+def test_register_precmd_hook_no_parameter_annotation():
+ app = PluggedApp()
+ with pytest.raises(TypeError):
+ app.register_precmd_hook(app.precmd_hook_no_parameter_annotation)
+
+def test_register_precmd_hook_wrong_parameter_annotation():
+ app = PluggedApp()
+ with pytest.raises(TypeError):
+ app.register_precmd_hook(app.precmd_hook_wrong_parameter_annotation)
+
+def test_register_precmd_hook_no_return_annotation():
+ app = PluggedApp()
+ with pytest.raises(TypeError):
+ app.register_precmd_hook(app.precmd_hook_no_return_annotation)
+
+def test_register_precmd_hook_wrong_return_annotation():
+ app = PluggedApp()
+ with pytest.raises(TypeError):
+ app.register_precmd_hook(app.precmd_hook_wrong_return_annotation)
+
+def test_precmd_hook(capsys):
+ app = PluggedApp()
+ app.onecmd_plus_hooks('say hello')
+ out, err = capsys.readouterr()
+ assert out == 'hello\n'
+ assert not err
+ # without registering any hooks, precmd() should be called
+ assert app.called_precmd == 1
+
+ app.reset_counters()
+ app.register_precmd_hook(app.precmd_hook)
+ app.onecmd_plus_hooks('say hello')
+ out, err = capsys.readouterr()
+ assert out == 'hello\n'
+ assert not err
+ # with one hook registered, we should get precmd() and the hook
+ assert app.called_precmd == 2
+
+ # register the function again, so it should be called twice
+ app.reset_counters()
+ app.register_precmd_hook(app.precmd_hook)
+ app.onecmd_plus_hooks('say hello')
+ out, err = capsys.readouterr()
+ assert out == 'hello\n'
+ assert not err
+ # with two hooks registered, we should get precmd() and both hooks
+ assert app.called_precmd == 3
+
+def test_precmd_hook_emptystatement_first(capsys):
+ app = PluggedApp()
+ app.register_precmd_hook(app.precmd_hook_emptystatement)
+ stop = app.onecmd_plus_hooks('say hello')
+ out, err = capsys.readouterr()
+ assert not stop
+ assert not out
+ assert not err
+ # since the registered hooks are called before precmd(), if a registered
+ # hook throws an exception, precmd() is never called
+ assert app.called_precmd == 1
+
+ # register another function but it shouldn't be called
+ app.reset_counters()
+ stop = app.register_precmd_hook(app.precmd_hook)
+ app.onecmd_plus_hooks('say hello')
+ out, err = capsys.readouterr()
+ assert not stop
+ assert not out
+ assert not err
+ # the exception raised by the first hook should prevent the second
+ # hook from being called, and it also prevents precmd() from being
+ # called
+ assert app.called_precmd == 1
+
+def test_precmd_hook_emptystatement_second(capsys):
+ app = PluggedApp()
+ app.register_precmd_hook(app.precmd_hook)
+ stop = app.onecmd_plus_hooks('say hello')
+ out, err = capsys.readouterr()
+ assert not stop
+ assert out == 'hello\n'
+ assert not err
+ # with one hook registered, we should get precmd() and the hook
+ assert app.called_precmd == 2
+
+ # register another function and make sure it gets called
+ app.reset_counters()
+ app.register_precmd_hook(app.precmd_hook_emptystatement)
+ stop = app.onecmd_plus_hooks('say hello')
+ out, err = capsys.readouterr()
+ assert not stop
+ assert not out
+ assert not err
+ # since the registered hooks are called before precmd(), if a registered
+ # hook throws an exception, precmd() is never called
+ assert app.called_precmd == 2
+
+ # register a third function which shouldn't be called
+ app.reset_counters()
+ app.register_precmd_hook(app.precmd_hook)
+ stop = app.onecmd_plus_hooks('say hello')
+ out, err = capsys.readouterr()
+ assert not stop
+ assert not out
+ assert not err
+ # the exception raised by the second hook should prevent the third
+ # hook from being called. since the registered hooks are called before precmd(),
+ # if a registered hook throws an exception, precmd() is never called
+ assert app.called_precmd == 2
+
+###
+#
+# test postcmd hooks
+#
+####
+def test_register_postcmd_hook_parameter_count():
+ app = PluggedApp()
+ with pytest.raises(TypeError):
+ app.register_postcmd_hook(app.postcmd_hook_not_enough_parameters)
+ with pytest.raises(TypeError):
+ app.register_postcmd_hook(app.postcmd_hook_too_many_parameters)
+
+def test_register_postcmd_hook_no_parameter_annotation():
+ app = PluggedApp()
+ with pytest.raises(TypeError):
+ app.register_postcmd_hook(app.postcmd_hook_no_parameter_annotation)
+
+def test_register_postcmd_hook_wrong_parameter_annotation():
+ app = PluggedApp()
+ with pytest.raises(TypeError):
+ app.register_postcmd_hook(app.postcmd_hook_wrong_parameter_annotation)
+
+def test_register_postcmd_hook_no_return_annotation():
+ app = PluggedApp()
+ with pytest.raises(TypeError):
+ app.register_postcmd_hook(app.postcmd_hook_no_return_annotation)
+
+def test_register_postcmd_hook_wrong_return_annotation():
+ app = PluggedApp()
+ with pytest.raises(TypeError):
+ app.register_postcmd_hook(app.postcmd_hook_wrong_return_annotation)
+
+def test_postcmd(capsys):
+ app = PluggedApp()
+ app.onecmd_plus_hooks('say hello')
+ out, err = capsys.readouterr()
+ assert out == 'hello\n'
+ assert not err
+ # without registering any hooks, postcmd() should be called
+ assert app.called_postcmd == 1
+
+ app.reset_counters()
+ app.register_postcmd_hook(app.postcmd_hook)
+ app.onecmd_plus_hooks('say hello')
+ out, err = capsys.readouterr()
+ assert out == 'hello\n'
+ assert not err
+ # with one hook registered, we should get precmd() and the hook
+ assert app.called_postcmd == 2
+
+ # register the function again, so it should be called twice
+ app.reset_counters()
+ app.register_postcmd_hook(app.postcmd_hook)
+ app.onecmd_plus_hooks('say hello')
+ out, err = capsys.readouterr()
+ assert out == 'hello\n'
+ assert not err
+ # with two hooks registered, we should get precmd() and both hooks
+ assert app.called_postcmd == 3
+
+def test_postcmd_exception_first(capsys):
+ app = PluggedApp()
+ app.register_postcmd_hook(app.postcmd_hook_exception)
+ stop = app.onecmd_plus_hooks('say hello')
+ out, err = capsys.readouterr()
+ assert not stop
+ assert out == 'hello\n'
+ assert err
+ # since the registered hooks are called before postcmd(), if a registered
+ # hook throws an exception, postcmd() is never called. So we should have
+ # a count of one because we called the hook that raised the exception
+ assert app.called_postcmd == 1
+
+ # register another function but it shouldn't be called
+ app.reset_counters()
+ stop = app.register_postcmd_hook(app.postcmd_hook)
+ app.onecmd_plus_hooks('say hello')
+ out, err = capsys.readouterr()
+ assert not stop
+ assert out == 'hello\n'
+ assert err
+ # the exception raised by the first hook should prevent the second
+ # hook from being called, and it also prevents postcmd() from being
+ # called
+ assert app.called_postcmd == 1
+
+def test_postcmd_exception_second(capsys):
+ app = PluggedApp()
+ app.register_postcmd_hook(app.postcmd_hook)
+ stop = app.onecmd_plus_hooks('say hello')
+ out, err = capsys.readouterr()
+ assert not stop
+ assert out == 'hello\n'
+ assert not err
+ # with one hook registered, we should get the hook and postcmd()
+ assert app.called_postcmd == 2
+
+ # register another function which should be called
+ app.reset_counters()
+ stop = app.register_postcmd_hook(app.postcmd_hook_exception)
+ app.onecmd_plus_hooks('say hello')
+ out, err = capsys.readouterr()
+ assert not stop
+ assert out == 'hello\n'
+ assert err
+ # the exception raised by the first hook should prevent the second
+ # hook from being called, and it also prevents postcmd() from being
+ # called. So we have the first hook, and the second hook, which raised
+ # the exception
+ assert app.called_postcmd == 2
+
+##
+#
+# command finalization
+#
+###
+def test_register_cmdfinalization_hook_parameter_count():
+ app = PluggedApp()
+ with pytest.raises(TypeError):
+ app.register_cmdfinalization_hook(app.cmdfinalization_hook_not_enough_parameters)
+ with pytest.raises(TypeError):
+ app.register_cmdfinalization_hook(app.cmdfinalization_hook_too_many_parameters)
+
+def test_register_cmdfinalization_hook_no_parameter_annotation():
+ app = PluggedApp()
+ with pytest.raises(TypeError):
+ app.register_cmdfinalization_hook(app.cmdfinalization_hook_no_parameter_annotation)
+
+def test_register_cmdfinalization_hook_wrong_parameter_annotation():
+ app = PluggedApp()
+ with pytest.raises(TypeError):
+ app.register_cmdfinalization_hook(app.cmdfinalization_hook_wrong_parameter_annotation)
+
+def test_register_cmdfinalization_hook_no_return_annotation():
+ app = PluggedApp()
+ with pytest.raises(TypeError):
+ app.register_cmdfinalization_hook(app.cmdfinalization_hook_no_return_annotation)
+
+def test_register_cmdfinalization_hook_wrong_return_annotation():
+ app = PluggedApp()
+ with pytest.raises(TypeError):
+ app.register_cmdfinalization_hook(app.cmdfinalization_hook_wrong_return_annotation)
+
+def test_cmdfinalization(capsys):
+ app = PluggedApp()
+ app.onecmd_plus_hooks('say hello')
+ out, err = capsys.readouterr()
+ assert out == 'hello\n'
+ assert not err
+ assert app.called_cmdfinalization == 0
+
+ app.register_cmdfinalization_hook(app.cmdfinalization_hook)
+ app.onecmd_plus_hooks('say hello')
+ out, err = capsys.readouterr()
+ assert out == 'hello\n'
+ assert not err
+ assert app.called_cmdfinalization == 1
+
+ # register the function again, so it should be called twice
+ app.reset_counters()
+ app.register_cmdfinalization_hook(app.cmdfinalization_hook)
+ app.onecmd_plus_hooks('say hello')
+ out, err = capsys.readouterr()
+ assert out == 'hello\n'
+ assert not err
+ assert app.called_cmdfinalization == 2
+
+def test_cmdfinalization_stop_first(capsys):
+ app = PluggedApp()
+ app.register_cmdfinalization_hook(app.cmdfinalization_hook_stop)
+ app.register_cmdfinalization_hook(app.cmdfinalization_hook)
+ stop = app.onecmd_plus_hooks('say hello')
+ out, err = capsys.readouterr()
+ assert out == 'hello\n'
+ assert not err
+ assert app.called_cmdfinalization == 2
+ assert stop
+
+def test_cmdfinalization_stop_second(capsys):
+ app = PluggedApp()
+ app.register_cmdfinalization_hook(app.cmdfinalization_hook)
+ app.register_cmdfinalization_hook(app.cmdfinalization_hook_stop)
+ stop = app.onecmd_plus_hooks('say hello')
+ out, err = capsys.readouterr()
+ assert out == 'hello\n'
+ assert not err
+ assert app.called_cmdfinalization == 2
+ assert stop
+
+def test_cmdfinalization_hook_exception(capsys):
+ app = PluggedApp()
+ app.register_cmdfinalization_hook(app.cmdfinalization_hook_exception)
+ stop = app.onecmd_plus_hooks('say hello')
+ out, err = capsys.readouterr()
+ assert not stop
+ assert out == 'hello\n'
+ assert err
+ assert app.called_cmdfinalization == 1
+
+ # register another function, but it shouldn't be called
+ app.reset_counters()
+ app.register_cmdfinalization_hook(app.cmdfinalization_hook)
+ stop = app.onecmd_plus_hooks('say hello')
+ out, err = capsys.readouterr()
+ assert not stop
+ assert out == 'hello\n'
+ assert err
+ assert app.called_cmdfinalization == 1