summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/test_cmd2.py2
-rw-r--r--tests/test_parsing.py120
-rw-r--r--tests/test_plugin.py823
3 files changed, 928 insertions, 17 deletions
diff --git a/tests/test_cmd2.py b/tests/test_cmd2.py
index 25d1db3f..74e3e45a 100644
--- a/tests/test_cmd2.py
+++ b/tests/test_cmd2.py
@@ -29,7 +29,7 @@ from .conftest import run_cmd, normalize, BASE_HELP, BASE_HELP_VERBOSE, \
def test_ver():
- assert cmd2.__version__ == '0.9.3'
+ assert cmd2.__version__ == '0.9.4'
def test_empty_statement(base_app):
diff --git a/tests/test_parsing.py b/tests/test_parsing.py
index 5dc78745..6e795660 100644
--- a/tests/test_parsing.py
+++ b/tests/test_parsing.py
@@ -26,10 +26,37 @@ def parser():
)
return parser
+@pytest.fixture
+def default_parser():
+ parser = StatementParser()
+ return parser
+
+def test_parse_empty_string_default(default_parser):
+ statement = default_parser.parse('')
+ assert not statement.command
+ assert not statement.args
+ assert statement == ''
+ assert statement.raw == ''
+
+@pytest.mark.parametrize('line,tokens', [
+ ('command', ['command']),
+ ('command /* with some comment */ arg', ['command', 'arg']),
+ ('command arg1 arg2 # comment at the end', ['command', 'arg1', 'arg2']),
+ ('termbare ; > /tmp/output', ['termbare', ';', '>', '/tmp/output']),
+ ('termbare; > /tmp/output', ['termbare', ';', '>', '/tmp/output']),
+ ('termbare & > /tmp/output', ['termbare', '&', '>', '/tmp/output']),
+ ('termbare& > /tmp/output', ['termbare&', '>', '/tmp/output']),
+ ('help|less', ['help', '|', 'less']),
+])
+def test_tokenize_default(default_parser, line, tokens):
+ tokens_to_test = default_parser.tokenize(line)
+ assert tokens_to_test == tokens
+
def test_parse_empty_string(parser):
statement = parser.parse('')
assert not statement.command
assert not statement.args
+ assert statement == ''
assert statement.raw == ''
@pytest.mark.parametrize('line,tokens', [
@@ -71,7 +98,8 @@ def test_command_and_args(parser, tokens, command, args):
def test_parse_single_word(parser, line):
statement = parser.parse(line)
assert statement.command == line
- assert not statement.args
+ assert statement.args is None
+ assert statement == ''
assert statement.argv == [utils.strip_quotes(line)]
@pytest.mark.parametrize('line,terminator', [
@@ -83,8 +111,10 @@ def test_parse_single_word(parser, line):
def test_parse_word_plus_terminator(parser, line, terminator):
statement = parser.parse(line)
assert statement.command == 'termbare'
- assert statement.terminator == terminator
+ assert statement.args is None
+ assert statement == ''
assert statement.argv == ['termbare']
+ assert statement.terminator == terminator
@pytest.mark.parametrize('line,terminator', [
('termbare; suffx', ';'),
@@ -95,15 +125,18 @@ def test_parse_word_plus_terminator(parser, line, terminator):
def test_parse_suffix_after_terminator(parser, line, terminator):
statement = parser.parse(line)
assert statement.command == 'termbare'
+ assert statement.args is None
+ assert statement == ''
+ assert statement.argv == ['termbare']
assert statement.terminator == terminator
assert statement.suffix == 'suffx'
- assert statement.argv == ['termbare']
def test_parse_command_with_args(parser):
line = 'command with args'
statement = parser.parse(line)
assert statement.command == 'command'
assert statement.args == 'with args'
+ assert statement == statement.args
assert statement.argv == ['command', 'with', 'args']
def test_parse_command_with_quoted_args(parser):
@@ -111,6 +144,7 @@ def test_parse_command_with_quoted_args(parser):
statement = parser.parse(line)
assert statement.command == 'command'
assert statement.args == 'with "quoted args" and "some not"'
+ assert statement == statement.args
assert statement.argv == ['command', 'with', 'quoted args', 'and', 'some not']
def test_parse_command_with_args_terminator_and_suffix(parser):
@@ -118,22 +152,25 @@ def test_parse_command_with_args_terminator_and_suffix(parser):
statement = parser.parse(line)
assert statement.command == 'command'
assert statement.args == "with args and terminator"
+ assert statement.argv == ['command', 'with', 'args', 'and', 'terminator']
+ assert statement == statement.args
assert statement.terminator == ';'
assert statement.suffix == 'and suffix'
- assert statement.argv == ['command', 'with', 'args', 'and', 'terminator']
def test_parse_hashcomment(parser):
statement = parser.parse('hi # this is all a comment')
assert statement.command == 'hi'
- assert not statement.args
+ assert statement.args is None
+ assert statement == ''
assert statement.argv == ['hi']
def test_parse_c_comment(parser):
statement = parser.parse('hi /* this is | all a comment */')
assert statement.command == 'hi'
- assert not statement.args
- assert not statement.pipe_to
+ assert statement.args is None
assert statement.argv == ['hi']
+ assert statement == ''
+ assert not statement.pipe_to
def test_parse_c_comment_empty(parser):
statement = parser.parse('/* this is | all a comment */')
@@ -141,6 +178,7 @@ def test_parse_c_comment_empty(parser):
assert not statement.args
assert not statement.pipe_to
assert not statement.argv
+ assert statement == ''
def test_parse_c_comment_no_closing(parser):
statement = parser.parse('cat /tmp/*.txt')
@@ -160,8 +198,9 @@ def test_parse_what_if_quoted_strings_seem_to_start_comments(parser):
statement = parser.parse('what if "quoted strings /* seem to " start comments?')
assert statement.command == 'what'
assert statement.args == 'if "quoted strings /* seem to " start comments?'
- assert not statement.pipe_to
assert statement.argv == ['what', 'if', 'quoted strings /* seem to ', 'start', 'comments?']
+ assert statement == statement.args
+ assert not statement.pipe_to
@pytest.mark.parametrize('line',[
'simple | piped',
@@ -170,7 +209,8 @@ def test_parse_what_if_quoted_strings_seem_to_start_comments(parser):
def test_parse_simple_pipe(parser, line):
statement = parser.parse(line)
assert statement.command == 'simple'
- assert not statement.args
+ assert statement.args is None
+ assert statement == ''
assert statement.argv == ['simple']
assert statement.pipe_to == ['piped']
@@ -179,6 +219,7 @@ def test_parse_double_pipe_is_not_a_pipe(parser):
statement = parser.parse(line)
assert statement.command == 'double-pipe'
assert statement.args == '|| is not a pipe'
+ assert statement == statement.args
assert statement.argv == ['double-pipe', '||', 'is', 'not', 'a', 'pipe']
assert not statement.pipe_to
@@ -188,6 +229,7 @@ def test_parse_complex_pipe(parser):
assert statement.command == 'command'
assert statement.args == "with args, terminator"
assert statement.argv == ['command', 'with', 'args,', 'terminator']
+ assert statement == statement.args
assert statement.terminator == '&'
assert statement.suffix == 'sufx'
assert statement.pipe_to == ['piped']
@@ -201,7 +243,8 @@ def test_parse_complex_pipe(parser):
def test_parse_redirect(parser,line, output):
statement = parser.parse(line)
assert statement.command == 'help'
- assert not statement.args
+ assert statement.args is None
+ assert statement == ''
assert statement.output == output
assert statement.output_to == 'out.txt'
@@ -210,6 +253,7 @@ def test_parse_redirect_with_args(parser):
statement = parser.parse(line)
assert statement.command == 'output'
assert statement.args == 'into'
+ assert statement == statement.args
assert statement.argv == ['output', 'into']
assert statement.output == '>'
assert statement.output_to == 'afile.txt'
@@ -219,6 +263,7 @@ def test_parse_redirect_with_dash_in_path(parser):
statement = parser.parse(line)
assert statement.command == 'output'
assert statement.args == 'into'
+ assert statement == statement.args
assert statement.argv == ['output', 'into']
assert statement.output == '>'
assert statement.output_to == 'python-cmd2/afile.txt'
@@ -228,6 +273,7 @@ def test_parse_redirect_append(parser):
statement = parser.parse(line)
assert statement.command == 'output'
assert statement.args == 'appended to'
+ assert statement == statement.args
assert statement.argv == ['output', 'appended', 'to']
assert statement.output == '>>'
assert statement.output_to == '/tmp/afile.txt'
@@ -237,6 +283,7 @@ def test_parse_pipe_and_redirect(parser):
statement = parser.parse(line)
assert statement.command == 'output'
assert statement.args == 'into'
+ assert statement == statement.args
assert statement.argv == ['output', 'into']
assert statement.terminator == ';'
assert statement.suffix == 'sufx'
@@ -249,6 +296,7 @@ def test_parse_output_to_paste_buffer(parser):
statement = parser.parse(line)
assert statement.command == 'output'
assert statement.args == 'to paste buffer'
+ assert statement == statement.args
assert statement.argv == ['output', 'to', 'paste', 'buffer']
assert statement.output == '>>'
@@ -260,6 +308,7 @@ def test_parse_redirect_inside_terminator(parser):
statement = parser.parse(line)
assert statement.command == 'has'
assert statement.args == '> inside'
+ assert statement == statement.args
assert statement.argv == ['has', '>', 'inside']
assert statement.terminator == ';'
@@ -277,6 +326,7 @@ def test_parse_multiple_terminators(parser, line, terminator):
statement = parser.parse(line)
assert statement.multiline_command == 'multiline'
assert statement.args == 'with | inside'
+ assert statement == statement.args
assert statement.argv == ['multiline', 'with', '|', 'inside']
assert statement.terminator == terminator
@@ -286,6 +336,7 @@ def test_parse_unfinished_multiliine_command(parser):
assert statement.multiline_command == 'multiline'
assert statement.command == 'multiline'
assert statement.args == 'has > inside an unfinished command'
+ assert statement == statement.args
assert statement.argv == ['multiline', 'has', '>', 'inside', 'an', 'unfinished', 'command']
assert not statement.terminator
@@ -300,6 +351,7 @@ def test_parse_multiline_command_ignores_redirectors_within_it(parser, line, ter
statement = parser.parse(line)
assert statement.multiline_command == 'multiline'
assert statement.args == 'has > inside'
+ assert statement == statement.args
assert statement.argv == ['multiline', 'has', '>', 'inside']
assert statement.terminator == terminator
@@ -320,6 +372,7 @@ def test_parse_multiline_with_complete_comment(parser):
assert statement.multiline_command == 'multiline'
assert statement.command == 'multiline'
assert statement.args == 'command is done'
+ assert statement == statement.args
assert statement.argv == ['multiline', 'command', 'is', 'done']
assert statement.terminator == ';'
@@ -329,6 +382,7 @@ def test_parse_multiline_termninated_by_empty_line(parser):
assert statement.multiline_command == 'multiline'
assert statement.command == 'multiline'
assert statement.args == 'command ends'
+ assert statement == statement.args
assert statement.argv == ['multiline', 'command', 'ends']
assert statement.terminator == '\n'
@@ -338,6 +392,7 @@ def test_parse_multiline_ignores_terminators_in_comments(parser):
assert statement.multiline_command == 'multiline'
assert statement.command == 'multiline'
assert statement.args == 'command "with term; ends" now'
+ assert statement == statement.args
assert statement.argv == ['multiline', 'command', 'with term; ends', 'now']
assert statement.terminator == '\n'
@@ -346,6 +401,7 @@ def test_parse_command_with_unicode_args(parser):
statement = parser.parse(line)
assert statement.command == 'drink'
assert statement.args == 'café'
+ assert statement == statement.args
assert statement.argv == ['drink', 'café']
def test_parse_unicode_command(parser):
@@ -353,6 +409,7 @@ def test_parse_unicode_command(parser):
statement = parser.parse(line)
assert statement.command == 'café'
assert statement.args == 'au lait'
+ assert statement == statement.args
assert statement.argv == ['café', 'au', 'lait']
def test_parse_redirect_to_unicode_filename(parser):
@@ -360,6 +417,7 @@ def test_parse_redirect_to_unicode_filename(parser):
statement = parser.parse(line)
assert statement.command == 'dir'
assert statement.args == 'home'
+ assert statement == statement.args
assert statement.argv == ['dir', 'home']
assert statement.output == '>'
assert statement.output_to == 'café'
@@ -389,6 +447,10 @@ def test_parse_alias_and_shortcut_expansion(parser, line, command, args):
statement = parser.parse(line)
assert statement.command == command
assert statement.args == args
+ if statement.args is None:
+ assert statement == ''
+ else:
+ assert statement == statement.args
def test_parse_alias_on_multiline_command(parser):
line = 'anothermultiline has > inside an unfinished command'
@@ -396,6 +458,7 @@ def test_parse_alias_on_multiline_command(parser):
assert statement.multiline_command == 'multiline'
assert statement.command == 'multiline'
assert statement.args == 'has > inside an unfinished command'
+ assert statement == statement.args
assert not statement.terminator
@pytest.mark.parametrize('line,output', [
@@ -407,7 +470,8 @@ def test_parse_alias_on_multiline_command(parser):
def test_parse_alias_redirection(parser, line, output):
statement = parser.parse(line)
assert statement.command == 'help'
- assert not statement.args
+ assert statement.args is None
+ assert statement == ''
assert statement.output == output
assert statement.output_to == 'out.txt'
@@ -418,7 +482,8 @@ def test_parse_alias_redirection(parser, line, output):
def test_parse_alias_pipe(parser, line):
statement = parser.parse(line)
assert statement.command == 'help'
- assert not statement.args
+ assert statement.args is None
+ assert statement == ''
assert statement.pipe_to == ['less']
@pytest.mark.parametrize('line', [
@@ -432,7 +497,8 @@ def test_parse_alias_pipe(parser, line):
def test_parse_alias_terminator_no_whitespace(parser, line):
statement = parser.parse(line)
assert statement.command == 'help'
- assert not statement.args
+ assert statement.args is None
+ assert statement == ''
assert statement.terminator == ';'
def test_parse_command_only_command_and_args(parser):
@@ -440,6 +506,7 @@ def test_parse_command_only_command_and_args(parser):
statement = parser.parse_command_only(line)
assert statement.command == 'help'
assert statement.args == 'history'
+ assert statement == statement.args
assert statement.command_and_args == line
def test_parse_command_only_emptyline(parser):
@@ -448,9 +515,9 @@ def test_parse_command_only_emptyline(parser):
# statement is a subclass of str(), the value of the str
# should be '', to retain backwards compatibility with
# the cmd in the standard library
- assert statement == ''
assert statement.command is None
assert statement.args is None
+ assert statement == ''
assert not statement.argv
assert statement.command_and_args == None
@@ -459,6 +526,7 @@ def test_parse_command_only_strips_line(parser):
statement = parser.parse_command_only(line)
assert statement.command == 'help'
assert statement.args == 'history'
+ assert statement == statement.args
assert statement.command_and_args == line.strip()
def test_parse_command_only_expands_alias(parser):
@@ -466,12 +534,14 @@ def test_parse_command_only_expands_alias(parser):
statement = parser.parse_command_only(line)
assert statement.command == 'pyscript'
assert statement.args == 'foobar.py'
+ assert statement == statement.args
def test_parse_command_only_expands_shortcuts(parser):
line = '!cat foobar.txt'
statement = parser.parse_command_only(line)
assert statement.command == 'shell'
assert statement.args == 'cat foobar.txt'
+ assert statement == statement.args
assert statement.command_and_args == 'shell cat foobar.txt'
def test_parse_command_only_quoted_args(parser):
@@ -479,6 +549,7 @@ def test_parse_command_only_quoted_args(parser):
statement = parser.parse_command_only(line)
assert statement.command == 'shell'
assert statement.args == 'ls -al "/tmp/directory with spaces/doit.sh"'
+ assert statement == statement.args
assert statement.command_and_args == line.replace('l', 'shell ls -al')
@pytest.mark.parametrize('line', [
@@ -509,5 +580,22 @@ def test_parse_command_only_specialchars(parser, line):
])
def test_parse_command_only_none(parser, line):
statement = parser.parse_command_only(line)
- assert statement.command == None
- assert statement.args == None
+ assert statement.command is None
+ assert statement.args is None
+ assert statement == ''
+
+def test_statement_initialization(parser):
+ string = 'alias'
+ statement = cmd2.Statement(string)
+ assert string == statement
+ assert statement.raw is None
+ assert statement.command is None
+ assert statement.args is None
+ assert isinstance(statement.argv, list)
+ assert not statement.argv
+ assert statement.multiline_command is None
+ assert statement.terminator is None
+ assert statement.suffix is None
+ assert statement.pipe_to is None
+ assert statement.output is None
+ assert statement.output_to is None
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