summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd2/pyscript_bridge.py41
-rw-r--r--cmd2/transcript.py23
-rw-r--r--cmd2/utils.py58
-rw-r--r--tests/conftest.py22
-rw-r--r--tests/test_argparse.py10
-rw-r--r--tests/test_autocompletion.py12
-rw-r--r--tests/test_cmd2.py60
-rw-r--r--tests/test_completion.py14
-rw-r--r--tests/test_plugin.py74
-rw-r--r--tests/test_pyscript.py20
-rw-r--r--tests/test_transcript.py16
-rw-r--r--tests/test_utils.py55
12 files changed, 225 insertions, 180 deletions
diff --git a/cmd2/pyscript_bridge.py b/cmd2/pyscript_bridge.py
index 3f58ab84..d8fa5fb9 100644
--- a/cmd2/pyscript_bridge.py
+++ b/cmd2/pyscript_bridge.py
@@ -12,15 +12,15 @@ import functools
import sys
from typing import List, Callable
+from .argparse_completer import _RangeAction
+from .utils import namedtuple_with_defaults, StdSim
+
# Python 3.4 require contextlib2 for temporarily redirecting stderr and stdout
if sys.version_info < (3, 5):
from contextlib2 import redirect_stdout, redirect_stderr
else:
from contextlib import redirect_stdout, redirect_stderr
-from .argparse_completer import _RangeAction
-from .utils import namedtuple_with_defaults
-
class CommandResult(namedtuple_with_defaults('CommandResult', ['stdout', 'stderr', 'data'])):
"""Encapsulates the results from a command.
@@ -38,37 +38,12 @@ class CommandResult(namedtuple_with_defaults('CommandResult', ['stdout', 'stderr
return not self.stderr and self.data is not None
-class CopyStream(object):
- """Copies all data written to a stream"""
- def __init__(self, inner_stream, echo: bool = False) -> None:
- self.buffer = ''
- self.inner_stream = inner_stream
- self.echo = echo
-
- def write(self, s):
- self.buffer += s
- if self.echo:
- self.inner_stream.write(s)
-
- def read(self):
- raise NotImplementedError
-
- def clear(self):
- self.buffer = ''
-
- def __getattr__(self, item: str):
- if item in self.__dict__:
- return self.__dict__[item]
- else:
- return getattr(self.inner_stream, item)
-
-
def _exec_cmd(cmd2_app, func: Callable, echo: bool):
"""Helper to encapsulate executing a command and capturing the results"""
- copy_stdout = CopyStream(sys.stdout, echo)
- copy_stderr = CopyStream(sys.stderr, echo)
+ copy_stdout = StdSim(sys.stdout, echo)
+ copy_stderr = StdSim(sys.stderr, echo)
- copy_cmd_stdout = CopyStream(cmd2_app.stdout, echo)
+ copy_cmd_stdout = StdSim(cmd2_app.stdout, echo)
cmd2_app._last_result = None
@@ -81,9 +56,9 @@ def _exec_cmd(cmd2_app, func: Callable, echo: bool):
cmd2_app.stdout = copy_cmd_stdout.inner_stream
# if stderr is empty, set it to None
- stderr = copy_stderr.buffer if copy_stderr.buffer else None
+ stderr = copy_stderr.getvalue() if copy_stderr.getvalue() else None
- outbuf = copy_cmd_stdout.buffer if copy_cmd_stdout.buffer else copy_stdout.buffer
+ outbuf = copy_cmd_stdout.getvalue() if copy_cmd_stdout.getvalue() else copy_stdout.getvalue()
result = CommandResult(stdout=outbuf, stderr=stderr, data=cmd2_app._last_result)
return result
diff --git a/cmd2/transcript.py b/cmd2/transcript.py
index 8df58634..2d94f4e4 100644
--- a/cmd2/transcript.py
+++ b/cmd2/transcript.py
@@ -44,7 +44,7 @@ class Cmd2TestCase(unittest.TestCase):
# Trap stdout
self._orig_stdout = self.cmdapp.stdout
- self.cmdapp.stdout = OutputTrap()
+ self.cmdapp.stdout = utils.StdSim(self.cmdapp.stdout)
def runTest(self): # was testall
if self.cmdapp:
@@ -203,24 +203,3 @@ class Cmd2TestCase(unittest.TestCase):
if self.cmdapp:
# Restore stdout
self.cmdapp.stdout = self._orig_stdout
-
-class OutputTrap(object):
- """Instantiate an OutputTrap to divert/capture ALL stdout output.
- For use in transcript testing.
- """
-
- def __init__(self):
- self.contents = ''
-
- def write(self, txt: str):
- """Add text to the internal contents."""
- self.contents += txt
-
- def read(self) -> str:
- """Read from the internal contents and then clear them out.
-
- :return: str - text from the internal contents
- """
- result = self.contents
- self.contents = ''
- return result
diff --git a/cmd2/utils.py b/cmd2/utils.py
index 735221c8..bdb488cc 100644
--- a/cmd2/utils.py
+++ b/cmd2/utils.py
@@ -246,3 +246,61 @@ def natural_sort(list_to_sort: List[str]) -> List[str]:
:return: the list sorted naturally
"""
return sorted(list_to_sort, key=natural_keys)
+
+
+class StdSim(object):
+ """Class to simulate behavior of sys.stdout or sys.stderr.
+
+ Stores contents in internal buffer and optionally echos to the inner stream it is simulating.
+ """
+ class ByteBuf(object):
+ """Inner class which stores an actual bytes buffer and does the actual output if echo is enabled."""
+ def __init__(self, inner_stream, echo: bool = False) -> None:
+ self.byte_buf = b''
+ self.inner_stream = inner_stream
+ self.echo = echo
+
+ def write(self, b: bytes) -> None:
+ """Add bytes to internal bytes buffer and if echo is True, echo contents to inner stream."""
+ if not isinstance(b, bytes):
+ raise TypeError('a bytes-like object is required, not {}'.format(type(b)))
+ self.byte_buf += b
+ if self.echo:
+ self.inner_stream.buffer.write(b)
+
+ def __init__(self, inner_stream, echo: bool = False) -> None:
+ self.buffer = self.ByteBuf(inner_stream, echo)
+ self.inner_stream = inner_stream
+
+ def write(self, s: str) -> None:
+ """Add str to internal bytes buffer and if echo is True, echo contents to inner stream."""
+ if not isinstance(s, str):
+ raise TypeError('write() argument must be str, not {}'.format(type(s)))
+ b = s.encode()
+ self.buffer.write(b)
+
+ def getvalue(self) -> str:
+ """Get the internal contents as a str.
+
+ :return string from the internal contents
+ """
+ return self.buffer.byte_buf.decode()
+
+ def read(self) -> str:
+ """Read from the internal contents as a str and then clear them out.
+
+ :return: string from the internal contents
+ """
+ result = self.getvalue()
+ self.clear()
+ return result
+
+ def clear(self) -> None:
+ """Clear the internal contents."""
+ self.buffer.byte_buf = b''
+
+ def __getattr__(self, item: str):
+ if item in self.__dict__:
+ return self.__dict__[item]
+ else:
+ return getattr(self.inner_stream, item)
diff --git a/tests/conftest.py b/tests/conftest.py
index a85135b9..561f281b 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -12,6 +12,7 @@ from unittest import mock
from pytest import fixture
import cmd2
+from cmd2.utils import StdSim
# Prefer statically linked gnureadline if available (for macOS compatibility due to issues with libedit)
try:
@@ -115,21 +116,6 @@ timing: False # Report execution times
""".format(color_str)
-class StdOut(object):
- """ Toy class for replacing self.stdout in cmd2.Cmd instances for unit testing. """
- def __init__(self):
- self.buffer = ''
-
- def write(self, s):
- self.buffer += s
-
- def read(self):
- raise NotImplementedError
-
- def clear(self):
- self.buffer = ''
-
-
def normalize(block):
""" Normalize a block of text to perform comparison.
@@ -142,10 +128,10 @@ def normalize(block):
def run_cmd(app, cmd):
- """ Clear StdOut buffer, run the command, extract the buffer contents, """
+ """ Clear StdSim buffer, run the command, extract the buffer contents, """
app.stdout.clear()
app.onecmd_plus_hooks(cmd)
- out = app.stdout.buffer
+ out = app.stdout.getvalue()
app.stdout.clear()
return normalize(out)
@@ -153,7 +139,7 @@ def run_cmd(app, cmd):
@fixture
def base_app():
c = cmd2.Cmd()
- c.stdout = StdOut()
+ c.stdout = StdSim(c.stdout)
return c
diff --git a/tests/test_argparse.py b/tests/test_argparse.py
index 469cbe76..bf20710b 100644
--- a/tests/test_argparse.py
+++ b/tests/test_argparse.py
@@ -6,9 +6,9 @@ import argparse
import pytest
import cmd2
-from unittest import mock
+from cmd2.utils import StdSim
-from .conftest import run_cmd, StdOut
+from .conftest import run_cmd
# Prefer statically linked gnureadline if available (for macOS compatibility due to issues with libedit)
try:
@@ -115,7 +115,7 @@ class ArgparseApp(cmd2.Cmd):
@pytest.fixture
def argparse_app():
app = ArgparseApp()
- app.stdout = StdOut()
+ app.stdout = StdSim(app.stdout)
return app
@@ -217,12 +217,12 @@ class SubcommandApp(cmd2.Cmd):
func(self, args)
else:
# No subcommand was provided, so call help
- self.do_help('base')
+ self.do_help(['base'])
@pytest.fixture
def subcommand_app():
app = SubcommandApp()
- app.stdout = StdOut()
+ app.stdout = StdSim(app.stdout)
return app
diff --git a/tests/test_autocompletion.py b/tests/test_autocompletion.py
index 8aa26e0e..8035587a 100644
--- a/tests/test_autocompletion.py
+++ b/tests/test_autocompletion.py
@@ -1,3 +1,4 @@
+# coding=utf-8
"""
Unit/functional testing for argparse completer in cmd2
@@ -5,16 +6,17 @@ Copyright 2018 Eric Lin <anselor@gmail.com>
Released under MIT license, see LICENSE file
"""
import pytest
-from .conftest import run_cmd, normalize, StdOut, complete_tester
+
+from cmd2.utils import StdSim
+from .conftest import run_cmd, normalize, complete_tester
from examples.tab_autocompletion import TabCompleteExample
@pytest.fixture
def cmd2_app():
- c = TabCompleteExample()
- c.stdout = StdOut()
-
- return c
+ app = TabCompleteExample()
+ app.stdout = StdSim(app.stdout)
+ return app
SUGGEST_HELP = '''Usage: suggest -t {movie, show} [-h] [-d DURATION{1..2}]
diff --git a/tests/test_cmd2.py b/tests/test_cmd2.py
index 1e7e2c3f..4b0687c9 100644
--- a/tests/test_cmd2.py
+++ b/tests/test_cmd2.py
@@ -25,7 +25,7 @@ import cmd2
from cmd2 import clipboard
from cmd2 import utils
from .conftest import run_cmd, normalize, BASE_HELP, BASE_HELP_VERBOSE, \
- HELP_HISTORY, SHORTCUTS_TXT, SHOW_TXT, SHOW_LONG, StdOut
+ HELP_HISTORY, SHORTCUTS_TXT, SHOW_TXT, SHOW_LONG
def test_version(base_app):
@@ -930,7 +930,7 @@ def test_base_cmdloop_with_queue():
app.use_rawinput = True
intro = 'Hello World, this is an intro ...'
app.cmdqueue.append('quit\n')
- app.stdout = StdOut()
+ app.stdout = utils.StdSim(app.stdout)
# Need to patch sys.argv so cmd2 doesn't think it was called with arguments equal to the py.test args
testargs = ["prog"]
@@ -938,7 +938,7 @@ def test_base_cmdloop_with_queue():
with mock.patch.object(sys, 'argv', testargs):
# Run the command loop with custom intro
app.cmdloop(intro=intro)
- out = app.stdout.buffer
+ out = app.stdout.getvalue()
assert out == expected
@@ -947,7 +947,7 @@ def test_base_cmdloop_without_queue():
app = cmd2.Cmd()
app.use_rawinput = True
app.intro = 'Hello World, this is an intro ...'
- app.stdout = StdOut()
+ app.stdout = utils.StdSim(app.stdout)
# Mock out the input call so we don't actually wait for a user's response on stdin
m = mock.MagicMock(name='input', return_value='quit')
@@ -959,7 +959,7 @@ def test_base_cmdloop_without_queue():
with mock.patch.object(sys, 'argv', testargs):
# Run the command loop
app.cmdloop()
- out = app.stdout.buffer
+ out = app.stdout.getvalue()
assert out == expected
@@ -969,7 +969,7 @@ def test_cmdloop_without_rawinput():
app.use_rawinput = False
app.echo = False
app.intro = 'Hello World, this is an intro ...'
- app.stdout = StdOut()
+ app.stdout = utils.StdSim(app.stdout)
# Mock out the input call so we don't actually wait for a user's response on stdin
m = mock.MagicMock(name='input', return_value='quit')
@@ -981,7 +981,7 @@ def test_cmdloop_without_rawinput():
with mock.patch.object(sys, 'argv', testargs):
# Run the command loop
app.cmdloop()
- out = app.stdout.buffer
+ out = app.stdout.getvalue()
assert out == expected
@@ -996,7 +996,7 @@ class HookFailureApp(cmd2.Cmd):
@pytest.fixture
def hook_failure():
app = HookFailureApp()
- app.stdout = StdOut()
+ app.stdout = utils.StdSim(app.stdout)
return app
def test_precmd_hook_success(base_app):
@@ -1020,7 +1020,7 @@ class SayApp(cmd2.Cmd):
def say_app():
app = SayApp()
app.allow_cli_args = False
- app.stdout = StdOut()
+ app.stdout = utils.StdSim(app.stdout)
return app
def test_interrupt_quit(say_app):
@@ -1034,7 +1034,7 @@ def test_interrupt_quit(say_app):
say_app.cmdloop()
# And verify the expected output to stdout
- out = say_app.stdout.buffer
+ out = say_app.stdout.getvalue()
assert out == 'hello\n'
def test_interrupt_noquit(say_app):
@@ -1048,7 +1048,7 @@ def test_interrupt_noquit(say_app):
say_app.cmdloop()
# And verify the expected output to stdout
- out = say_app.stdout.buffer
+ out = say_app.stdout.getvalue()
assert out == 'hello\n^C\ngoodbye\n'
@@ -1060,7 +1060,7 @@ class ShellApp(cmd2.Cmd):
@pytest.fixture
def shell_app():
app = ShellApp()
- app.stdout = StdOut()
+ app.stdout = utils.StdSim(app.stdout)
return app
def test_default_to_shell_unknown(shell_app):
@@ -1140,7 +1140,7 @@ class HelpApp(cmd2.Cmd):
@pytest.fixture
def help_app():
app = HelpApp()
- app.stdout = StdOut()
+ app.stdout = utils.StdSim(app.stdout)
return app
def test_custom_command_help(help_app):
@@ -1203,7 +1203,7 @@ class HelpCategoriesApp(cmd2.Cmd):
@pytest.fixture
def helpcat_app():
app = HelpCategoriesApp()
- app.stdout = StdOut()
+ app.stdout = utils.StdSim(app.stdout)
return app
def test_help_cat_base(helpcat_app):
@@ -1296,7 +1296,7 @@ class SelectApp(cmd2.Cmd):
@pytest.fixture
def select_app():
app = SelectApp()
- app.stdout = StdOut()
+ app.stdout = utils.StdSim(app.stdout)
return app
def test_select_options(select_app):
@@ -1461,7 +1461,7 @@ class MultilineApp(cmd2.Cmd):
@pytest.fixture
def multiline_app():
app = MultilineApp()
- app.stdout = StdOut()
+ app.stdout = utils.StdSim(app.stdout)
return app
def test_multiline_complete_empty_statement_raises_exception(multiline_app):
@@ -1522,7 +1522,7 @@ class CommandResultApp(cmd2.Cmd):
@pytest.fixture
def commandresult_app():
app = CommandResultApp()
- app.stdout = StdOut()
+ app.stdout = utils.StdSim(app.stdout)
return app
def test_commandresult_truthy(commandresult_app):
@@ -1628,7 +1628,7 @@ def piped_rawinput_true(capsys, echo, command):
# run the cmdloop, which should pull input from our mock
app._cmdloop()
out, err = capsys.readouterr()
- return (app, out)
+ return app, out
# using the decorator puts the original input function back when this unit test returns
@mock.patch('builtins.input', mock.MagicMock(name='input', side_effect=['set', EOFError]))
@@ -1658,7 +1658,7 @@ def piped_rawinput_false(capsys, echo, command):
app.echo = echo
app._cmdloop()
out, err = capsys.readouterr()
- return (app, out)
+ return app, out
def test_pseudo_raw_input_piped_rawinput_false_echo_true(capsys):
command = 'set'
@@ -1715,28 +1715,28 @@ def test_empty_stdin_input():
def test_poutput_string(base_app):
msg = 'This is a test'
base_app.poutput(msg)
- out = base_app.stdout.buffer
+ out = base_app.stdout.getvalue()
expected = msg + '\n'
assert out == expected
def test_poutput_zero(base_app):
msg = 0
base_app.poutput(msg)
- out = base_app.stdout.buffer
+ out = base_app.stdout.getvalue()
expected = str(msg) + '\n'
assert out == expected
def test_poutput_empty_string(base_app):
msg = ''
base_app.poutput(msg)
- out = base_app.stdout.buffer
+ out = base_app.stdout.getvalue()
expected = msg
assert out == expected
def test_poutput_none(base_app):
msg = None
base_app.poutput(msg)
- out = base_app.stdout.buffer
+ out = base_app.stdout.getvalue()
expected = ''
assert out == expected
@@ -1826,7 +1826,7 @@ def test_ppaged(base_app):
msg = 'testing...'
end = '\n'
base_app.ppaged(msg)
- out = base_app.stdout.buffer
+ out = base_app.stdout.getvalue()
assert out == msg + end
# we override cmd.parseline() so we always get consistent
@@ -1859,14 +1859,14 @@ def test_readline_remove_history_item(base_app):
def test_onecmd_raw_str_continue(base_app):
line = "help"
stop = base_app.onecmd(line)
- out = base_app.stdout.buffer
+ out = base_app.stdout.getvalue()
assert not stop
assert out.strip() == BASE_HELP.strip()
def test_onecmd_raw_str_quit(base_app):
line = "quit"
stop = base_app.onecmd(line)
- out = base_app.stdout.buffer
+ out = base_app.stdout.getvalue()
assert stop
assert out == ''
@@ -1996,7 +1996,7 @@ def test_exit_code_default(exit_code_repl):
# Create a cmd2.Cmd() instance and make sure basic settings are like we want for test
app = exit_code_repl
app.use_rawinput = True
- app.stdout = StdOut()
+ app.stdout = utils.StdSim(app.stdout)
# Mock out the input call so we don't actually wait for a user's response on stdin
m = mock.MagicMock(name='input', return_value='exit')
@@ -2008,14 +2008,14 @@ def test_exit_code_default(exit_code_repl):
with mock.patch.object(sys, 'argv', testargs):
# Run the command loop
app.cmdloop()
- out = app.stdout.buffer
+ out = app.stdout.getvalue()
assert out == expected
def test_exit_code_nonzero(exit_code_repl):
# Create a cmd2.Cmd() instance and make sure basic settings are like we want for test
app = exit_code_repl
app.use_rawinput = True
- app.stdout = StdOut()
+ app.stdout = utils.StdSim(app.stdout)
# Mock out the input call so we don't actually wait for a user's response on stdin
m = mock.MagicMock(name='input', return_value='exit 23')
@@ -2028,5 +2028,5 @@ def test_exit_code_nonzero(exit_code_repl):
# Run the command loop
with pytest.raises(SystemExit):
app.cmdloop()
- out = app.stdout.buffer
+ out = app.stdout.getvalue()
assert out == expected
diff --git a/tests/test_completion.py b/tests/test_completion.py
index 00a120cc..40299954 100644
--- a/tests/test_completion.py
+++ b/tests/test_completion.py
@@ -15,7 +15,7 @@ import sys
import pytest
import cmd2
from cmd2 import utils
-from .conftest import complete_tester, StdOut
+from .conftest import complete_tester
from examples.subcommands import SubcommandsExample
# List of strings used with completion functions
@@ -114,13 +114,6 @@ def test_complete_bogus_command(cmd2_app):
assert first_match is None
-def test_cmd2_command_completion_single(cmd2_app):
- text = 'hel'
- line = text
- endidx = len(line)
- begidx = endidx - len(text)
- assert cmd2_app.completenames(text, line, begidx, endidx) == ['help']
-
def test_cmd2_command_completion_multiple(cmd2_app):
text = 'h'
line = text
@@ -694,8 +687,7 @@ def test_add_opening_quote_delimited_space_in_prefix(cmd2_app):
@pytest.fixture
def sc_app():
c = SubcommandsExample()
- c.stdout = StdOut()
-
+ c.stdout = utils.StdSim(c.stdout)
return c
def test_cmd2_subcommand_completion_single_end(sc_app):
@@ -843,7 +835,7 @@ class SubcommandsWithUnknownExample(cmd2.Cmd):
func(self, args)
else:
# No subcommand was provided, so call help
- self.do_help('base')
+ self.do_help(['base'])
@pytest.fixture
diff --git a/tests/test_plugin.py b/tests/test_plugin.py
index e401e837..20f2f32c 100644
--- a/tests/test_plugin.py
+++ b/tests/test_plugin.py
@@ -5,18 +5,14 @@ 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"
+ """A mixin class for testing hook registration and calling"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.reset_counters()
@@ -35,19 +31,19 @@ class Plugin:
#
###
def prepost_hook_one(self) -> None:
- "Method used for preloop or postloop hooks"
+ """Method used for preloop or postloop hooks"""
self.poutput("one")
def prepost_hook_two(self) -> None:
- "Another method used for preloop or postloop hooks"
+ """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"
+ """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"
+ """A preloop or postloop hook with incorrect return type"""
pass
###
@@ -56,7 +52,7 @@ class Plugin:
#
###
def preparse(self, line: str) -> str:
- "Preparsing hook"
+ """Preparsing hook"""
self.called_preparse += 1
return line
@@ -66,44 +62,44 @@ class Plugin:
#
###
def postparse_hook(self, data: cmd2.plugin.PostparsingData) -> cmd2.plugin.PostparsingData:
- "A postparsing hook"
+ """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"
+ """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"
+ """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"
+ """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"
+ """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"
+ """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"
+ """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"
+ """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"
+ """A postparsing hook with the wrong return type"""
pass
###
@@ -112,43 +108,43 @@ class Plugin:
#
###
def precmd(self, statement: cmd2.Statement) -> cmd2.Statement:
- "Override cmd.Cmd method"
+ """Override cmd.Cmd method"""
self.called_precmd += 1
return statement
def precmd_hook(self, data: plugin.PrecommandData) -> plugin.PrecommandData:
- "A precommand hook"
+ """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"
+ """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"
+ """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"
+ """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"
+ """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"
+ """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"
+ """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"
+ """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:
@@ -160,38 +156,38 @@ class Plugin:
#
###
def postcmd(self, stop: bool, statement: cmd2.Statement) -> bool:
- "Override cmd.Cmd method"
+ """Override cmd.Cmd method"""
self.called_postcmd += 1
return stop
def postcmd_hook(self, data: plugin.PostcommandData) -> plugin.PostcommandData:
- "A postcommand hook"
+ """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"
+ """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"
+ """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"
+ """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"
+ """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"
+ """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"
+ """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:
@@ -208,13 +204,13 @@ class Plugin:
return data
def cmdfinalization_hook_stop(self, data: cmd2.plugin.CommandFinalizationData) -> cmd2.plugin.CommandFinalizationData:
- "A postparsing hook which requests application exit"
+ """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"
+ """A postparsing hook which raises an exception"""
self.called_cmdfinalization += 1
raise ValueError
@@ -244,7 +240,7 @@ class Plugin:
class PluggedApp(Plugin, cmd2.Cmd):
- "A sample app with a plugin mixed in"
+ """A sample app with a plugin mixed in"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
diff --git a/tests/test_pyscript.py b/tests/test_pyscript.py
index 73c1a62a..256e63a7 100644
--- a/tests/test_pyscript.py
+++ b/tests/test_pyscript.py
@@ -1,3 +1,4 @@
+# coding=utf-8
"""
Unit/functional testing for argparse completer in cmd2
@@ -8,24 +9,25 @@ import os
import pytest
from cmd2.cmd2 import Cmd, with_argparser
from cmd2 import argparse_completer
-from .conftest import run_cmd, StdOut
-from cmd2.utils import namedtuple_with_defaults
+from .conftest import run_cmd
+from cmd2.utils import namedtuple_with_defaults, StdSim
+
class PyscriptExample(Cmd):
ratings_types = ['G', 'PG', 'PG-13', 'R', 'NC-17']
def _do_media_movies(self, args) -> None:
if not args.command:
- self.do_help('media movies')
+ self.do_help(['media movies'])
else:
print('media movies ' + str(args.__dict__))
def _do_media_shows(self, args) -> None:
if not args.command:
- self.do_help('media shows')
+ self.do_help(['media shows'])
if not args.command:
- self.do_help('media shows')
+ self.do_help(['media shows'])
else:
print('media shows ' + str(args.__dict__))
@@ -70,7 +72,7 @@ class PyscriptExample(Cmd):
func(self, args)
else:
# No subcommand was provided, so call help
- self.do_help('media')
+ self.do_help(['media'])
foo_parser = argparse_completer.ACArgumentParser(prog='foo')
foo_parser.add_argument('-c', dest='counter', action='count')
@@ -114,8 +116,7 @@ class PyscriptExample(Cmd):
@pytest.fixture
def ps_app():
c = PyscriptExample()
- c.stdout = StdOut()
-
+ c.stdout = StdSim(c.stdout)
return c
@@ -131,8 +132,7 @@ class PyscriptCustomNameExample(Cmd):
@pytest.fixture
def ps_echo():
c = PyscriptCustomNameExample()
- c.stdout = StdOut()
-
+ c.stdout = StdSim(c.stdout)
return c
diff --git a/tests/test_transcript.py b/tests/test_transcript.py
index 3caf6a37..f854241b 100644
--- a/tests/test_transcript.py
+++ b/tests/test_transcript.py
@@ -16,8 +16,10 @@ from unittest import mock
import pytest
import cmd2
-from .conftest import run_cmd, StdOut
+from .conftest import run_cmd
from cmd2 import transcript
+from cmd2.utils import StdSim
+
class CmdLineApp(cmd2.Cmd):
@@ -83,9 +85,9 @@ def test_commands_at_invocation():
expected = "This is an intro banner ...\nhello\nGracie\n"
with mock.patch.object(sys, 'argv', testargs):
app = CmdLineApp()
- app.stdout = StdOut()
+ app.stdout = StdSim(app.stdout)
app.cmdloop()
- out = app.stdout.buffer
+ out = app.stdout.getvalue()
assert out == expected
@pytest.mark.parametrize('filename,feedback_to_output', [
@@ -129,7 +131,7 @@ def test_transcript(request, capsys, filename, feedback_to_output):
def test_history_transcript(request, capsys):
app = CmdLineApp()
- app.stdout = StdOut()
+ app.stdout = StdSim(app.stdout)
run_cmd(app, 'orate this is\na /multiline/\ncommand;\n')
run_cmd(app, 'speak /tmp/file.txt is not a regex')
@@ -150,13 +152,13 @@ this is a \/multiline\/ command
# read in the transcript created by the history command
with open(history_fname) as f:
- transcript = f.read()
+ xscript = f.read()
- assert transcript == expected
+ assert xscript == expected
def test_history_transcript_bad_filename(request, capsys):
app = CmdLineApp()
- app.stdout = StdOut()
+ app.stdout = StdSim(app.stdout)
run_cmd(app, 'orate this is\na /multiline/\ncommand;\n')
run_cmd(app, 'speak /tmp/file.txt is not a regex')
diff --git a/tests/test_utils.py b/tests/test_utils.py
index 8c8daa39..53031567 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -5,6 +5,10 @@ Unit testing for cmd2/utils.py module.
Copyright 2018 Todd Leonhardt <todd.leonhardt@gmail.com>
Released under MIT license, see LICENSE file
"""
+import sys
+
+import pytest
+
from colorama import Fore
import cmd2.utils as cu
@@ -110,4 +114,55 @@ def test_quot_string_if_needed_no():
assert cu.quote_string_if_needed(your_str) == your_str
+@pytest.fixture
+def stdout_sim():
+ stdsim = cu.StdSim(sys.stdout)
+ return stdsim
+
+def test_stdsim_write_str(stdout_sim):
+ my_str = 'Hello World'
+ stdout_sim.write(my_str)
+ assert stdout_sim.getvalue() == my_str
+
+def test_stdsim_write_bytes(stdout_sim):
+ b_str = b'Hello World'
+ with pytest.raises(TypeError):
+ stdout_sim.write(b_str)
+
+def test_stdsim_buffer_write_bytes(stdout_sim):
+ b_str = b'Hello World'
+ stdout_sim.buffer.write(b_str)
+ assert stdout_sim.getvalue() == b_str.decode()
+
+def test_stdsim_buffer_write_str(stdout_sim):
+ my_str = 'Hello World'
+ with pytest.raises(TypeError):
+ stdout_sim.buffer.write(my_str)
+
+def test_stdsim_read(stdout_sim):
+ my_str = 'Hello World'
+ stdout_sim.write(my_str)
+ # getvalue() returns the value and leaves it unaffected internally
+ assert stdout_sim.getvalue() == my_str
+ # read() returns the value and then clears the internal buffer
+ assert stdout_sim.read() == my_str
+ assert stdout_sim.getvalue() == ''
+
+def test_stdsim_clear(stdout_sim):
+ my_str = 'Hello World'
+ stdout_sim.write(my_str)
+ assert stdout_sim.getvalue() == my_str
+ stdout_sim.clear()
+ assert stdout_sim.getvalue() == ''
+
+def test_stdsim_getattr_exist(stdout_sim):
+ # Here the StdSim getattr is allowing us to access methods within StdSim
+ my_str = 'Hello World'
+ stdout_sim.write(my_str)
+ val_func = getattr(stdout_sim, 'getvalue')
+ assert val_func() == my_str
+
+def test_stdsim_getattr_noexist(stdout_sim):
+ # Here the StdSim getattr is allowing us to access methods defined by the inner stream
+ assert not stdout_sim.isatty()