summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/conftest.py14
-rw-r--r--tests/pyscript/recursive.py2
-rw-r--r--tests/test_ansi.py51
-rw-r--r--tests/test_argparse.py17
-rw-r--r--tests/test_argparse_completer.py797
-rw-r--r--tests/test_argparse_custom.py62
-rwxr-xr-xtests/test_cmd2.py351
-rwxr-xr-xtests/test_completion.py236
-rwxr-xr-xtests/test_history.py189
-rwxr-xr-xtests/test_parsing.py263
-rw-r--r--tests/test_plugin.py67
-rw-r--r--tests/test_run_pyscript.py16
-rw-r--r--tests/test_table_creator.py298
-rw-r--r--tests/test_transcript.py95
-rw-r--r--tests/test_utils.py83
-rw-r--r--tests/test_utils_defining_class.py4
16 files changed, 1593 insertions, 952 deletions
diff --git a/tests/conftest.py b/tests/conftest.py
index 73080b5c..1116539d 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -25,9 +25,9 @@ except ImportError:
pass
-def verify_help_text(cmd2_app: cmd2.Cmd,
- help_output: Union[str, List[str]],
- verbose_strings: Optional[List[str]] = None) -> None:
+def verify_help_text(
+ cmd2_app: cmd2.Cmd, help_output: Union[str, List[str]], verbose_strings: Optional[List[str]] = None
+) -> None:
"""This function verifies that all expected commands are present in the help text.
:param cmd2_app: instance of cmd2.Cmd
@@ -158,12 +158,7 @@ def base_app():
# These are odd file names for testing quoting of them
-odd_file_names = [
- 'nothingweird',
- 'has spaces',
- '"is_double_quoted"',
- "'is_single_quoted'"
-]
+odd_file_names = ['nothingweird', 'has spaces', '"is_double_quoted"', "'is_single_quoted'"]
def complete_tester(text: str, line: str, begidx: int, endidx: int, app) -> Optional[str]:
@@ -182,6 +177,7 @@ def complete_tester(text: str, line: str, begidx: int, endidx: int, app) -> Opti
Matches are stored in app.completion_matches
These matches also have been sorted by complete()
"""
+
def get_line():
return line
diff --git a/tests/pyscript/recursive.py b/tests/pyscript/recursive.py
index 7f02bb78..b88ba5a5 100644
--- a/tests/pyscript/recursive.py
+++ b/tests/pyscript/recursive.py
@@ -8,5 +8,5 @@ import os
import sys
app.cmd_echo = True
-my_dir = (os.path.dirname(os.path.realpath(sys.argv[0])))
+my_dir = os.path.dirname(os.path.realpath(sys.argv[0]))
app('run_pyscript {}'.format(os.path.join(my_dir, 'stop.py')))
diff --git a/tests/test_ansi.py b/tests/test_ansi.py
index 4a28b1a0..8051b248 100644
--- a/tests/test_ansi.py
+++ b/tests/test_ansi.py
@@ -65,11 +65,19 @@ def test_style_multi():
base_str = HELLO_WORLD
fg_color = 'blue'
bg_color = 'green'
- ansi_str = (ansi.fg[fg_color].value + ansi.bg[bg_color].value +
- ansi.INTENSITY_BRIGHT + ansi.INTENSITY_DIM + ansi.UNDERLINE_ENABLE +
- base_str +
- ansi.FG_RESET + ansi.BG_RESET +
- ansi.INTENSITY_NORMAL + ansi.INTENSITY_NORMAL + ansi.UNDERLINE_DISABLE)
+ ansi_str = (
+ ansi.fg[fg_color].value
+ + ansi.bg[bg_color].value
+ + ansi.INTENSITY_BRIGHT
+ + ansi.INTENSITY_DIM
+ + ansi.UNDERLINE_ENABLE
+ + base_str
+ + ansi.FG_RESET
+ + ansi.BG_RESET
+ + ansi.INTENSITY_NORMAL
+ + ansi.INTENSITY_NORMAL
+ + ansi.UNDERLINE_DISABLE
+ )
assert ansi.style(base_str, fg=fg_color, bg=bg_color, bold=True, dim=True, underline=True) == ansi_str
@@ -110,14 +118,23 @@ def test_set_title_str():
assert ansi.set_title_str(title) == OSC + '2;' + title + BEL
-@pytest.mark.parametrize('cols, prompt, line, cursor, msg, expected', [
- (127, '(Cmd) ', 'help his', 12, ansi.style('Hello World!', fg='magenta'), '\x1b[2K\r\x1b[35mHello World!\x1b[39m'),
- (127, '\n(Cmd) ', 'help ', 5, 'foo', '\x1b[2K\x1b[1A\x1b[2K\rfoo'),
- (10, '(Cmd) ', 'help history of the american republic', 4, 'boo', '\x1b[3B\x1b[2K\x1b[1A\x1b[2K\x1b[1A\x1b[2K\x1b[1A\x1b[2K\x1b[1A\x1b[2K\rboo')
-])
+@pytest.mark.parametrize(
+ 'cols, prompt, line, cursor, msg, expected',
+ [
+ (127, '(Cmd) ', 'help his', 12, ansi.style('Hello World!', fg='magenta'), '\x1b[2K\r\x1b[35mHello World!\x1b[39m'),
+ (127, '\n(Cmd) ', 'help ', 5, 'foo', '\x1b[2K\x1b[1A\x1b[2K\rfoo'),
+ (
+ 10,
+ '(Cmd) ',
+ 'help history of the american republic',
+ 4,
+ 'boo',
+ '\x1b[3B\x1b[2K\x1b[1A\x1b[2K\x1b[1A\x1b[2K\x1b[1A\x1b[2K\x1b[1A\x1b[2K\rboo',
+ ),
+ ],
+)
def test_async_alert_str(cols, prompt, line, cursor, msg, expected):
- alert_str = ansi.async_alert_str(terminal_columns=cols, prompt=prompt, line=line, cursor_offset=cursor,
- alert_msg=msg)
+ alert_str = ansi.async_alert_str(terminal_columns=cols, prompt=prompt, line=line, cursor_offset=cursor, alert_msg=msg)
assert alert_str == expected
@@ -127,19 +144,23 @@ def test_cast_color_as_str():
def test_color_str_building():
- from cmd2.ansi import fg, bg
+ from cmd2.ansi import bg, fg
+
assert fg.blue + "hello" == fg.blue.value + "hello"
assert bg.blue + "hello" == bg.blue.value + "hello"
assert fg.blue + "hello" + fg.reset == fg.blue.value + "hello" + fg.reset.value
assert bg.blue + "hello" + bg.reset == bg.blue.value + "hello" + bg.reset.value
- assert fg.blue + bg.white + "hello" + fg.reset + bg.reset == \
- fg.blue.value + bg.white.value + "hello" + fg.reset.value + bg.reset.value
+ assert (
+ fg.blue + bg.white + "hello" + fg.reset + bg.reset
+ == fg.blue.value + bg.white.value + "hello" + fg.reset.value + bg.reset.value
+ )
def test_color_nonunique_values():
class Matching(ansi.ColorBase):
magenta = ansi.fg_lookup('magenta')
purple = ansi.fg_lookup('magenta')
+
assert sorted(Matching.colors()) == ['magenta', 'purple']
diff --git a/tests/test_argparse.py b/tests/test_argparse.py
index 7059e9d3..e91b4dba 100644
--- a/tests/test_argparse.py
+++ b/tests/test_argparse.py
@@ -49,7 +49,7 @@ class ArgparseApp(cmd2.Cmd):
if word is None:
word = ''
if args.piglatin:
- word = '%s%say' % (word[1:], word[0])
+ word = '{}{}ay'.format(word[1:], word[0])
if args.shout:
word = word.upper()
words.append(word)
@@ -101,7 +101,7 @@ class ArgparseApp(cmd2.Cmd):
if word is None:
word = ''
if args.piglatin:
- word = '%s%say' % (word[1:], word[0])
+ word = '{}{}ay'.format(word[1:], word[0])
if args.shout:
word = word.upper()
words.append(word)
@@ -308,8 +308,9 @@ class SubcommandApp(cmd2.Cmd):
helpless_subcmd_parser = cmd2.Cmd2ArgumentParser(add_help=False, description="A subcommand with no help")
- @cmd2.as_subcommand_to('test_subcmd_decorator', 'helpless_subcmd', helpless_subcmd_parser,
- help=helpless_subcmd_parser.description.lower())
+ @cmd2.as_subcommand_to(
+ 'test_subcmd_decorator', 'helpless_subcmd', helpless_subcmd_parser, help=helpless_subcmd_parser.description.lower()
+ )
def helpless_subcmd_func(self, args: argparse.Namespace):
# Make sure vars(Namespace) works. The way we originally added cmd2_hander to it resulted in a RecursionError.
self.poutput(vars(args))
@@ -425,6 +426,7 @@ def test_subcmd_decorator(subcommand_app):
def test_unittest_mock():
from unittest import mock
+
from cmd2 import CommandSetRegistrationError
with mock.patch.object(ArgparseApp, 'namespace_provider'):
@@ -449,12 +451,7 @@ def test_pytest_mock_invalid(mocker):
app = ArgparseApp()
-@pytest.mark.parametrize('spec_param', [
- {'spec': True},
- {'spec_set': True},
- {'autospec': True},
-])
+@pytest.mark.parametrize('spec_param', [{'spec': True}, {'spec_set': True}, {'autospec': True},])
def test_pytest_mock_valid(mocker, spec_param):
mocker.patch.object(ArgparseApp, 'namespace_provider', **spec_param)
app = ArgparseApp()
-
diff --git a/tests/test_argparse_completer.py b/tests/test_argparse_completer.py
index dd86163b..3898de85 100644
--- a/tests/test_argparse_completer.py
+++ b/tests/test_argparse_completer.py
@@ -12,6 +12,7 @@ import pytest
import cmd2
from cmd2 import Cmd2ArgumentParser, CompletionItem, with_argparser
from cmd2.utils import CompletionError, StdSim, basic_complete
+
from .conftest import complete_tester, run_cmd
# Lists used in our tests (there is a mix of sorted and unsorted on purpose)
@@ -48,8 +49,7 @@ def choices_takes_arg_tokens(arg_tokens: argparse.Namespace) -> List[str]:
return [arg_tokens['parent_arg'][0], arg_tokens['subcommand'][0]]
-def completer_takes_arg_tokens(text: str, line: str, begidx: int, endidx: int,
- arg_tokens: argparse.Namespace) -> List[str]:
+def completer_takes_arg_tokens(text: str, line: str, begidx: int, endidx: int, arg_tokens: argparse.Namespace) -> List[str]:
"""Completer function that receives arg_tokens from ArgparseCompleter"""
match_against = [arg_tokens['parent_arg'][0], arg_tokens['subcommand'][0]]
return basic_complete(text, line, begidx, endidx, match_against)
@@ -58,6 +58,7 @@ def completer_takes_arg_tokens(text: str, line: str, begidx: int, endidx: int,
# noinspection PyMethodMayBeStatic,PyUnusedLocal,PyProtectedMember
class ArgparseCompleterTester(cmd2.Cmd):
"""Cmd2 app that exercises ArgparseCompleter class"""
+
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@@ -129,34 +130,47 @@ class ArgparseCompleterTester(cmd2.Cmd):
choices_parser = Cmd2ArgumentParser()
# Flag args for choices command. Include string and non-string arg types.
- choices_parser.add_argument("-l", "--list", help="a flag populated with a choices list",
- choices=static_choices_list)
- choices_parser.add_argument("-f", "--function", help="a flag populated with a choices function",
- choices_function=choices_function)
- choices_parser.add_argument("-m", "--method", help="a flag populated with a choices method",
- choices_method=choices_method)
- choices_parser.add_argument('-d', "--desc_header", help='this arg has a descriptive header',
- choices_method=completion_item_method,
- descriptive_header=CUSTOM_DESC_HEADER)
- choices_parser.add_argument('-n', "--no_header", help='this arg has no descriptive header',
- choices_method=completion_item_method, metavar=STR_METAVAR)
- choices_parser.add_argument('-t', "--tuple_metavar", help='this arg has tuple for a metavar',
- choices_method=completion_item_method, metavar=TUPLE_METAVAR,
- nargs=argparse.ONE_OR_MORE)
- choices_parser.add_argument('-i', '--int', type=int, help='a flag with an int type',
- choices=int_choices)
+ choices_parser.add_argument("-l", "--list", help="a flag populated with a choices list", choices=static_choices_list)
+ choices_parser.add_argument(
+ "-f", "--function", help="a flag populated with a choices function", choices_function=choices_function
+ )
+ choices_parser.add_argument("-m", "--method", help="a flag populated with a choices method", choices_method=choices_method)
+ choices_parser.add_argument(
+ '-d',
+ "--desc_header",
+ help='this arg has a descriptive header',
+ choices_method=completion_item_method,
+ descriptive_header=CUSTOM_DESC_HEADER,
+ )
+ choices_parser.add_argument(
+ '-n',
+ "--no_header",
+ help='this arg has no descriptive header',
+ choices_method=completion_item_method,
+ metavar=STR_METAVAR,
+ )
+ choices_parser.add_argument(
+ '-t',
+ "--tuple_metavar",
+ help='this arg has tuple for a metavar',
+ choices_method=completion_item_method,
+ metavar=TUPLE_METAVAR,
+ nargs=argparse.ONE_OR_MORE,
+ )
+ choices_parser.add_argument('-i', '--int', type=int, help='a flag with an int type', choices=int_choices)
# Positional args for choices command
- choices_parser.add_argument("list_pos", help="a positional populated with a choices list",
- choices=static_choices_list)
- choices_parser.add_argument("function_pos", help="a positional populated with a choices function",
- choices_function=choices_function)
- choices_parser.add_argument("method_pos", help="a positional populated with a choices method",
- choices_method=choices_method)
- choices_parser.add_argument('non_negative_int', type=int, help='a positional with non-negative int choices',
- choices=non_negative_int_choices)
- choices_parser.add_argument('empty_choices', help='a positional with empty choices',
- choices=[])
+ choices_parser.add_argument("list_pos", help="a positional populated with a choices list", choices=static_choices_list)
+ choices_parser.add_argument(
+ "function_pos", help="a positional populated with a choices function", choices_function=choices_function
+ )
+ choices_parser.add_argument(
+ "method_pos", help="a positional populated with a choices method", choices_method=choices_method
+ )
+ choices_parser.add_argument(
+ 'non_negative_int', type=int, help='a positional with non-negative int choices', choices=non_negative_int_choices
+ )
+ choices_parser.add_argument('empty_choices', help='a positional with empty choices', choices=[])
@with_argparser(choices_parser)
def do_choices(self, args: argparse.Namespace) -> None:
@@ -172,16 +186,18 @@ class ArgparseCompleterTester(cmd2.Cmd):
completer_parser = Cmd2ArgumentParser()
# Flag args for completer command
- completer_parser.add_argument("-f", "--function", help="a flag using a completer function",
- completer_function=completer_function)
- completer_parser.add_argument("-m", "--method", help="a flag using a completer method",
- completer_method=completer_method)
+ completer_parser.add_argument(
+ "-f", "--function", help="a flag using a completer function", completer_function=completer_function
+ )
+ completer_parser.add_argument("-m", "--method", help="a flag using a completer method", completer_method=completer_method)
# Positional args for completer command
- completer_parser.add_argument("function_pos", help="a positional using a completer function",
- completer_function=completer_function)
- completer_parser.add_argument("method_pos", help="a positional using a completer method",
- completer_method=completer_method)
+ completer_parser.add_argument(
+ "function_pos", help="a positional using a completer function", completer_function=completer_function
+ )
+ completer_parser.add_argument(
+ "method_pos", help="a positional using a completer method", completer_method=completer_method
+ )
@with_argparser(completer_parser)
def do_completer(self, args: argparse.Namespace) -> None:
@@ -193,22 +209,23 @@ class ArgparseCompleterTester(cmd2.Cmd):
nargs_parser = Cmd2ArgumentParser()
# Flag args for nargs command
- nargs_parser.add_argument("--set_value", help="a flag with a set value for nargs", nargs=2,
- choices=set_value_choices)
- nargs_parser.add_argument("--one_or_more", help="a flag wanting one or more args", nargs=argparse.ONE_OR_MORE,
- choices=one_or_more_choices)
- nargs_parser.add_argument("--optional", help="a flag with an optional value", nargs=argparse.OPTIONAL,
- choices=optional_choices)
+ nargs_parser.add_argument("--set_value", help="a flag with a set value for nargs", nargs=2, choices=set_value_choices)
+ nargs_parser.add_argument(
+ "--one_or_more", help="a flag wanting one or more args", nargs=argparse.ONE_OR_MORE, choices=one_or_more_choices
+ )
+ nargs_parser.add_argument(
+ "--optional", help="a flag with an optional value", nargs=argparse.OPTIONAL, choices=optional_choices
+ )
# noinspection PyTypeChecker
- nargs_parser.add_argument("--range", help="a flag with nargs range", nargs=(1, 2),
- choices=range_choices)
- nargs_parser.add_argument("--remainder", help="a flag wanting remaining", nargs=argparse.REMAINDER,
- choices=remainder_choices)
+ nargs_parser.add_argument("--range", help="a flag with nargs range", nargs=(1, 2), choices=range_choices)
+ nargs_parser.add_argument(
+ "--remainder", help="a flag wanting remaining", nargs=argparse.REMAINDER, choices=remainder_choices
+ )
- nargs_parser.add_argument("normal_pos", help="a remainder positional", nargs=2,
- choices=positional_choices)
- nargs_parser.add_argument("remainder_pos", help="a remainder positional", nargs=argparse.REMAINDER,
- choices=remainder_choices)
+ nargs_parser.add_argument("normal_pos", help="a remainder positional", nargs=2, choices=positional_choices)
+ nargs_parser.add_argument(
+ "remainder_pos", help="a remainder positional", nargs=argparse.REMAINDER, choices=remainder_choices
+ )
@with_argparser(nargs_parser)
def do_nargs(self, args: argparse.Namespace) -> None:
@@ -241,10 +258,8 @@ class ArgparseCompleterTester(cmd2.Cmd):
raise CompletionError('choice broke something')
comp_error_parser = Cmd2ArgumentParser()
- comp_error_parser.add_argument('completer', help='positional arg',
- completer_method=completer_raise_error)
- comp_error_parser.add_argument('--choice', help='flag arg',
- choices_method=choice_raise_error)
+ comp_error_parser.add_argument('completer', help='positional arg', completer_method=completer_raise_error)
+ comp_error_parser.add_argument('--choice', help='flag arg', choices_method=choice_raise_error)
@with_argparser(comp_error_parser)
def do_raise_completion_error(self, args: argparse.Namespace) -> None:
@@ -294,12 +309,7 @@ def ac_app():
return app
-@pytest.mark.parametrize('command', [
- 'music',
- 'music create',
- 'music create rock',
- 'music create jazz'
-])
+@pytest.mark.parametrize('command', ['music', 'music create', 'music create rock', 'music create jazz'])
def test_help(ac_app, command):
out1, err1 = run_cmd(ac_app, '{} -h'.format(command))
out2, err2 = run_cmd(ac_app, 'help {}'.format(command))
@@ -314,16 +324,19 @@ def test_bad_subcommand_help(ac_app):
assert out1 == out2
-@pytest.mark.parametrize('command, text, completions', [
- ('', 'mus', ['music ']),
- ('music', 'cre', ['create ']),
- ('music', 'creab', []),
- ('music create', '', ['jazz', 'rock']),
- ('music crea', 'jazz', []),
- ('music create', 'foo', []),
- ('fake create', '', []),
- ('music fake', '', [])
-])
+@pytest.mark.parametrize(
+ 'command, text, completions',
+ [
+ ('', 'mus', ['music ']),
+ ('music', 'cre', ['create ']),
+ ('music', 'creab', []),
+ ('music create', '', ['jazz', 'rock']),
+ ('music crea', 'jazz', []),
+ ('music create', 'foo', []),
+ ('fake create', '', []),
+ ('music fake', '', []),
+ ],
+)
def test_complete_help(ac_app, command, text, completions):
line = 'help {} {}'.format(command, text)
endidx = len(line)
@@ -338,12 +351,10 @@ def test_complete_help(ac_app, command, text, completions):
assert ac_app.completion_matches == sorted(completions, key=ac_app.default_sort_key)
-@pytest.mark.parametrize('subcommand, text, completions', [
- ('create', '', ['jazz', 'rock']),
- ('create', 'ja', ['jazz ']),
- ('create', 'foo', []),
- ('creab', 'ja', [])
-])
+@pytest.mark.parametrize(
+ 'subcommand, text, completions',
+ [('create', '', ['jazz', 'rock']), ('create', 'ja', ['jazz ']), ('create', 'foo', []), ('creab', 'ja', [])],
+)
def test_subcommand_completions(ac_app, subcommand, text, completions):
line = 'music {} {}'.format(subcommand, text)
endidx = len(line)
@@ -358,64 +369,132 @@ def test_subcommand_completions(ac_app, subcommand, text, completions):
assert ac_app.completion_matches == sorted(completions, key=ac_app.default_sort_key)
-@pytest.mark.parametrize('command_and_args, text, completion_matches, display_matches', [
- # Complete all flags (suppressed will not show)
- ('flag', '-',
- ['--append_const_flag', '--append_flag', '--count_flag', '--help', '--normal_flag',
- '--remainder_flag', '--required_flag', '-a', '-c', '-h', '-n', '-o', '-q', '-r'],
- ['-q, --required_flag', '[-o, --append_const_flag]', '[-a, --append_flag]', '[-c, --count_flag]', '[-h, --help]',
- '[-n, --normal_flag]', '[-r, --remainder_flag]']),
- ('flag', '--',
- ['--append_const_flag', '--append_flag', '--count_flag', '--help',
- '--normal_flag', '--remainder_flag', '--required_flag'],
- ['--required_flag', '[--append_const_flag]', '[--append_flag]', '[--count_flag]', '[--help]',
- '[--normal_flag]', '[--remainder_flag]']),
-
- # Complete individual flag
- ('flag', '-n', ['-n '], ['[-n]']),
- ('flag', '--n', ['--normal_flag '], ['[--normal_flag]']),
-
- # No flags should complete until current flag has its args
- ('flag --append_flag', '-', [], []),
-
- # Complete REMAINDER flag name
- ('flag', '-r', ['-r '], ['[-r]']),
- ('flag', '--rem', ['--remainder_flag '], ['[--remainder_flag]']),
-
- # No flags after a REMAINDER should complete
- ('flag -r value', '-', [], []),
- ('flag --remainder_flag value', '--', [], []),
-
- # Suppressed flag should not complete
- ('flag', '-s', [], []),
- ('flag', '--s', [], []),
-
- # A used flag should not show in completions
- ('flag -n', '--',
- ['--append_const_flag', '--append_flag', '--count_flag', '--help', '--remainder_flag', '--required_flag'],
- ['--required_flag', '[--append_const_flag]', '[--append_flag]', '[--count_flag]', '[--help]', '[--remainder_flag]']),
-
- # Flags with actions set to append, append_const, and count will always show even if they've been used
- ('flag --append_const_flag -c --append_flag value', '--',
- ['--append_const_flag', '--append_flag', '--count_flag', '--help',
- '--normal_flag', '--remainder_flag', '--required_flag'],
- ['--required_flag', '[--append_const_flag]', '[--append_flag]', '[--count_flag]', '[--help]',
- '[--normal_flag]', '[--remainder_flag]']),
-
- # Non-default flag prefix character (+)
- ('plus_flag', '+',
- ['++help', '++normal_flag', '+h', '+n', '+q', '++required_flag'],
- ['+q, ++required_flag', '[+h, ++help]', '[+n, ++normal_flag]']),
- ('plus_flag', '++',
- ['++help', '++normal_flag', '++required_flag'],
- ['++required_flag', '[++help]', '[++normal_flag]']),
-
- # Flag completion should not occur after '--' since that tells argparse all remaining arguments are non-flags
- ('flag --', '--', [], []),
- ('flag --help --', '--', [], []),
- ('plus_flag --', '++', [], []),
- ('plus_flag ++help --', '++', [], [])
-])
+@pytest.mark.parametrize(
+ 'command_and_args, text, completion_matches, display_matches',
+ [
+ # Complete all flags (suppressed will not show)
+ (
+ 'flag',
+ '-',
+ [
+ '--append_const_flag',
+ '--append_flag',
+ '--count_flag',
+ '--help',
+ '--normal_flag',
+ '--remainder_flag',
+ '--required_flag',
+ '-a',
+ '-c',
+ '-h',
+ '-n',
+ '-o',
+ '-q',
+ '-r',
+ ],
+ [
+ '-q, --required_flag',
+ '[-o, --append_const_flag]',
+ '[-a, --append_flag]',
+ '[-c, --count_flag]',
+ '[-h, --help]',
+ '[-n, --normal_flag]',
+ '[-r, --remainder_flag]',
+ ],
+ ),
+ (
+ 'flag',
+ '--',
+ [
+ '--append_const_flag',
+ '--append_flag',
+ '--count_flag',
+ '--help',
+ '--normal_flag',
+ '--remainder_flag',
+ '--required_flag',
+ ],
+ [
+ '--required_flag',
+ '[--append_const_flag]',
+ '[--append_flag]',
+ '[--count_flag]',
+ '[--help]',
+ '[--normal_flag]',
+ '[--remainder_flag]',
+ ],
+ ),
+ # Complete individual flag
+ ('flag', '-n', ['-n '], ['[-n]']),
+ ('flag', '--n', ['--normal_flag '], ['[--normal_flag]']),
+ # No flags should complete until current flag has its args
+ ('flag --append_flag', '-', [], []),
+ # Complete REMAINDER flag name
+ ('flag', '-r', ['-r '], ['[-r]']),
+ ('flag', '--rem', ['--remainder_flag '], ['[--remainder_flag]']),
+ # No flags after a REMAINDER should complete
+ ('flag -r value', '-', [], []),
+ ('flag --remainder_flag value', '--', [], []),
+ # Suppressed flag should not complete
+ ('flag', '-s', [], []),
+ ('flag', '--s', [], []),
+ # A used flag should not show in completions
+ (
+ 'flag -n',
+ '--',
+ ['--append_const_flag', '--append_flag', '--count_flag', '--help', '--remainder_flag', '--required_flag'],
+ [
+ '--required_flag',
+ '[--append_const_flag]',
+ '[--append_flag]',
+ '[--count_flag]',
+ '[--help]',
+ '[--remainder_flag]',
+ ],
+ ),
+ # Flags with actions set to append, append_const, and count will always show even if they've been used
+ (
+ 'flag --append_const_flag -c --append_flag value',
+ '--',
+ [
+ '--append_const_flag',
+ '--append_flag',
+ '--count_flag',
+ '--help',
+ '--normal_flag',
+ '--remainder_flag',
+ '--required_flag',
+ ],
+ [
+ '--required_flag',
+ '[--append_const_flag]',
+ '[--append_flag]',
+ '[--count_flag]',
+ '[--help]',
+ '[--normal_flag]',
+ '[--remainder_flag]',
+ ],
+ ),
+ # Non-default flag prefix character (+)
+ (
+ 'plus_flag',
+ '+',
+ ['++help', '++normal_flag', '+h', '+n', '+q', '++required_flag'],
+ ['+q, ++required_flag', '[+h, ++help]', '[+n, ++normal_flag]'],
+ ),
+ (
+ 'plus_flag',
+ '++',
+ ['++help', '++normal_flag', '++required_flag'],
+ ['++required_flag', '[++help]', '[++normal_flag]'],
+ ),
+ # Flag completion should not occur after '--' since that tells argparse all remaining arguments are non-flags
+ ('flag --', '--', [], []),
+ ('flag --help --', '--', [], []),
+ ('plus_flag --', '++', [], []),
+ ('plus_flag ++help --', '++', [], []),
+ ],
+)
def test_autcomp_flag_completion(ac_app, command_and_args, text, completion_matches, display_matches):
line = '{} {}'.format(command_and_args, text)
endidx = len(line)
@@ -427,22 +506,26 @@ def test_autcomp_flag_completion(ac_app, command_and_args, text, completion_matc
else:
assert first_match is None
- assert (ac_app.completion_matches == sorted(completion_matches, key=ac_app.default_sort_key) and
- ac_app.display_matches == sorted(display_matches, key=ac_app.default_sort_key))
-
-
-@pytest.mark.parametrize('flag, text, completions', [
- ('-l', '', static_choices_list),
- ('--list', 's', ['static', 'stop']),
- ('-f', '', choices_from_function),
- ('--function', 'ch', ['choices', 'chatty']),
- ('-m', '', choices_from_method),
- ('--method', 'm', ['method', 'most']),
- ('-i', '', int_choices),
- ('--int', '1', ['1 ']),
- ('--int', '-', [-1, -2, -12]),
- ('--int', '-1', [-1, -12])
-])
+ assert ac_app.completion_matches == sorted(
+ completion_matches, key=ac_app.default_sort_key
+ ) and ac_app.display_matches == sorted(display_matches, key=ac_app.default_sort_key)
+
+
+@pytest.mark.parametrize(
+ 'flag, text, completions',
+ [
+ ('-l', '', static_choices_list),
+ ('--list', 's', ['static', 'stop']),
+ ('-f', '', choices_from_function),
+ ('--function', 'ch', ['choices', 'chatty']),
+ ('-m', '', choices_from_method),
+ ('--method', 'm', ['method', 'most']),
+ ('-i', '', int_choices),
+ ('--int', '1', ['1 ']),
+ ('--int', '-', [-1, -2, -12]),
+ ('--int', '-1', [-1, -12]),
+ ],
+)
def test_autocomp_flag_choices_completion(ac_app, flag, text, completions):
line = 'choices {} {}'.format(flag, text)
endidx = len(line)
@@ -464,17 +547,20 @@ def test_autocomp_flag_choices_completion(ac_app, flag, text, completions):
assert ac_app.completion_matches == completions
-@pytest.mark.parametrize('pos, text, completions', [
- (1, '', static_choices_list),
- (1, 's', ['static', 'stop']),
- (2, '', choices_from_function),
- (2, 'ch', ['choices', 'chatty']),
- (3, '', choices_from_method),
- (3, 'm', ['method', 'most']),
- (4, '', non_negative_int_choices),
- (4, '2', [2, 22]),
- (5, '', []),
-])
+@pytest.mark.parametrize(
+ 'pos, text, completions',
+ [
+ (1, '', static_choices_list),
+ (1, 's', ['static', 'stop']),
+ (2, '', choices_from_function),
+ (2, 'ch', ['choices', 'chatty']),
+ (3, '', choices_from_method),
+ (3, 'm', ['method', 'most']),
+ (4, '', non_negative_int_choices),
+ (4, '2', [2, 22]),
+ (5, '', []),
+ ],
+)
def test_autocomp_positional_choices_completion(ac_app, pos, text, completions):
# Generate line were preceding positionals are already filled
line = 'choices {} {}'.format('foo ' * (pos - 1), text)
@@ -518,12 +604,15 @@ def test_flag_sorting(ac_app):
assert first_match is not None and ac_app.completion_matches == option_strings
-@pytest.mark.parametrize('flag, text, completions', [
- ('-f', '', completions_from_function),
- ('--function', 'f', ['function', 'fairly']),
- ('-m', '', completions_from_method),
- ('--method', 'm', ['method', 'missed'])
-])
+@pytest.mark.parametrize(
+ 'flag, text, completions',
+ [
+ ('-f', '', completions_from_function),
+ ('--function', 'f', ['function', 'fairly']),
+ ('-m', '', completions_from_method),
+ ('--method', 'm', ['method', 'missed']),
+ ],
+)
def test_autocomp_flag_completers(ac_app, flag, text, completions):
line = 'completer {} {}'.format(flag, text)
endidx = len(line)
@@ -538,12 +627,15 @@ def test_autocomp_flag_completers(ac_app, flag, text, completions):
assert ac_app.completion_matches == sorted(completions, key=ac_app.default_sort_key)
-@pytest.mark.parametrize('pos, text, completions', [
- (1, '', completions_from_function),
- (1, 'c', ['completions', 'complete']),
- (2, '', completions_from_method),
- (2, 'm', ['method', 'missed'])
-])
+@pytest.mark.parametrize(
+ 'pos, text, completions',
+ [
+ (1, '', completions_from_function),
+ (1, 'c', ['completions', 'complete']),
+ (2, '', completions_from_method),
+ (2, 'm', ['method', 'missed']),
+ ],
+)
def test_autocomp_positional_completers(ac_app, pos, text, completions):
# Generate line were preceding positionals are already filled
line = 'completer {} {}'.format('foo ' * (pos - 1), text)
@@ -588,14 +680,17 @@ def test_autocomp_blank_token(ac_app):
assert completions == completions_from_method
-@pytest.mark.parametrize('num_aliases, show_description', [
- # The number of completion results determines if the description field of CompletionItems gets displayed
- # in the tab completions. The count must be greater than 1 and less than ac_app.max_completion_items,
- # which defaults to 50.
- (1, False),
- (5, True),
- (100, False)
-])
+@pytest.mark.parametrize(
+ 'num_aliases, show_description',
+ [
+ # The number of completion results determines if the description field of CompletionItems gets displayed
+ # in the tab completions. The count must be greater than 1 and less than ac_app.max_completion_items,
+ # which defaults to 50.
+ (1, False),
+ (5, True),
+ (100, False),
+ ],
+)
def test_completion_items(ac_app, num_aliases, show_description):
# Create aliases
for i in range(0, num_aliases):
@@ -617,71 +712,57 @@ def test_completion_items(ac_app, num_aliases, show_description):
assert ('help' in ac_app.display_matches[0]) == show_description
-@pytest.mark.parametrize('args, completions', [
- # Flag with nargs = 2
- ('--set_value', set_value_choices),
- ('--set_value set', ['value', 'choices']),
-
- # Both args are filled. At positional arg now.
- ('--set_value set value', positional_choices),
-
- # Using the flag again will reset the choices available
- ('--set_value set value --set_value', set_value_choices),
-
- # Flag with nargs = ONE_OR_MORE
- ('--one_or_more', one_or_more_choices),
- ('--one_or_more one', ['or', 'more', 'choices']),
-
- # Flag with nargs = OPTIONAL
- ('--optional', optional_choices),
-
- # Only one arg allowed for an OPTIONAL. At positional now.
- ('--optional optional', positional_choices),
-
- # Flag with nargs range (1, 2)
- ('--range', range_choices),
- ('--range some', ['range', 'choices']),
-
- # Already used 2 args so at positional
- ('--range some range', positional_choices),
-
- # Flag with nargs = REMAINDER
- ('--remainder', remainder_choices),
- ('--remainder remainder ', ['choices ']),
-
- # No more flags can appear after a REMAINDER flag)
- ('--remainder choices --set_value', ['remainder ']),
-
- # Double dash ends the current flag
- ('--range choice --', positional_choices),
-
- # Double dash ends a REMAINDER flag
- ('--remainder remainder --', positional_choices),
-
- # No more flags after a double dash
- ('-- --one_or_more ', positional_choices),
-
- # Consume positional
- ('', positional_choices),
- ('positional', ['the', 'choices']),
-
- # Intermixed flag and positional
- ('positional --set_value', set_value_choices),
- ('positional --set_value set', ['choices', 'value']),
-
- # Intermixed flag and positional with flag finishing
- ('positional --set_value set value', ['the', 'choices']),
- ('positional --range choice --', ['the', 'choices']),
-
- # REMAINDER positional
- ('the positional', remainder_choices),
- ('the positional remainder', ['choices ']),
- ('the positional remainder choices', []),
-
- # REMAINDER positional. Flags don't work in REMAINDER
- ('the positional --set_value', remainder_choices),
- ('the positional remainder --set_value', ['choices '])
-])
+@pytest.mark.parametrize(
+ 'args, completions',
+ [
+ # Flag with nargs = 2
+ ('--set_value', set_value_choices),
+ ('--set_value set', ['value', 'choices']),
+ # Both args are filled. At positional arg now.
+ ('--set_value set value', positional_choices),
+ # Using the flag again will reset the choices available
+ ('--set_value set value --set_value', set_value_choices),
+ # Flag with nargs = ONE_OR_MORE
+ ('--one_or_more', one_or_more_choices),
+ ('--one_or_more one', ['or', 'more', 'choices']),
+ # Flag with nargs = OPTIONAL
+ ('--optional', optional_choices),
+ # Only one arg allowed for an OPTIONAL. At positional now.
+ ('--optional optional', positional_choices),
+ # Flag with nargs range (1, 2)
+ ('--range', range_choices),
+ ('--range some', ['range', 'choices']),
+ # Already used 2 args so at positional
+ ('--range some range', positional_choices),
+ # Flag with nargs = REMAINDER
+ ('--remainder', remainder_choices),
+ ('--remainder remainder ', ['choices ']),
+ # No more flags can appear after a REMAINDER flag)
+ ('--remainder choices --set_value', ['remainder ']),
+ # Double dash ends the current flag
+ ('--range choice --', positional_choices),
+ # Double dash ends a REMAINDER flag
+ ('--remainder remainder --', positional_choices),
+ # No more flags after a double dash
+ ('-- --one_or_more ', positional_choices),
+ # Consume positional
+ ('', positional_choices),
+ ('positional', ['the', 'choices']),
+ # Intermixed flag and positional
+ ('positional --set_value', set_value_choices),
+ ('positional --set_value set', ['choices', 'value']),
+ # Intermixed flag and positional with flag finishing
+ ('positional --set_value set value', ['the', 'choices']),
+ ('positional --range choice --', ['the', 'choices']),
+ # REMAINDER positional
+ ('the positional', remainder_choices),
+ ('the positional remainder', ['choices ']),
+ ('the positional remainder choices', []),
+ # REMAINDER positional. Flags don't work in REMAINDER
+ ('the positional --set_value', remainder_choices),
+ ('the positional remainder --set_value', ['choices ']),
+ ],
+)
def test_autcomp_nargs(ac_app, args, completions):
text = ''
line = 'nargs {} {}'.format(args, text)
@@ -697,43 +778,39 @@ def test_autcomp_nargs(ac_app, args, completions):
assert ac_app.completion_matches == sorted(completions, key=ac_app.default_sort_key)
-@pytest.mark.parametrize('command_and_args, text, is_error', [
- # Flag is finished before moving on
- ('hint --flag foo --', '', False),
- ('hint --flag foo --help', '', False),
- ('hint --flag foo', '--', False),
-
- ('nargs --one_or_more one --', '', False),
- ('nargs --one_or_more one or --set_value', '', False),
- ('nargs --one_or_more one or more', '--', False),
-
- ('nargs --set_value set value --', '', False),
- ('nargs --set_value set value --one_or_more', '', False),
- ('nargs --set_value set value', '--', False),
- ('nargs --set_val set value', '--', False), # This exercises our abbreviated flag detection
-
- ('nargs --range choices --', '', False),
- ('nargs --range choices range --set_value', '', False),
- ('nargs --range range', '--', False),
-
- # Flag is not finished before moving on
- ('hint --flag --', '', True),
- ('hint --flag --help', '', True),
- ('hint --flag', '--', True),
-
- ('nargs --one_or_more --', '', True),
- ('nargs --one_or_more --set_value', '', True),
- ('nargs --one_or_more', '--', True),
-
- ('nargs --set_value set --', '', True),
- ('nargs --set_value set --one_or_more', '', True),
- ('nargs --set_value set', '--', True),
- ('nargs --set_val set', '--', True), # This exercises our abbreviated flag detection
-
- ('nargs --range --', '', True),
- ('nargs --range --set_value', '', True),
- ('nargs --range', '--', True),
-])
+@pytest.mark.parametrize(
+ 'command_and_args, text, is_error',
+ [
+ # Flag is finished before moving on
+ ('hint --flag foo --', '', False),
+ ('hint --flag foo --help', '', False),
+ ('hint --flag foo', '--', False),
+ ('nargs --one_or_more one --', '', False),
+ ('nargs --one_or_more one or --set_value', '', False),
+ ('nargs --one_or_more one or more', '--', False),
+ ('nargs --set_value set value --', '', False),
+ ('nargs --set_value set value --one_or_more', '', False),
+ ('nargs --set_value set value', '--', False),
+ ('nargs --set_val set value', '--', False), # This exercises our abbreviated flag detection
+ ('nargs --range choices --', '', False),
+ ('nargs --range choices range --set_value', '', False),
+ ('nargs --range range', '--', False),
+ # Flag is not finished before moving on
+ ('hint --flag --', '', True),
+ ('hint --flag --help', '', True),
+ ('hint --flag', '--', True),
+ ('nargs --one_or_more --', '', True),
+ ('nargs --one_or_more --set_value', '', True),
+ ('nargs --one_or_more', '--', True),
+ ('nargs --set_value set --', '', True),
+ ('nargs --set_value set --one_or_more', '', True),
+ ('nargs --set_value set', '--', True),
+ ('nargs --set_val set', '--', True), # This exercises our abbreviated flag detection
+ ('nargs --range --', '', True),
+ ('nargs --range --set_value', '', True),
+ ('nargs --range', '--', True),
+ ],
+)
def test_unfinished_flag_error(ac_app, command_and_args, text, is_error, capsys):
line = '{} {}'.format(command_and_args, text)
endidx = len(line)
@@ -816,35 +893,32 @@ def test_completion_items_descriptive_header(ac_app):
assert DEFAULT_DESCRIPTIVE_HEADER in ac_app.completion_header
-@pytest.mark.parametrize('command_and_args, text, has_hint', [
- # Normal cases
- ('hint', '', True),
- ('hint --flag', '', True),
- ('hint --suppressed_help', '', False),
- ('hint --suppressed_hint', '', False),
-
- # Hint because flag does not have enough values to be considered finished
- ('nargs --one_or_more', '-', True),
-
- # This flag has reached its minimum value count and therefore a new flag could start.
- # However the flag can still consume values and the text is not a single prefix character.
- # Therefor a hint will be shown.
- ('nargs --one_or_more choices', 'bad_completion', True),
-
- # Like the previous case, but this time text is a single prefix character which will cause flag
- # name completion to occur instead of a hint for the current flag.
- ('nargs --one_or_more choices', '-', False),
-
- # Hint because this is a REMAINDER flag and therefore no more flag name completions occur.
- ('nargs --remainder', '-', True),
-
- # No hint for the positional because text is a single prefix character which results in flag name completion
- ('hint', '-', False),
-
- # Hint because this is a REMAINDER positional and therefore no more flag name completions occur.
- ('nargs the choices', '-', True),
- ('nargs the choices remainder', '-', True),
-])
+@pytest.mark.parametrize(
+ 'command_and_args, text, has_hint',
+ [
+ # Normal cases
+ ('hint', '', True),
+ ('hint --flag', '', True),
+ ('hint --suppressed_help', '', False),
+ ('hint --suppressed_hint', '', False),
+ # Hint because flag does not have enough values to be considered finished
+ ('nargs --one_or_more', '-', True),
+ # This flag has reached its minimum value count and therefore a new flag could start.
+ # However the flag can still consume values and the text is not a single prefix character.
+ # Therefor a hint will be shown.
+ ('nargs --one_or_more choices', 'bad_completion', True),
+ # Like the previous case, but this time text is a single prefix character which will cause flag
+ # name completion to occur instead of a hint for the current flag.
+ ('nargs --one_or_more choices', '-', False),
+ # Hint because this is a REMAINDER flag and therefore no more flag name completions occur.
+ ('nargs --remainder', '-', True),
+ # No hint for the positional because text is a single prefix character which results in flag name completion
+ ('hint', '-', False),
+ # Hint because this is a REMAINDER positional and therefore no more flag name completions occur.
+ ('nargs the choices', '-', True),
+ ('nargs the choices remainder', '-', True),
+ ],
+)
def test_autocomp_hint(ac_app, command_and_args, text, has_hint, capsys):
line = '{} {}'.format(command_and_args, text)
endidx = len(line)
@@ -868,20 +942,25 @@ def test_autocomp_hint_no_help_text(ac_app, capsys):
out, err = capsys.readouterr()
assert first_match is None
- assert not out == '''
+ assert (
+ not out
+ == '''
Hint:
NO_HELP_POS
'''
-
-
-@pytest.mark.parametrize('args, text', [
- # Exercise a flag arg and choices function that raises a CompletionError
- ('--choice ', 'choice'),
-
- # Exercise a positional arg and completer that raises a CompletionError
- ('', 'completer')
-])
+ )
+
+
+@pytest.mark.parametrize(
+ 'args, text',
+ [
+ # Exercise a flag arg and choices function that raises a CompletionError
+ ('--choice ', 'choice'),
+ # Exercise a positional arg and completer that raises a CompletionError
+ ('', 'completer'),
+ ],
+)
def test_completion_error(ac_app, capsys, args, text):
line = 'raise_completion_error {} {}'.format(args, text)
endidx = len(line)
@@ -894,16 +973,17 @@ def test_completion_error(ac_app, capsys, args, text):
assert "{} broke something".format(text) in out
-@pytest.mark.parametrize('command_and_args, completions', [
- # Exercise a choices function that receives arg_tokens dictionary
- ('arg_tokens choice subcmd', ['choice', 'subcmd']),
-
- # Exercise a completer that receives arg_tokens dictionary
- ('arg_tokens completer subcmd fake', ['completer', 'subcmd']),
-
- # Exercise overriding parent_arg from the subcommand
- ('arg_tokens completer subcmd --parent_arg override fake', ['override', 'subcmd'])
-])
+@pytest.mark.parametrize(
+ 'command_and_args, completions',
+ [
+ # Exercise a choices function that receives arg_tokens dictionary
+ ('arg_tokens choice subcmd', ['choice', 'subcmd']),
+ # Exercise a completer that receives arg_tokens dictionary
+ ('arg_tokens completer subcmd fake', ['completer', 'subcmd']),
+ # Exercise overriding parent_arg from the subcommand
+ ('arg_tokens completer subcmd --parent_arg override fake', ['override', 'subcmd']),
+ ],
+)
def test_arg_tokens(ac_app, command_and_args, completions):
text = ''
line = '{} {}'.format(command_and_args, text)
@@ -919,34 +999,29 @@ def test_arg_tokens(ac_app, command_and_args, completions):
assert ac_app.completion_matches == sorted(completions, key=ac_app.default_sort_key)
-@pytest.mark.parametrize('command_and_args, text, output_contains, first_match', [
- # Group isn't done. Hint will show for optional positional and no completions returned
- ('mutex', '', 'the optional positional', None),
-
- # Group isn't done. Flag name will still complete.
- ('mutex', '--fl', '', '--flag '),
-
- # Group isn't done. Flag hint will show.
- ('mutex --flag', '', 'the flag arg', None),
-
- # Group finished by optional positional. No flag name will complete.
- ('mutex pos_val', '--fl', '', None),
-
- # Group finished by optional positional. Error will display trying to complete the flag's value.
- ('mutex pos_val --flag', '', 'f/--flag: not allowed with argument optional_pos', None),
-
- # Group finished by --flag. Optional positional will be skipped and last_arg will show its hint.
- ('mutex --flag flag_val', '', 'the last arg', None),
-
- # Group finished by --flag. Other flag name won't complete.
- ('mutex --flag flag_val', '--oth', '', None),
-
- # Group finished by --flag. Error will display trying to complete other flag's value.
- ('mutex --flag flag_val --other', '', '-o/--other_flag: not allowed with argument -f/--flag', None),
-
- # Group finished by --flag. That same flag can be used again so it's hint will show.
- ('mutex --flag flag_val --flag', '', 'the flag arg', None)
-])
+@pytest.mark.parametrize(
+ 'command_and_args, text, output_contains, first_match',
+ [
+ # Group isn't done. Hint will show for optional positional and no completions returned
+ ('mutex', '', 'the optional positional', None),
+ # Group isn't done. Flag name will still complete.
+ ('mutex', '--fl', '', '--flag '),
+ # Group isn't done. Flag hint will show.
+ ('mutex --flag', '', 'the flag arg', None),
+ # Group finished by optional positional. No flag name will complete.
+ ('mutex pos_val', '--fl', '', None),
+ # Group finished by optional positional. Error will display trying to complete the flag's value.
+ ('mutex pos_val --flag', '', 'f/--flag: not allowed with argument optional_pos', None),
+ # Group finished by --flag. Optional positional will be skipped and last_arg will show its hint.
+ ('mutex --flag flag_val', '', 'the last arg', None),
+ # Group finished by --flag. Other flag name won't complete.
+ ('mutex --flag flag_val', '--oth', '', None),
+ # Group finished by --flag. Error will display trying to complete other flag's value.
+ ('mutex --flag flag_val --other', '', '-o/--other_flag: not allowed with argument -f/--flag', None),
+ # Group finished by --flag. That same flag can be used again so it's hint will show.
+ ('mutex --flag flag_val --flag', '', 'the flag arg', None),
+ ],
+)
def test_complete_mutex_group(ac_app, command_and_args, text, output_contains, first_match, capsys):
line = '{} {}'.format(command_and_args, text)
endidx = len(line)
@@ -960,6 +1035,7 @@ def test_complete_mutex_group(ac_app, command_and_args, text, output_contains, f
def test_single_prefix_char():
from cmd2.argparse_completer import _single_prefix_char
+
parser = Cmd2ArgumentParser(prefix_chars='-+')
# Invalid
@@ -976,6 +1052,7 @@ def test_single_prefix_char():
def test_looks_like_flag():
from cmd2.argparse_completer import _looks_like_flag
+
parser = Cmd2ArgumentParser()
# Does not start like a flag
diff --git a/tests/test_argparse_custom.py b/tests/test_argparse_custom.py
index e2b3bb97..24a037fa 100644
--- a/tests/test_argparse_custom.py
+++ b/tests/test_argparse_custom.py
@@ -15,6 +15,7 @@ from .conftest import run_cmd
class ApCustomTestApp(cmd2.Cmd):
"""Test app for cmd2's argparse customization"""
+
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@@ -40,15 +41,18 @@ def fake_func():
pass
-@pytest.mark.parametrize('kwargs, is_valid', [
- ({'choices_function': fake_func}, True),
- ({'choices_method': fake_func}, True),
- ({'completer_function': fake_func}, True),
- ({'completer_method': fake_func}, True),
- ({'choices_function': fake_func, 'choices_method': fake_func}, False),
- ({'choices_method': fake_func, 'completer_function': fake_func}, False),
- ({'completer_function': fake_func, 'completer_method': fake_func}, False),
-])
+@pytest.mark.parametrize(
+ 'kwargs, is_valid',
+ [
+ ({'choices_function': fake_func}, True),
+ ({'choices_method': fake_func}, True),
+ ({'completer_function': fake_func}, True),
+ ({'completer_method': fake_func}, True),
+ ({'choices_function': fake_func, 'choices_method': fake_func}, False),
+ ({'choices_method': fake_func, 'completer_function': fake_func}, False),
+ ({'completer_function': fake_func, 'completer_method': fake_func}, False),
+ ],
+)
def test_apcustom_choices_callable_count(kwargs, is_valid):
parser = Cmd2ArgumentParser()
try:
@@ -59,12 +63,15 @@ def test_apcustom_choices_callable_count(kwargs, is_valid):
assert 'Only one of the following parameters' in str(ex)
-@pytest.mark.parametrize('kwargs', [
- ({'choices_function': fake_func}),
- ({'choices_method': fake_func}),
- ({'completer_function': fake_func}),
- ({'completer_method': fake_func})
-])
+@pytest.mark.parametrize(
+ 'kwargs',
+ [
+ ({'choices_function': fake_func}),
+ ({'choices_method': fake_func}),
+ ({'completer_function': fake_func}),
+ ({'completer_method': fake_func}),
+ ],
+)
def test_apcustom_no_choices_callables_alongside_choices(kwargs):
with pytest.raises(TypeError) as excinfo:
parser = Cmd2ArgumentParser()
@@ -72,12 +79,15 @@ def test_apcustom_no_choices_callables_alongside_choices(kwargs):
assert 'None of the following parameters can be used alongside a choices parameter' in str(excinfo.value)
-@pytest.mark.parametrize('kwargs', [
- ({'choices_function': fake_func}),
- ({'choices_method': fake_func}),
- ({'completer_function': fake_func}),
- ({'completer_method': fake_func})
-])
+@pytest.mark.parametrize(
+ 'kwargs',
+ [
+ ({'choices_function': fake_func}),
+ ({'choices_method': fake_func}),
+ ({'completer_function': fake_func}),
+ ({'completer_method': fake_func}),
+ ],
+)
def test_apcustom_no_choices_callables_when_nargs_is_0(kwargs):
with pytest.raises(TypeError) as excinfo:
parser = Cmd2ArgumentParser()
@@ -119,12 +129,7 @@ def test_apcustom_nargs_range_validation(cust_app):
assert not err
-@pytest.mark.parametrize('nargs_tuple', [
- (),
- ('f', 5),
- (5, 'f'),
- (1, 2, 3),
-])
+@pytest.mark.parametrize('nargs_tuple', [(), ('f', 5), (5, 'f'), (1, 2, 3),])
def test_apcustom_narg_invalid_tuples(nargs_tuple):
with pytest.raises(ValueError) as excinfo:
parser = Cmd2ArgumentParser()
@@ -200,6 +205,7 @@ def test_apcustom_narg_tuple_other_ranges():
def test_apcustom_print_message(capsys):
import sys
+
test_message = 'The test message'
# Specify the file
@@ -247,6 +253,7 @@ def test_apcustom_required_options():
def test_override_parser():
import importlib
+
from cmd2 import DEFAULT_ARGUMENT_PARSER
# The standard parser is Cmd2ArgumentParser
@@ -259,6 +266,7 @@ def test_override_parser():
# Verify DEFAULT_ARGUMENT_PARSER is now our CustomParser
from examples.custom_parser import CustomParser
+
assert DEFAULT_ARGUMENT_PARSER == CustomParser
diff --git a/tests/test_cmd2.py b/tests/test_cmd2.py
index 2f24f4d7..ae911474 100755
--- a/tests/test_cmd2.py
+++ b/tests/test_cmd2.py
@@ -40,15 +40,19 @@ def CreateOutsimApp():
c.stdout = utils.StdSim(c.stdout)
return c
+
@pytest.fixture
def outsim_app():
return CreateOutsimApp()
+
def test_version(base_app):
assert cmd2.__version__
+
def test_not_in_main_thread(base_app, capsys):
import threading
+
cli_thread = threading.Thread(name='cli_thread', target=base_app.cmdloop)
cli_thread.start()
@@ -56,15 +60,18 @@ def test_not_in_main_thread(base_app, capsys):
out, err = capsys.readouterr()
assert "cmdloop must be run in the main thread" in err
+
def test_empty_statement(base_app):
out, err = run_cmd(base_app, '')
expected = normalize('')
assert out == expected
+
def test_base_help(base_app):
out, err = run_cmd(base_app, 'help')
verify_help_text(base_app, out)
+
def test_base_help_verbose(base_app):
out, err = run_cmd(base_app, 'help -v')
verify_help_text(base_app, out)
@@ -78,6 +85,7 @@ def test_base_help_verbose(base_app):
verify_help_text(base_app, out)
assert ':param' not in ''.join(out)
+
def test_base_argparse_help(base_app):
# Verify that "set -h" gives the same output as "help set" and that it starts in a way that makes sense
out1, err1 = run_cmd(base_app, 'set -h')
@@ -88,21 +96,25 @@ def test_base_argparse_help(base_app):
assert out1[1] == ''
assert out1[2].startswith('Set a settable parameter')
+
def test_base_invalid_option(base_app):
out, err = run_cmd(base_app, 'set -z')
assert err[0] == 'Usage: set [-h] [-v] [param] [value]'
assert 'Error: unrecognized arguments: -z' in err[1]
+
def test_base_shortcuts(base_app):
out, err = run_cmd(base_app, 'shortcuts')
expected = normalize(SHORTCUTS_TXT)
assert out == expected
+
def test_command_starts_with_shortcut():
with pytest.raises(ValueError) as excinfo:
app = cmd2.Cmd(shortcuts={'help': 'fake'})
assert "Invalid command name 'help'" in str(excinfo.value)
+
def test_base_show(base_app):
# force editor to be 'vim' so test is repeatable across platforms
base_app.editor = 'vim'
@@ -121,30 +133,37 @@ def test_base_show_long(base_app):
def test_set(base_app):
out, err = run_cmd(base_app, 'set quiet True')
- expected = normalize("""
+ expected = normalize(
+ """
quiet - was: False
now: True
-""")
+"""
+ )
assert out == expected
out, err = run_cmd(base_app, 'set quiet')
assert out == ['quiet: True']
+
def test_set_val_empty(base_app):
base_app.editor = "fake"
out, err = run_cmd(base_app, 'set editor ""')
assert base_app.editor == ''
+
def test_set_val_is_flag(base_app):
base_app.editor = "fake"
out, err = run_cmd(base_app, 'set editor "-h"')
assert base_app.editor == '-h'
+
def test_set_not_supported(base_app):
out, err = run_cmd(base_app, 'set qqq True')
- expected = normalize("""
+ expected = normalize(
+ """
Parameter 'qqq' not supported (type 'set' for list of parameters).
-""")
+"""
+ )
assert err == expected
@@ -155,15 +174,18 @@ def test_set_no_settables(base_app):
assert err == expected
-@pytest.mark.parametrize('new_val, is_valid, expected', [
- (ansi.STYLE_NEVER, True, ansi.STYLE_NEVER),
- ('neVeR', True, ansi.STYLE_NEVER),
- (ansi.STYLE_TERMINAL, True, ansi.STYLE_TERMINAL),
- ('TeRMInal', True, ansi.STYLE_TERMINAL),
- (ansi.STYLE_ALWAYS, True, ansi.STYLE_ALWAYS),
- ('AlWaYs', True, ansi.STYLE_ALWAYS),
- ('invalid', False, ansi.STYLE_TERMINAL),
-])
+@pytest.mark.parametrize(
+ 'new_val, is_valid, expected',
+ [
+ (ansi.STYLE_NEVER, True, ansi.STYLE_NEVER),
+ ('neVeR', True, ansi.STYLE_NEVER),
+ (ansi.STYLE_TERMINAL, True, ansi.STYLE_TERMINAL),
+ ('TeRMInal', True, ansi.STYLE_TERMINAL),
+ (ansi.STYLE_ALWAYS, True, ansi.STYLE_ALWAYS),
+ ('AlWaYs', True, ansi.STYLE_ALWAYS),
+ ('invalid', False, ansi.STYLE_TERMINAL),
+ ],
+)
def test_set_allow_style(base_app, new_val, is_valid, expected):
# Initialize allow_style for this test
ansi.allow_style = ansi.STYLE_TERMINAL
@@ -190,18 +212,22 @@ class OnChangeHookApp(cmd2.Cmd):
"""Runs when quiet is changed via set command"""
self.poutput("You changed " + name)
+
@pytest.fixture
def onchange_app():
app = OnChangeHookApp()
return app
+
def test_set_onchange_hook(onchange_app):
out, err = run_cmd(onchange_app, 'set quiet True')
- expected = normalize("""
+ expected = normalize(
+ """
quiet - was: False
now: True
You changed quiet
-""")
+"""
+ )
assert out == expected
@@ -212,6 +238,7 @@ def test_base_shell(base_app, monkeypatch):
assert out == []
assert m.called
+
def test_shell_last_result(base_app):
base_app.last_result = None
run_cmd(base_app, 'shell fake')
@@ -220,11 +247,7 @@ def test_shell_last_result(base_app):
def test_shell_manual_call(base_app):
# Verifies crash from Issue #986 doesn't happen
- cmds = [
- 'echo "hi"',
- 'echo "there"',
- 'echo "cmd2!"'
- ]
+ cmds = ['echo "hi"', 'echo "there"', 'echo "cmd2!"']
cmd = ';'.join(cmds)
base_app.do_shell(cmd)
@@ -299,31 +322,37 @@ def test_run_script(base_app, request):
assert script_out == manual_out
assert script_err == manual_err
+
def test_run_script_with_empty_args(base_app):
out, err = run_cmd(base_app, 'run_script')
assert "the following arguments are required" in err[1]
+
def test_run_script_with_nonexistent_file(base_app, capsys):
out, err = run_cmd(base_app, 'run_script does_not_exist.txt')
assert "does not exist" in err[0]
+
def test_run_script_with_directory(base_app, request):
test_dir = os.path.dirname(request.module.__file__)
out, err = run_cmd(base_app, 'run_script {}'.format(test_dir))
assert "is not a file" in err[0]
+
def test_run_script_with_empty_file(base_app, request):
test_dir = os.path.dirname(request.module.__file__)
filename = os.path.join(test_dir, 'scripts', 'empty.txt')
out, err = run_cmd(base_app, 'run_script {}'.format(filename))
assert not out and not err
+
def test_run_script_with_binary_file(base_app, request):
test_dir = os.path.dirname(request.module.__file__)
filename = os.path.join(test_dir, 'scripts', 'binary.bin')
out, err = run_cmd(base_app, 'run_script {}'.format(filename))
assert "is not an ASCII or UTF-8 encoded text file" in err[0]
+
def test_run_script_with_python_file(base_app, request):
m = mock.MagicMock(name='input', return_value='2')
builtins.input = m
@@ -333,6 +362,7 @@ def test_run_script_with_python_file(base_app, request):
out, err = run_cmd(base_app, 'run_script {}'.format(filename))
assert "appears to be a Python file" in err[0]
+
def test_run_script_with_utf8_file(base_app, request):
test_dir = os.path.dirname(request.module.__file__)
filename = os.path.join(test_dir, 'scripts', 'utf8.txt')
@@ -360,6 +390,7 @@ def test_run_script_with_utf8_file(base_app, request):
assert script_out == manual_out
assert script_err == manual_err
+
def test_run_script_nested_run_scripts(base_app, request):
# Verify that running a script with nested run_script commands works correctly,
# and runs the nested script commands in the correct order.
@@ -371,43 +402,48 @@ def test_run_script_nested_run_scripts(base_app, request):
run_cmd(base_app, initial_run)
# Check that the right commands were executed.
- expected = """
+ expected = (
+ """
%s
_relative_run_script precmds.txt
set allow_style Always
help
shortcuts
_relative_run_script postcmds.txt
-set allow_style Never""" % initial_run
+set allow_style Never"""
+ % initial_run
+ )
out, err = run_cmd(base_app, 'history -s')
assert out == normalize(expected)
+
def test_runcmds_plus_hooks(base_app, request):
test_dir = os.path.dirname(request.module.__file__)
prefilepath = os.path.join(test_dir, 'scripts', 'precmds.txt')
postfilepath = os.path.join(test_dir, 'scripts', 'postcmds.txt')
- base_app.runcmds_plus_hooks(['run_script ' + prefilepath,
- 'help',
- 'shortcuts',
- 'run_script ' + postfilepath])
+ base_app.runcmds_plus_hooks(['run_script ' + prefilepath, 'help', 'shortcuts', 'run_script ' + postfilepath])
expected = """
-run_script %s
+run_script {}
set allow_style Always
help
shortcuts
-run_script %s
-set allow_style Never""" % (prefilepath, postfilepath)
+run_script {}
+set allow_style Never""".format(
+ prefilepath, postfilepath,
+ )
out, err = run_cmd(base_app, 'history -s')
assert out == normalize(expected)
+
def test_runcmds_plus_hooks_ctrl_c(base_app, capsys):
"""Test Ctrl-C while in runcmds_plus_hooks"""
import types
def do_keyboard_interrupt(self, _):
raise KeyboardInterrupt('Interrupting this command')
+
setattr(base_app, 'do_keyboard_interrupt', types.MethodType(do_keyboard_interrupt, base_app))
# Default behavior is to stop command loop on Ctrl-C
@@ -424,6 +460,7 @@ def test_runcmds_plus_hooks_ctrl_c(base_app, capsys):
assert not err
assert len(base_app.history) == 3
+
def test_relative_run_script(base_app, request):
test_dir = os.path.dirname(request.module.__file__)
filename = os.path.join(test_dir, 'script.txt')
@@ -451,6 +488,7 @@ def test_relative_run_script(base_app, request):
assert script_out == manual_out
assert script_err == manual_err
+
@pytest.mark.parametrize('file_name', odd_file_names)
def test_relative_run_script_with_odd_file_names(base_app, file_name, monkeypatch):
"""Test file names with various patterns"""
@@ -461,10 +499,12 @@ def test_relative_run_script_with_odd_file_names(base_app, file_name, monkeypatc
run_cmd(base_app, "_relative_run_script {}".format(utils.quote_string(file_name)))
run_script_mock.assert_called_once_with(utils.quote_string(file_name))
+
def test_relative_run_script_requires_an_argument(base_app):
out, err = run_cmd(base_app, '_relative_run_script')
assert 'Error: the following arguments' in err[1]
+
def test_in_script(request):
class HookApp(cmd2.Cmd):
def __init__(self, *args, **kwargs):
@@ -483,17 +523,20 @@ def test_in_script(request):
assert "WE ARE IN SCRIPT" in out[-1]
+
def test_system_exit_in_command(base_app, capsys):
"""Test raising SystemExit from a command"""
import types
def do_system_exit(self, _):
raise SystemExit
+
setattr(base_app, 'do_system_exit', types.MethodType(do_system_exit, base_app))
stop = base_app.onecmd_plus_hooks('system_exit')
assert stop
+
def test_output_redirection(base_app):
fd, filename = tempfile.mkstemp(prefix='cmd2_test', suffix='.txt')
os.close(fd)
@@ -516,6 +559,7 @@ def test_output_redirection(base_app):
finally:
os.remove(filename)
+
def test_output_redirection_to_nonexistent_directory(base_app):
filename = '~/fakedir/this_does_not_exist.txt'
@@ -525,12 +569,15 @@ def test_output_redirection_to_nonexistent_directory(base_app):
out, err = run_cmd(base_app, 'help >> {}'.format(filename))
assert 'Failed to redirect' in err[0]
+
def test_output_redirection_to_too_long_filename(base_app):
- filename = '~/sdkfhksdjfhkjdshfkjsdhfkjsdhfkjdshfkjdshfkjshdfkhdsfkjhewfuihewiufhweiufhiweufhiuewhiuewhfiuwehfia' \
- 'ewhfiuewhfiuewhfiuewhiuewhfiuewhfiuewfhiuwehewiufhewiuhfiweuhfiuwehfiuewfhiuwehiuewfhiuewhiewuhfiueh' \
- 'fiuwefhewiuhewiufhewiufhewiufhewiufhewiufhewiufhewiufhewiuhewiufhewiufhewiuheiufhiuewheiwufhewiufheu' \
- 'fheiufhieuwhfewiuhfeiufhiuewfhiuewheiwuhfiuewhfiuewhfeiuwfhewiufhiuewhiuewhfeiuwhfiuwehfuiwehfiuehie' \
- 'whfieuwfhieufhiuewhfeiuwfhiuefhueiwhfw'
+ filename = (
+ '~/sdkfhksdjfhkjdshfkjsdhfkjsdhfkjdshfkjdshfkjshdfkhdsfkjhewfuihewiufhweiufhiweufhiuewhiuewhfiuwehfia'
+ 'ewhfiuewhfiuewhfiuewhiuewhfiuewhfiuewfhiuwehewiufhewiuhfiweuhfiuwehfiuewfhiuwehiuewfhiuewhiewuhfiueh'
+ 'fiuwefhewiuhewiufhewiufhewiufhewiufhewiufhewiufhewiufhewiuhewiufhewiufhewiuheiufhiuewheiwufhewiufheu'
+ 'fheiufhieuwhfewiuhfeiufhiuewfhiuewheiwuhfiuewhfiuewhfeiuwfhewiufhiuewhiuewhfeiuwhfiuwehfuiwehfiuehie'
+ 'whfieuwfhieufhiuewhfeiuwfhiuefhueiwhfw'
+ )
out, err = run_cmd(base_app, 'help > {}'.format(filename))
assert 'Failed to redirect' in err[0]
@@ -588,6 +635,7 @@ def test_disallow_redirection(base_app):
# Verify that no file got created
assert not os.path.exists(filename)
+
def test_pipe_to_shell(base_app):
if sys.platform == "win32":
# Windows
@@ -600,6 +648,7 @@ def test_pipe_to_shell(base_app):
out, err = run_cmd(base_app, command)
assert out and not err
+
def test_pipe_to_shell_and_redirect(base_app):
filename = 'out.txt'
if sys.platform == "win32":
@@ -615,14 +664,15 @@ def test_pipe_to_shell_and_redirect(base_app):
assert os.path.exists(filename)
os.remove(filename)
+
def test_pipe_to_shell_error(base_app):
# Try to pipe command output to a shell command that doesn't exist in order to produce an error
out, err = run_cmd(base_app, 'help | foobarbaz.this_does_not_exist')
assert not out
assert "Pipe process exited with code" in err[0]
-@pytest.mark.skipif(not clipboard.can_clip,
- reason="Pyperclip could not find a copy/paste mechanism for your system")
+
+@pytest.mark.skipif(not clipboard.can_clip, reason="Pyperclip could not find a copy/paste mechanism for your system")
def test_send_to_paste_buffer(base_app):
# Test writing to the PasteBuffer/Clipboard
run_cmd(base_app, 'help >')
@@ -639,9 +689,11 @@ def test_send_to_paste_buffer(base_app):
def test_base_timing(base_app):
base_app.feedback_to_output = False
out, err = run_cmd(base_app, 'set timing True')
- expected = normalize("""timing - was: False
+ expected = normalize(
+ """timing - was: False
now: True
-""")
+"""
+ )
assert out == expected
if sys.platform == 'win32':
@@ -656,13 +708,18 @@ def _expected_no_editor_error():
if hasattr(sys, "pypy_translation_info"):
expected_exception = 'EnvironmentError'
- expected_text = normalize("""
+ expected_text = normalize(
+ """
EXCEPTION of type '{}' occurred with message: 'Please use 'set editor' to specify your text editing program of choice.'
To enable full traceback, run the following command: 'set debug true'
-""".format(expected_exception))
+""".format(
+ expected_exception
+ )
+ )
return expected_text
+
def test_base_debug(base_app):
# Purposely set the editor to None
base_app.editor = None
@@ -675,16 +732,19 @@ def test_base_debug(base_app):
# Set debug true
out, err = run_cmd(base_app, 'set debug True')
- expected = normalize("""
+ expected = normalize(
+ """
debug - was: False
now: True
-""")
+"""
+ )
assert out == expected
# Verify that we now see the exception traceback
out, err = run_cmd(base_app, 'edit')
assert err[0].startswith('Traceback (most recent call last):')
+
def test_debug_not_settable(base_app):
# Set debug to False and make it unsettable
base_app.debug = False
@@ -696,10 +756,12 @@ def test_debug_not_settable(base_app):
# Since debug is unsettable, the user will not be given the option to enable a full traceback
assert err == ['Invalid syntax: No closing quotation']
+
def test_remove_settable_keyerror(base_app):
with pytest.raises(KeyError):
base_app.remove_settable('fake')
+
def test_edit_file(base_app, request, monkeypatch):
# Set a fake editor just to make sure we have one. We aren't really going to call it due to the mock
base_app.editor = 'fooedit'
@@ -716,6 +778,7 @@ def test_edit_file(base_app, request, monkeypatch):
# We think we have an editor, so should expect a Popen call
m.assert_called_once()
+
@pytest.mark.parametrize('file_name', odd_file_names)
def test_edit_file_with_odd_file_names(base_app, file_name, monkeypatch):
"""Test editor and file names with various patterns"""
@@ -728,6 +791,7 @@ def test_edit_file_with_odd_file_names(base_app, file_name, monkeypatch):
run_cmd(base_app, "edit {}".format(utils.quote_string(file_name)))
shell_mock.assert_called_once_with('"fooedit" {}'.format(utils.quote_string(file_name)))
+
def test_edit_file_with_spaces(base_app, request, monkeypatch):
# Set a fake editor just to make sure we have one. We aren't really going to call it due to the mock
base_app.editor = 'fooedit'
@@ -744,6 +808,7 @@ def test_edit_file_with_spaces(base_app, request, monkeypatch):
# We think we have an editor, so should expect a Popen call
m.assert_called_once()
+
def test_edit_blank(base_app, monkeypatch):
# Set a fake editor just to make sure we have one. We aren't really going to call it due to the mock
base_app.editor = 'fooedit'
@@ -830,8 +895,8 @@ def test_cmdloop_without_rawinput():
out = app.stdout.getvalue()
assert out == expected
-@pytest.mark.skipif(sys.platform.startswith('win'),
- reason="stty sane only run on Linux/Mac")
+
+@pytest.mark.skipif(sys.platform.startswith('win'), reason="stty sane only run on Linux/Mac")
def test_stty_sane(base_app, monkeypatch):
"""Make sure stty sane is run on Linux/Mac after each command if stdin is a terminal"""
with mock.patch('sys.stdin.isatty', mock.MagicMock(name='isatty', return_value=True)):
@@ -842,6 +907,7 @@ def test_stty_sane(base_app, monkeypatch):
base_app.onecmd_plus_hooks('help')
m.assert_called_once_with(['stty', 'sane'])
+
class HookFailureApp(cmd2.Cmd):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@@ -853,11 +919,13 @@ class HookFailureApp(cmd2.Cmd):
data.stop = True
return data
+
@pytest.fixture
def hook_failure():
app = HookFailureApp()
return app
+
def test_precmd_hook_success(base_app):
out = base_app.onecmd_plus_hooks('help')
assert out is False
@@ -875,12 +943,14 @@ class SayApp(cmd2.Cmd):
def do_say(self, arg):
self.poutput(arg)
+
@pytest.fixture
def say_app():
app = SayApp(allow_cli_args=False)
app.stdout = utils.StdSim(app.stdout)
return app
+
def test_interrupt_quit(say_app):
say_app.quit_on_sigint = True
@@ -898,6 +968,7 @@ def test_interrupt_quit(say_app):
out = say_app.stdout.getvalue()
assert out == 'hello\n'
+
def test_interrupt_noquit(say_app):
say_app.quit_on_sigint = False
@@ -921,6 +992,7 @@ class ShellApp(cmd2.Cmd):
super().__init__(*args, **kwargs)
self.default_to_shell = True
+
def test_default_to_shell(base_app, monkeypatch):
if sys.platform.startswith('win'):
line = 'dir'
@@ -934,14 +1006,17 @@ def test_default_to_shell(base_app, monkeypatch):
assert out == []
assert m.called
+
def test_ansi_prompt_not_esacped(base_app):
from cmd2.rl_utils import rl_make_safe_prompt
+
prompt = '(Cmd) '
assert rl_make_safe_prompt(prompt) == prompt
def test_ansi_prompt_escaped():
from cmd2.rl_utils import rl_make_safe_prompt
+
app = cmd2.Cmd()
color = 'cyan'
prompt = 'InColor'
@@ -963,6 +1038,7 @@ def test_ansi_prompt_escaped():
class HelpApp(cmd2.Cmd):
"""Class for testing custom help_* methods which override docstring help."""
+
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@@ -996,24 +1072,29 @@ def help_app():
app = HelpApp()
return app
+
def test_custom_command_help(help_app):
out, err = run_cmd(help_app, 'help squat')
expected = normalize('This command does diddly squat...')
assert out == expected
+
def test_custom_help_menu(help_app):
out, err = run_cmd(help_app, 'help')
verify_help_text(help_app, out)
+
def test_help_undocumented(help_app):
out, err = run_cmd(help_app, 'help undoc')
assert err[0].startswith("No help on undoc")
+
def test_help_overridden_method(help_app):
out, err = run_cmd(help_app, 'help edit')
expected = normalize('This overrides the edit command and does nothing.')
assert out == expected
+
def test_help_multiline_docstring(help_app):
out, err = run_cmd(help_app, 'help multiline_docstr')
expected = normalize('This documentation\nis multiple lines\nand there are no\ntabs')
@@ -1022,6 +1103,7 @@ def test_help_multiline_docstring(help_app):
class HelpCategoriesApp(cmd2.Cmd):
"""Class for testing custom help_* methods which override docstring help."""
+
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@@ -1052,15 +1134,18 @@ class HelpCategoriesApp(cmd2.Cmd):
def do_undoc(self, arg):
pass
+
@pytest.fixture
def helpcat_app():
app = HelpCategoriesApp()
return app
+
def test_help_cat_base(helpcat_app):
out, err = run_cmd(helpcat_app, 'help')
verify_help_text(helpcat_app, out)
+
def test_help_cat_verbose(helpcat_app):
out, err = run_cmd(helpcat_app, 'help --verbose')
verify_help_text(helpcat_app, out)
@@ -1085,8 +1170,9 @@ class SelectApp(cmd2.Cmd):
def do_procrastinate(self, arg):
"""Waste time in your manner of choice."""
# Pass in a list of tuples for selections
- leisure_activity = self.select([('Netflix and chill', 'Netflix'), ('YouTube', 'WebSurfing')],
- 'How would you like to procrastinate? ')
+ leisure_activity = self.select(
+ [('Netflix and chill', 'Netflix'), ('YouTube', 'WebSurfing')], 'How would you like to procrastinate? '
+ )
result = 'Have fun procrasinating with {}!\n'.format(leisure_activity)
self.stdout.write(result)
@@ -1097,11 +1183,13 @@ class SelectApp(cmd2.Cmd):
result = 'Charm us with the {}...\n'.format(instrument)
self.stdout.write(result)
+
@pytest.fixture
def select_app():
app = SelectApp()
return app
+
def test_select_options(select_app, monkeypatch):
# Mock out the read_input call so we don't actually wait for a user's response on stdin
read_input_mock = mock.MagicMock(name='read_input', return_value='2')
@@ -1109,11 +1197,15 @@ def test_select_options(select_app, monkeypatch):
food = 'bacon'
out, err = run_cmd(select_app, "eat {}".format(food))
- expected = normalize("""
+ expected = normalize(
+ """
1. sweet
2. salty
{} with salty sauce, yum!
-""".format(food))
+""".format(
+ food
+ )
+ )
# Make sure our mock was called with the expected arguments
read_input_mock.assert_called_once_with('Sauce? ')
@@ -1121,6 +1213,7 @@ def test_select_options(select_app, monkeypatch):
# And verify the expected output to stdout
assert out == expected
+
def test_select_invalid_option_too_big(select_app, monkeypatch):
# Mock out the input call so we don't actually wait for a user's response on stdin
read_input_mock = mock.MagicMock(name='read_input')
@@ -1131,12 +1224,16 @@ def test_select_invalid_option_too_big(select_app, monkeypatch):
food = 'fish'
out, err = run_cmd(select_app, "eat {}".format(food))
- expected = normalize("""
+ expected = normalize(
+ """
1. sweet
2. salty
'3' isn't a valid choice. Pick a number between 1 and 2:
{} with sweet sauce, yum!
-""".format(food))
+""".format(
+ food
+ )
+ )
# Make sure our mock was called exactly twice with the expected arguments
arg = 'Sauce? '
@@ -1147,6 +1244,7 @@ def test_select_invalid_option_too_big(select_app, monkeypatch):
# And verify the expected output to stdout
assert out == expected
+
def test_select_invalid_option_too_small(select_app, monkeypatch):
# Mock out the input call so we don't actually wait for a user's response on stdin
read_input_mock = mock.MagicMock(name='read_input')
@@ -1157,12 +1255,16 @@ def test_select_invalid_option_too_small(select_app, monkeypatch):
food = 'fish'
out, err = run_cmd(select_app, "eat {}".format(food))
- expected = normalize("""
+ expected = normalize(
+ """
1. sweet
2. salty
'0' isn't a valid choice. Pick a number between 1 and 2:
{} with sweet sauce, yum!
-""".format(food))
+""".format(
+ food
+ )
+ )
# Make sure our mock was called exactly twice with the expected arguments
arg = 'Sauce? '
@@ -1173,17 +1275,22 @@ def test_select_invalid_option_too_small(select_app, monkeypatch):
# And verify the expected output to stdout
assert out == expected
+
def test_select_list_of_strings(select_app, monkeypatch):
# Mock out the input call so we don't actually wait for a user's response on stdin
read_input_mock = mock.MagicMock(name='read_input', return_value='2')
monkeypatch.setattr("cmd2.Cmd.read_input", read_input_mock)
out, err = run_cmd(select_app, "study")
- expected = normalize("""
+ expected = normalize(
+ """
1. math
2. science
Good luck learning {}!
-""".format('science'))
+""".format(
+ 'science'
+ )
+ )
# Make sure our mock was called with the expected arguments
read_input_mock.assert_called_once_with('Subject? ')
@@ -1191,17 +1298,22 @@ Good luck learning {}!
# And verify the expected output to stdout
assert out == expected
+
def test_select_list_of_tuples(select_app, monkeypatch):
# Mock out the input call so we don't actually wait for a user's response on stdin
read_input_mock = mock.MagicMock(name='read_input', return_value='2')
monkeypatch.setattr("cmd2.Cmd.read_input", read_input_mock)
out, err = run_cmd(select_app, "procrastinate")
- expected = normalize("""
+ expected = normalize(
+ """
1. Netflix
2. WebSurfing
Have fun procrasinating with {}!
-""".format('YouTube'))
+""".format(
+ 'YouTube'
+ )
+ )
# Make sure our mock was called with the expected arguments
read_input_mock.assert_called_once_with('How would you like to procrastinate? ')
@@ -1216,11 +1328,15 @@ def test_select_uneven_list_of_tuples(select_app, monkeypatch):
monkeypatch.setattr("cmd2.Cmd.read_input", read_input_mock)
out, err = run_cmd(select_app, "play")
- expected = normalize("""
+ expected = normalize(
+ """
1. Electric Guitar
2. Drums
Charm us with the {}...
-""".format('Drums'))
+""".format(
+ 'Drums'
+ )
+ )
# Make sure our mock was called with the expected arguments
read_input_mock.assert_called_once_with('Instrument? ')
@@ -1228,6 +1344,7 @@ Charm us with the {}...
# And verify the expected output to stdout
assert out == expected
+
def test_select_eof(select_app, monkeypatch):
# Ctrl-D during select causes an EOFError that just reprompts the user
read_input_mock = mock.MagicMock(name='read_input', side_effect=[EOFError, 2])
@@ -1242,6 +1359,7 @@ def test_select_eof(select_app, monkeypatch):
read_input_mock.assert_has_calls(calls)
assert read_input_mock.call_count == 2
+
def test_select_ctrl_c(outsim_app, monkeypatch, capsys):
# Ctrl-C during select prints ^C and raises a KeyboardInterrupt
read_input_mock = mock.MagicMock(name='read_input', side_effect=KeyboardInterrupt)
@@ -1253,9 +1371,11 @@ def test_select_ctrl_c(outsim_app, monkeypatch, capsys):
out = outsim_app.stdout.getvalue()
assert out.rstrip().endswith('^C')
+
class HelpNoDocstringApp(cmd2.Cmd):
greet_parser = argparse.ArgumentParser()
greet_parser.add_argument('-s', '--shout', action="store_true", help="N00B EMULATION MODE")
+
@cmd2.with_argparser(greet_parser, with_unknown_args=True)
def do_greet(self, opts, arg):
arg = ''.join(arg)
@@ -1263,17 +1383,22 @@ class HelpNoDocstringApp(cmd2.Cmd):
arg = arg.upper()
self.stdout.write(arg + '\n')
+
def test_help_with_no_docstring(capsys):
app = HelpNoDocstringApp()
app.onecmd_plus_hooks('greet -h')
out, err = capsys.readouterr()
assert err == ''
- assert out == """usage: greet [-h] [-s]
+ assert (
+ out
+ == """usage: greet [-h] [-s]
optional arguments:
-h, --help show this help message and exit
-s, --shout N00B EMULATION MODE
"""
+ )
+
class MultilineApp(cmd2.Cmd):
def __init__(self, *args, **kwargs):
@@ -1289,15 +1414,18 @@ class MultilineApp(cmd2.Cmd):
arg = arg.upper()
self.stdout.write(arg + '\n')
+
@pytest.fixture
def multiline_app():
app = MultilineApp()
return app
+
def test_multiline_complete_empty_statement_raises_exception(multiline_app):
with pytest.raises(exceptions.EmptyStatement):
multiline_app._complete_statement('')
+
def test_multiline_complete_statement_without_terminator(multiline_app):
# Mock out the input call so we don't actually wait for a user's response
# on stdin when it looks for more input
@@ -1312,6 +1440,7 @@ def test_multiline_complete_statement_without_terminator(multiline_app):
assert statement.command == command
assert statement.multiline_command == command
+
def test_multiline_complete_statement_with_unclosed_quotes(multiline_app):
# Mock out the input call so we don't actually wait for a user's response
# on stdin when it looks for more input
@@ -1325,6 +1454,7 @@ def test_multiline_complete_statement_with_unclosed_quotes(multiline_app):
assert statement.multiline_command == 'orate'
assert statement.terminator == ';'
+
def test_multiline_input_line_to_statement(multiline_app):
# Verify _input_line_to_statement saves the fully entered input line for multiline commands
@@ -1340,6 +1470,7 @@ def test_multiline_input_line_to_statement(multiline_app):
assert statement.command == 'orate'
assert statement.multiline_command == 'orate'
+
def test_clipboard_failure(base_app, capsys):
# Force cmd2 clipboard to be disabled
base_app._can_clip = False
@@ -1369,11 +1500,13 @@ class CommandResultApp(cmd2.Cmd):
def do_negative_no_data(self, arg):
self.last_result = cmd2.CommandResult('', arg)
+
@pytest.fixture
def commandresult_app():
app = CommandResultApp()
return app
+
def test_commandresult_truthy(commandresult_app):
arg = 'foo'
run_cmd(commandresult_app, 'affirmative {}'.format(arg))
@@ -1384,6 +1517,7 @@ def test_commandresult_truthy(commandresult_app):
assert commandresult_app.last_result
assert commandresult_app.last_result == cmd2.CommandResult(arg)
+
def test_commandresult_falsy(commandresult_app):
arg = 'bar'
run_cmd(commandresult_app, 'negative {}'.format(arg))
@@ -1409,6 +1543,7 @@ def test_eof(base_app):
# Only thing to verify is that it returns True
assert base_app.do_eof('')
+
def test_echo(capsys):
app = cmd2.Cmd()
app.echo = True
@@ -1419,6 +1554,7 @@ def test_echo(capsys):
out, err = capsys.readouterr()
assert out.startswith('{}{}\n'.format(app.prompt, commands[0]) + HELP_HISTORY.split()[0])
+
def test_read_input_rawinput_true(capsys, monkeypatch):
prompt_str = 'the_prompt'
input_str = 'some input'
@@ -1450,6 +1586,7 @@ def test_read_input_rawinput_true(capsys, monkeypatch):
assert line == input_str
assert not out
+
def test_read_input_rawinput_false(capsys, monkeypatch):
prompt_str = 'the_prompt'
input_str = 'some input'
@@ -1502,6 +1639,7 @@ def test_read_input_rawinput_false(capsys, monkeypatch):
assert line == 'eof'
assert not out
+
def test_read_command_line_eof(base_app, monkeypatch):
read_input_mock = mock.MagicMock(name='read_input', side_effect=EOFError)
monkeypatch.setattr("cmd2.Cmd.read_input", read_input_mock)
@@ -1509,6 +1647,7 @@ def test_read_command_line_eof(base_app, monkeypatch):
line = base_app._read_command_line("Prompt> ")
assert line == 'eof'
+
def test_poutput_string(outsim_app):
msg = 'This is a test'
outsim_app.poutput(msg)
@@ -1516,6 +1655,7 @@ def test_poutput_string(outsim_app):
expected = msg + '\n'
assert out == expected
+
def test_poutput_zero(outsim_app):
msg = 0
outsim_app.poutput(msg)
@@ -1523,6 +1663,7 @@ def test_poutput_zero(outsim_app):
expected = str(msg) + '\n'
assert out == expected
+
def test_poutput_empty_string(outsim_app):
msg = ''
outsim_app.poutput(msg)
@@ -1530,6 +1671,7 @@ def test_poutput_empty_string(outsim_app):
expected = '\n'
assert out == expected
+
def test_poutput_none(outsim_app):
msg = None
outsim_app.poutput(msg)
@@ -1537,6 +1679,7 @@ def test_poutput_none(outsim_app):
expected = 'None\n'
assert out == expected
+
def test_poutput_ansi_always(outsim_app):
msg = 'Hello World'
ansi.allow_style = ansi.STYLE_ALWAYS
@@ -1547,6 +1690,7 @@ def test_poutput_ansi_always(outsim_app):
assert colored_msg != msg
assert out == expected
+
def test_poutput_ansi_never(outsim_app):
msg = 'Hello World'
ansi.allow_style = ansi.STYLE_NEVER
@@ -1571,6 +1715,7 @@ invalid_command_name = [
'noembedded"quotes',
]
+
def test_get_alias_completion_items(base_app):
run_cmd(base_app, 'alias create fake run_pyscript')
run_cmd(base_app, 'alias create ls !ls -hal')
@@ -1582,6 +1727,7 @@ def test_get_alias_completion_items(base_app):
assert cur_res in base_app.aliases
assert cur_res.description == base_app.aliases[cur_res]
+
def test_get_macro_completion_items(base_app):
run_cmd(base_app, 'macro create foo !echo foo')
run_cmd(base_app, 'macro create bar !echo bar')
@@ -1593,17 +1739,20 @@ def test_get_macro_completion_items(base_app):
assert cur_res in base_app.macros
assert cur_res.description == base_app.macros[cur_res].value
+
def test_get_settable_completion_items(base_app):
results = base_app._get_settable_completion_items()
for cur_res in results:
assert cur_res in base_app.settables
assert cur_res.description == base_app.settables[cur_res].description
+
def test_alias_no_subcommand(base_app):
out, err = run_cmd(base_app, 'alias')
assert "Usage: alias [-h]" in err[0]
assert "Error: the following arguments are required: SUBCOMMAND" in err[1]
+
def test_alias_create(base_app):
# Create the alias
out, err = run_cmd(base_app, 'alias create fake run_pyscript')
@@ -1636,6 +1785,7 @@ def test_alias_create(base_app):
out, err = run_cmd(base_app, 'alias list --with_silent fake')
assert out == normalize('alias create --silent fake set')
+
def test_alias_create_with_quoted_tokens(base_app):
"""Demonstrate that quotes in alias value will be preserved"""
create_command = 'alias create fake help ">" "out file.txt" ";"'
@@ -1648,21 +1798,25 @@ def test_alias_create_with_quoted_tokens(base_app):
out, err = run_cmd(base_app, 'alias list fake')
assert out == normalize(create_command)
+
@pytest.mark.parametrize('alias_name', invalid_command_name)
def test_alias_create_invalid_name(base_app, alias_name, capsys):
out, err = run_cmd(base_app, 'alias create {} help'.format(alias_name))
assert "Invalid alias name" in err[0]
+
def test_alias_create_with_command_name(base_app):
out, err = run_cmd(base_app, 'alias create help stuff')
assert "Alias cannot have the same name as a command" in err[0]
+
def test_alias_create_with_macro_name(base_app):
macro = "my_macro"
run_cmd(base_app, 'macro create {} help'.format(macro))
out, err = run_cmd(base_app, 'alias create {} help'.format(macro))
assert "Alias cannot have the same name as a macro" in err[0]
+
def test_alias_that_resolves_into_comment(base_app):
# Create the alias
out, err = run_cmd(base_app, 'alias create fake ' + constants.COMMENT_CHAR + ' blah blah')
@@ -1673,11 +1827,13 @@ def test_alias_that_resolves_into_comment(base_app):
assert not out
assert not err
+
def test_alias_list_invalid_alias(base_app):
# Look up invalid alias
out, err = run_cmd(base_app, 'alias list invalid')
assert "Alias 'invalid' not found" in err[0]
+
def test_alias_delete(base_app):
# Create an alias
run_cmd(base_app, 'alias create fake run_pyscript')
@@ -1686,18 +1842,22 @@ def test_alias_delete(base_app):
out, err = run_cmd(base_app, 'alias delete fake')
assert out == normalize("Alias 'fake' deleted")
+
def test_alias_delete_all(base_app):
out, err = run_cmd(base_app, 'alias delete --all')
assert out == normalize("All aliases deleted")
+
def test_alias_delete_non_existing(base_app):
out, err = run_cmd(base_app, 'alias delete fake')
assert "Alias 'fake' does not exist" in err[0]
+
def test_alias_delete_no_name(base_app):
out, err = run_cmd(base_app, 'alias delete')
assert "Either --all or alias name(s)" in err[0]
+
def test_multiple_aliases(base_app):
alias1 = 'h1'
alias2 = 'h2'
@@ -1709,11 +1869,13 @@ def test_multiple_aliases(base_app):
out, err = run_cmd(base_app, alias2)
verify_help_text(base_app, out)
+
def test_macro_no_subcommand(base_app):
out, err = run_cmd(base_app, 'macro')
assert "Usage: macro [-h]" in err[0]
assert "Error: the following arguments are required: SUBCOMMAND" in err[1]
+
def test_macro_create(base_app):
# Create the macro
out, err = run_cmd(base_app, 'macro create fake run_pyscript')
@@ -1746,6 +1908,7 @@ def test_macro_create(base_app):
out, err = run_cmd(base_app, 'macro list --with_silent fake')
assert out == normalize('macro create --silent fake set')
+
def test_macro_create_with_quoted_tokens(base_app):
"""Demonstrate that quotes in macro value will be preserved"""
create_command = 'macro create fake help ">" "out file.txt" ";"'
@@ -1758,21 +1921,25 @@ def test_macro_create_with_quoted_tokens(base_app):
out, err = run_cmd(base_app, 'macro list fake')
assert out == normalize(create_command)
+
@pytest.mark.parametrize('macro_name', invalid_command_name)
def test_macro_create_invalid_name(base_app, macro_name):
out, err = run_cmd(base_app, 'macro create {} help'.format(macro_name))
assert "Invalid macro name" in err[0]
+
def test_macro_create_with_command_name(base_app):
out, err = run_cmd(base_app, 'macro create help stuff')
assert "Macro cannot have the same name as a command" in err[0]
+
def test_macro_create_with_alias_name(base_app):
macro = "my_macro"
run_cmd(base_app, 'alias create {} help'.format(macro))
out, err = run_cmd(base_app, 'macro create {} help'.format(macro))
assert "Macro cannot have the same name as an alias" in err[0]
+
def test_macro_create_with_args(base_app):
# Create the macro
out, err = run_cmd(base_app, 'macro create fake {1} {2}')
@@ -1782,6 +1949,7 @@ def test_macro_create_with_args(base_app):
out, err = run_cmd(base_app, 'fake help -v')
verify_help_text(base_app, out)
+
def test_macro_create_with_escaped_args(base_app):
# Create the macro
out, err = run_cmd(base_app, 'macro create fake help {{1}}')
@@ -1791,6 +1959,7 @@ def test_macro_create_with_escaped_args(base_app):
out, err = run_cmd(base_app, 'fake')
assert err[0].startswith('No help on {1}')
+
def test_macro_usage_with_missing_args(base_app):
# Create the macro
out, err = run_cmd(base_app, 'macro create fake help {1} {2}')
@@ -1800,6 +1969,7 @@ def test_macro_usage_with_missing_args(base_app):
out, err = run_cmd(base_app, 'fake arg1')
assert "expects at least 2 argument(s)" in err[0]
+
def test_macro_usage_with_exta_args(base_app):
# Create the macro
out, err = run_cmd(base_app, 'macro create fake help {1}')
@@ -1809,16 +1979,19 @@ def test_macro_usage_with_exta_args(base_app):
out, err = run_cmd(base_app, 'fake alias create')
assert "Usage: alias create" in out[0]
+
def test_macro_create_with_missing_arg_nums(base_app):
# Create the macro
out, err = run_cmd(base_app, 'macro create fake help {1} {3}')
assert "Not all numbers between 1 and 3" in err[0]
+
def test_macro_create_with_invalid_arg_num(base_app):
# Create the macro
out, err = run_cmd(base_app, 'macro create fake help {1} {-1} {0}')
assert "Argument numbers must be greater than 0" in err[0]
+
def test_macro_create_with_unicode_numbered_arg(base_app):
# Create the macro expecting 1 argument
out, err = run_cmd(base_app, 'macro create fake help {\N{ARABIC-INDIC DIGIT ONE}}')
@@ -1828,10 +2001,12 @@ def test_macro_create_with_unicode_numbered_arg(base_app):
out, err = run_cmd(base_app, 'fake')
assert "expects at least 1 argument(s)" in err[0]
+
def test_macro_create_with_missing_unicode_arg_nums(base_app):
out, err = run_cmd(base_app, 'macro create fake help {1} {\N{ARABIC-INDIC DIGIT THREE}}')
assert "Not all numbers between 1 and 3" in err[0]
+
def test_macro_that_resolves_into_comment(base_app):
# Create the macro
out, err = run_cmd(base_app, 'macro create fake {1} blah blah')
@@ -1842,11 +2017,13 @@ def test_macro_that_resolves_into_comment(base_app):
assert not out
assert not err
+
def test_macro_list_invalid_macro(base_app):
# Look up invalid macro
out, err = run_cmd(base_app, 'macro list invalid')
assert "Macro 'invalid' not found" in err[0]
+
def test_macro_delete(base_app):
# Create an macro
run_cmd(base_app, 'macro create fake run_pyscript')
@@ -1855,18 +2032,22 @@ def test_macro_delete(base_app):
out, err = run_cmd(base_app, 'macro delete fake')
assert out == normalize("Macro 'fake' deleted")
+
def test_macro_delete_all(base_app):
out, err = run_cmd(base_app, 'macro delete --all')
assert out == normalize("All macros deleted")
+
def test_macro_delete_non_existing(base_app):
out, err = run_cmd(base_app, 'macro delete fake')
assert "Macro 'fake' does not exist" in err[0]
+
def test_macro_delete_no_name(base_app):
out, err = run_cmd(base_app, 'macro delete')
assert "Either --all or macro name(s)" in err[0]
+
def test_multiple_macros(base_app):
macro1 = 'h1'
macro2 = 'h2'
@@ -1879,8 +2060,10 @@ def test_multiple_macros(base_app):
verify_help_text(base_app, out2)
assert len(out2) > len(out)
+
def test_nonexistent_macro(base_app):
from cmd2.parsing import StatementParser
+
exception = None
try:
@@ -1890,6 +2073,7 @@ def test_nonexistent_macro(base_app):
assert exception is not None
+
def test_perror_style(base_app, capsys):
msg = 'testing...'
end = '\n'
@@ -1898,6 +2082,7 @@ def test_perror_style(base_app, capsys):
out, err = capsys.readouterr()
assert err == ansi.style_error(msg) + end
+
def test_perror_no_style(base_app, capsys):
msg = 'testing...'
end = '\n'
@@ -1906,6 +2091,7 @@ def test_perror_no_style(base_app, capsys):
out, err = capsys.readouterr()
assert err == msg + end
+
def test_pwarning_style(base_app, capsys):
msg = 'testing...'
end = '\n'
@@ -1914,6 +2100,7 @@ def test_pwarning_style(base_app, capsys):
out, err = capsys.readouterr()
assert err == ansi.style_warning(msg) + end
+
def test_pwarning_no_style(base_app, capsys):
msg = 'testing...'
end = '\n'
@@ -1922,6 +2109,7 @@ def test_pwarning_no_style(base_app, capsys):
out, err = capsys.readouterr()
assert err == msg + end
+
def test_ppaged(outsim_app):
msg = 'testing...'
end = '\n'
@@ -1929,18 +2117,21 @@ def test_ppaged(outsim_app):
out = outsim_app.stdout.getvalue()
assert out == msg + end
+
def test_ppaged_blank(outsim_app):
msg = ''
outsim_app.ppaged(msg)
out = outsim_app.stdout.getvalue()
assert not out
+
def test_ppaged_none(outsim_app):
msg = None
outsim_app.ppaged(msg)
out = outsim_app.stdout.getvalue()
assert not out
+
def test_ppaged_strips_ansi_when_redirecting(outsim_app):
msg = 'testing...'
end = '\n'
@@ -1950,6 +2141,7 @@ def test_ppaged_strips_ansi_when_redirecting(outsim_app):
out = outsim_app.stdout.getvalue()
assert out == msg + end
+
def test_ppaged_strips_ansi_when_redirecting_if_always(outsim_app):
msg = 'testing...'
end = '\n'
@@ -1960,6 +2152,7 @@ def test_ppaged_strips_ansi_when_redirecting_if_always(outsim_app):
out = outsim_app.stdout.getvalue()
assert out == colored_msg + end
+
# we override cmd.parseline() so we always get consistent
# command parsing by parent methods we don't override
# don't need to test all the parsing logic here, because
@@ -1971,6 +2164,7 @@ def test_parseline_empty(base_app):
assert not args
assert not line
+
def test_parseline(base_app):
statement = " command with 'partially completed quotes "
command, args, line = base_app.parseline(statement)
@@ -1986,6 +2180,7 @@ def test_onecmd_raw_str_continue(outsim_app):
assert not stop
verify_help_text(outsim_app, out)
+
def test_onecmd_raw_str_quit(outsim_app):
line = "quit"
stop = outsim_app.onecmd(line)
@@ -1993,6 +2188,7 @@ def test_onecmd_raw_str_quit(outsim_app):
assert stop
assert out == ''
+
def test_onecmd_add_to_history(outsim_app):
line = "help"
saved_hist_len = len(outsim_app.history)
@@ -2009,18 +2205,35 @@ def test_onecmd_add_to_history(outsim_app):
new_hist_len = len(outsim_app.history)
assert new_hist_len == saved_hist_len
+
def test_get_all_commands(base_app):
# Verify that the base app has the expected commands
commands = base_app.get_all_commands()
- expected_commands = ['_relative_run_script', 'alias', 'edit', 'eof', 'help', 'history', 'macro',
- 'py', 'quit', 'run_pyscript', 'run_script', 'set', 'shell', 'shortcuts']
+ expected_commands = [
+ '_relative_run_script',
+ 'alias',
+ 'edit',
+ 'eof',
+ 'help',
+ 'history',
+ 'macro',
+ 'py',
+ 'quit',
+ 'run_pyscript',
+ 'run_script',
+ 'set',
+ 'shell',
+ 'shortcuts',
+ ]
assert commands == expected_commands
+
def test_get_help_topics(base_app):
# Verify that the base app has no additional help_foo methods
custom_help = base_app.get_help_topics()
assert len(custom_help) == 0
+
def test_get_help_topics_hidden():
# Verify get_help_topics() filters out hidden commands
class TestApp(cmd2.Cmd):
@@ -2039,6 +2252,7 @@ def test_get_help_topics_hidden():
app.hidden_commands.append('my_cmd')
assert 'my_cmd' not in app.get_help_topics()
+
class ReplWithExitCode(cmd2.Cmd):
""" Example cmd2 application where we can specify an exit code when existing."""
@@ -2068,12 +2282,14 @@ Usage: exit [exit_code]
"""Hook method executed once when the cmdloop() method is about to return."""
self.poutput('exiting with code: {}'.format(self.exit_code))
+
@pytest.fixture
def exit_code_repl():
app = ReplWithExitCode()
app.stdout = utils.StdSim(app.stdout)
return app
+
def test_exit_code_default(exit_code_repl):
app = exit_code_repl
app.use_rawinput = True
@@ -2089,6 +2305,7 @@ def test_exit_code_default(exit_code_repl):
out = app.stdout.getvalue()
assert out == expected
+
def test_exit_code_nonzero(exit_code_repl):
app = exit_code_repl
app.use_rawinput = True
@@ -2118,6 +2335,7 @@ class AnsiApp(cmd2.Cmd):
# perror uses colors by default
self.perror(args)
+
def test_ansi_pouterr_always_tty(mocker, capsys):
app = AnsiApp()
ansi.allow_style = ansi.STYLE_ALWAYS
@@ -2140,6 +2358,7 @@ def test_ansi_pouterr_always_tty(mocker, capsys):
assert len(err) > len('oopsie\n')
assert 'oopsie' in err
+
def test_ansi_pouterr_always_notty(mocker, capsys):
app = AnsiApp()
ansi.allow_style = ansi.STYLE_ALWAYS
@@ -2162,6 +2381,7 @@ def test_ansi_pouterr_always_notty(mocker, capsys):
assert len(err) > len('oopsie\n')
assert 'oopsie' in err
+
def test_ansi_terminal_tty(mocker, capsys):
app = AnsiApp()
ansi.allow_style = ansi.STYLE_TERMINAL
@@ -2183,6 +2403,7 @@ def test_ansi_terminal_tty(mocker, capsys):
assert len(err) > len('oopsie\n')
assert 'oopsie' in err
+
def test_ansi_terminal_notty(mocker, capsys):
app = AnsiApp()
ansi.allow_style = ansi.STYLE_TERMINAL
@@ -2197,6 +2418,7 @@ def test_ansi_terminal_notty(mocker, capsys):
out, err = capsys.readouterr()
assert out == err == 'oopsie\n'
+
def test_ansi_never_tty(mocker, capsys):
app = AnsiApp()
ansi.allow_style = ansi.STYLE_NEVER
@@ -2211,6 +2433,7 @@ def test_ansi_never_tty(mocker, capsys):
out, err = capsys.readouterr()
assert out == err == 'oopsie\n'
+
def test_ansi_never_notty(mocker, capsys):
app = AnsiApp()
ansi.allow_style = ansi.STYLE_NEVER
@@ -2228,6 +2451,7 @@ def test_ansi_never_notty(mocker, capsys):
class DisableCommandsApp(cmd2.Cmd):
"""Class for disabling commands"""
+
category_name = "Test Category"
def __init__(self, *args, **kwargs):
@@ -2346,6 +2570,7 @@ def test_disable_and_enable_category(disable_commands_app):
help_topics = disable_commands_app.get_help_topics()
assert 'has_helper_funcs' in help_topics
+
def test_enable_enabled_command(disable_commands_app):
# Test enabling a command that is not disabled
saved_len = len(disable_commands_app.disabled_commands)
@@ -2354,10 +2579,12 @@ def test_enable_enabled_command(disable_commands_app):
# The number of disabled_commands should not have changed
assert saved_len == len(disable_commands_app.disabled_commands)
+
def test_disable_fake_command(disable_commands_app):
with pytest.raises(AttributeError):
disable_commands_app.disable_command('fake', 'fake message')
+
def test_disable_command_twice(disable_commands_app):
saved_len = len(disable_commands_app.disabled_commands)
message_to_print = 'These commands are currently disabled'
@@ -2373,6 +2600,7 @@ def test_disable_command_twice(disable_commands_app):
new_len = len(disable_commands_app.disabled_commands)
assert saved_len == new_len
+
def test_disabled_command_not_in_history(disable_commands_app):
message_to_print = 'These commands are currently disabled'
disable_commands_app.disable_command('has_helper_funcs', message_to_print)
@@ -2381,6 +2609,7 @@ def test_disabled_command_not_in_history(disable_commands_app):
run_cmd(disable_commands_app, 'has_helper_funcs')
assert saved_len == len(disable_commands_app.history)
+
def test_disabled_message_command_name(disable_commands_app):
message_to_print = '{} is currently disabled'.format(COMMAND_NAME)
disable_commands_app.disable_command('has_helper_funcs', message_to_print)
diff --git a/tests/test_completion.py b/tests/test_completion.py
index db243f48..785bb49d 100755
--- a/tests/test_completion.py
+++ b/tests/test_completion.py
@@ -28,44 +28,42 @@ from .conftest import complete_tester, normalize, run_cmd
# List of strings used with completion functions
food_item_strs = ['Pizza', 'Ham', 'Ham Sandwich', 'Potato', 'Cheese "Pizza"']
sport_item_strs = ['Bat', 'Basket', 'Basketball', 'Football', 'Space Ball']
-delimited_strs = \
- [
- '/home/user/file.txt',
- '/home/user/file space.txt',
- '/home/user/prog.c',
- '/home/other user/maps',
- '/home/other user/tests'
- ]
+delimited_strs = [
+ '/home/user/file.txt',
+ '/home/user/file space.txt',
+ '/home/user/prog.c',
+ '/home/other user/maps',
+ '/home/other user/tests',
+]
# Dictionary used with flag based completion functions
-flag_dict = \
- {
- # Tab complete food items after -f and --food flag in command line
- '-f': food_item_strs,
- '--food': food_item_strs,
-
- # Tab complete sport items after -s and --sport flag in command line
- '-s': sport_item_strs,
- '--sport': sport_item_strs,
- }
+flag_dict = {
+ # Tab complete food items after -f and --food flag in command line
+ '-f': food_item_strs,
+ '--food': food_item_strs,
+ # Tab complete sport items after -s and --sport flag in command line
+ '-s': sport_item_strs,
+ '--sport': sport_item_strs,
+}
# Dictionary used with index based completion functions
-index_dict = \
- {
- 1: food_item_strs, # Tab complete food items at index 1 in command line
- 2: sport_item_strs, # Tab complete sport items at index 2 in command line
- }
+index_dict = {
+ 1: food_item_strs, # Tab complete food items at index 1 in command line
+ 2: sport_item_strs, # Tab complete sport items at index 2 in command line
+}
class CompletionsExample(cmd2.Cmd):
"""
Example cmd2 application used to exercise tab completion tests
"""
+
def __init__(self):
cmd2.Cmd.__init__(self, multiline_commands=['test_multiline'])
self.foo = 'bar'
- self.add_settable(utils.Settable('foo', str, description="a settable param",
- completer_method=CompletionsExample.complete_foo_val))
+ self.add_settable(
+ utils.Settable('foo', str, description="a settable param", completer_method=CompletionsExample.complete_foo_val)
+ )
def do_test_basic(self, args):
pass
@@ -130,6 +128,7 @@ def test_cmd2_command_completion_single(cmd2_app):
begidx = endidx - len(text)
assert cmd2_app.completenames(text, line, begidx, endidx) == ['help']
+
def test_complete_command_single(cmd2_app):
text = 'he'
line = text
@@ -139,6 +138,7 @@ def test_complete_command_single(cmd2_app):
first_match = complete_tester(text, line, begidx, endidx, cmd2_app)
assert first_match is not None and cmd2_app.completion_matches == ['help ']
+
def test_complete_empty_arg(cmd2_app):
text = ''
line = 'help {}'.format(text)
@@ -150,6 +150,7 @@ def test_complete_empty_arg(cmd2_app):
assert first_match is not None and cmd2_app.completion_matches == expected
+
def test_complete_bogus_command(cmd2_app):
text = ''
line = 'fizbuzz {}'.format(text)
@@ -160,6 +161,7 @@ def test_complete_bogus_command(cmd2_app):
first_match = complete_tester(text, line, begidx, endidx, cmd2_app)
assert first_match is not None and cmd2_app.completion_matches == expected
+
def test_complete_exception(cmd2_app, capsys):
text = ''
line = 'test_raise_exception {}'.format(text)
@@ -172,6 +174,7 @@ def test_complete_exception(cmd2_app, capsys):
assert first_match is None
assert "IndexError" in err
+
def test_complete_macro(base_app, request):
# Create the macro
out, err = run_cmd(base_app, 'macro create fake run_pyscript {1}')
@@ -217,6 +220,7 @@ def test_cmd2_command_completion_multiple(cmd2_app):
begidx = endidx - len(text)
assert cmd2_app.completenames(text, line, begidx, endidx) == ['help', 'history']
+
def test_cmd2_command_completion_nomatch(cmd2_app):
text = 'fakecommand'
line = text
@@ -236,6 +240,7 @@ def test_cmd2_help_completion_single(cmd2_app):
# It is at end of line, so extra space is present
assert first_match is not None and cmd2_app.completion_matches == ['help ']
+
def test_cmd2_help_completion_multiple(cmd2_app):
text = 'h'
line = 'help {}'.format(text)
@@ -274,9 +279,8 @@ def test_shell_command_completion_shortcut(cmd2_app):
begidx = 0
first_match = complete_tester(text, line, begidx, endidx, cmd2_app)
- assert first_match is not None and \
- cmd2_app.completion_matches == expected and \
- cmd2_app.display_matches == expected_display
+ assert first_match is not None and cmd2_app.completion_matches == expected and cmd2_app.display_matches == expected_display
+
def test_shell_command_completion_doesnt_match_wildcards(cmd2_app):
if sys.platform == "win32":
@@ -291,6 +295,7 @@ def test_shell_command_completion_doesnt_match_wildcards(cmd2_app):
first_match = complete_tester(text, line, begidx, endidx, cmd2_app)
assert first_match is None
+
def test_shell_command_completion_multiple(cmd2_app):
if sys.platform == "win32":
text = 'c'
@@ -306,6 +311,7 @@ def test_shell_command_completion_multiple(cmd2_app):
first_match = complete_tester(text, line, begidx, endidx, cmd2_app)
assert first_match is not None and expected in cmd2_app.completion_matches
+
def test_shell_command_completion_nomatch(cmd2_app):
text = 'zzzz'
line = 'shell {}'.format(text)
@@ -315,6 +321,7 @@ def test_shell_command_completion_nomatch(cmd2_app):
first_match = complete_tester(text, line, begidx, endidx, cmd2_app)
assert first_match is None
+
def test_shell_command_completion_doesnt_complete_when_just_shell(cmd2_app):
text = ''
line = 'shell {}'.format(text)
@@ -324,6 +331,7 @@ def test_shell_command_completion_doesnt_complete_when_just_shell(cmd2_app):
first_match = complete_tester(text, line, begidx, endidx, cmd2_app)
assert first_match is None
+
def test_shell_command_completion_does_path_completion_when_after_command(cmd2_app, request):
test_dir = os.path.dirname(request.module.__file__)
@@ -336,6 +344,7 @@ def test_shell_command_completion_does_path_completion_when_after_command(cmd2_a
first_match = complete_tester(text, line, begidx, endidx, cmd2_app)
assert first_match is not None and cmd2_app.completion_matches == [text + '.py ']
+
def test_shell_commmand_complete_in_path(cmd2_app, request):
test_dir = os.path.dirname(request.module.__file__)
@@ -363,6 +372,7 @@ def test_path_completion_single_end(cmd2_app, request):
assert cmd2_app.path_complete(text, line, begidx, endidx) == [text + '.py']
+
def test_path_completion_multiple(cmd2_app, request):
test_dir = os.path.dirname(request.module.__file__)
@@ -376,6 +386,7 @@ def test_path_completion_multiple(cmd2_app, request):
expected = [text + 'cript.py', text + 'cript.txt', text + 'cripts' + os.path.sep]
assert matches == expected
+
def test_path_completion_nomatch(cmd2_app, request):
test_dir = os.path.dirname(request.module.__file__)
@@ -431,6 +442,7 @@ def test_path_completion_no_text(cmd2_app):
assert completions_no_text == completions_cwd
assert completions_cwd
+
def test_path_completion_no_path(cmd2_app):
# Run path complete with search text that isn't preceded by a path. This should use CWD as the path.
text = 's'
@@ -471,6 +483,7 @@ def test_path_completion_cwd_is_root_dir(cmd2_app):
# Restore CWD
os.chdir(cwd)
+
def test_path_completion_doesnt_match_wildcards(cmd2_app, request):
test_dir = os.path.dirname(request.module.__file__)
@@ -483,10 +496,14 @@ def test_path_completion_doesnt_match_wildcards(cmd2_app, request):
# Currently path completion doesn't accept wildcards, so will always return empty results
assert cmd2_app.path_complete(text, line, begidx, endidx) == []
-@pytest.mark.skipif(sys.platform == 'win32', reason="getpass.getuser() does not work on Windows in AppVeyor because "
- "no user name environment variables are set")
+
+@pytest.mark.skipif(
+ sys.platform == 'win32',
+ reason="getpass.getuser() does not work on Windows in AppVeyor because " "no user name environment variables are set",
+)
def test_path_completion_complete_user(cmd2_app):
import getpass
+
user = getpass.getuser()
text = '~{}'.format(user)
@@ -498,6 +515,7 @@ def test_path_completion_complete_user(cmd2_app):
expected = text + os.path.sep
assert expected in completions
+
def test_path_completion_user_path_expansion(cmd2_app):
# Run path with a tilde and a slash
if sys.platform.startswith('win'):
@@ -510,8 +528,7 @@ def test_path_completion_user_path_expansion(cmd2_app):
line = 'shell {} {}'.format(cmd, text)
endidx = len(line)
begidx = endidx - len(text)
- completions_tilde_slash = [match.replace(text, '', 1) for match in cmd2_app.path_complete(text, line,
- begidx, endidx)]
+ completions_tilde_slash = [match.replace(text, '', 1) for match in cmd2_app.path_complete(text, line, begidx, endidx)]
# Run path complete on the user's home directory
text = os.path.expanduser('~') + os.path.sep
@@ -522,6 +539,7 @@ def test_path_completion_user_path_expansion(cmd2_app):
assert completions_tilde_slash == completions_home
+
def test_path_completion_directories_only(cmd2_app, request):
test_dir = os.path.dirname(request.module.__file__)
@@ -535,6 +553,7 @@ def test_path_completion_directories_only(cmd2_app, request):
assert cmd2_app.path_complete(text, line, begidx, endidx, path_filter=os.path.isdir) == expected
+
def test_basic_completion_single(cmd2_app):
text = 'Pi'
line = 'list_food -f {}'.format(text)
@@ -543,6 +562,7 @@ def test_basic_completion_single(cmd2_app):
assert utils.basic_complete(text, line, begidx, endidx, food_item_strs) == ['Pizza']
+
def test_basic_completion_multiple(cmd2_app):
text = ''
line = 'list_food -f {}'.format(text)
@@ -552,6 +572,7 @@ def test_basic_completion_multiple(cmd2_app):
matches = sorted(utils.basic_complete(text, line, begidx, endidx, food_item_strs))
assert matches == sorted(food_item_strs)
+
def test_basic_completion_nomatch(cmd2_app):
text = 'q'
line = 'list_food -f {}'.format(text)
@@ -560,6 +581,7 @@ def test_basic_completion_nomatch(cmd2_app):
assert utils.basic_complete(text, line, begidx, endidx, food_item_strs) == []
+
def test_delimiter_completion(cmd2_app):
text = '/home/'
line = 'run_script {}'.format(text)
@@ -574,6 +596,7 @@ def test_delimiter_completion(cmd2_app):
assert display_list == ['other user', 'user']
+
def test_flag_based_completion_single(cmd2_app):
text = 'Pi'
line = 'list_food -f {}'.format(text)
@@ -582,6 +605,7 @@ def test_flag_based_completion_single(cmd2_app):
assert cmd2_app.flag_based_complete(text, line, begidx, endidx, flag_dict) == ['Pizza']
+
def test_flag_based_completion_multiple(cmd2_app):
text = ''
line = 'list_food -f {}'.format(text)
@@ -591,6 +615,7 @@ def test_flag_based_completion_multiple(cmd2_app):
matches = sorted(cmd2_app.flag_based_complete(text, line, begidx, endidx, flag_dict))
assert matches == sorted(food_item_strs)
+
def test_flag_based_completion_nomatch(cmd2_app):
text = 'q'
line = 'list_food -f {}'.format(text)
@@ -599,6 +624,7 @@ def test_flag_based_completion_nomatch(cmd2_app):
assert cmd2_app.flag_based_complete(text, line, begidx, endidx, flag_dict) == []
+
def test_flag_based_default_completer(cmd2_app, request):
test_dir = os.path.dirname(request.module.__file__)
@@ -608,8 +634,10 @@ def test_flag_based_default_completer(cmd2_app, request):
endidx = len(line)
begidx = endidx - len(text)
- assert cmd2_app.flag_based_complete(text, line, begidx, endidx,
- flag_dict, all_else=cmd2_app.path_complete) == [text + 'onftest.py']
+ assert cmd2_app.flag_based_complete(text, line, begidx, endidx, flag_dict, all_else=cmd2_app.path_complete) == [
+ text + 'onftest.py'
+ ]
+
def test_flag_based_callable_completer(cmd2_app, request):
test_dir = os.path.dirname(request.module.__file__)
@@ -621,8 +649,7 @@ def test_flag_based_callable_completer(cmd2_app, request):
begidx = endidx - len(text)
flag_dict['-o'] = cmd2_app.path_complete
- assert cmd2_app.flag_based_complete(text, line, begidx, endidx,
- flag_dict) == [text + 'onftest.py']
+ assert cmd2_app.flag_based_complete(text, line, begidx, endidx, flag_dict) == [text + 'onftest.py']
def test_index_based_completion_single(cmd2_app):
@@ -633,6 +660,7 @@ def test_index_based_completion_single(cmd2_app):
assert cmd2_app.index_based_complete(text, line, begidx, endidx, index_dict) == ['Football']
+
def test_index_based_completion_multiple(cmd2_app):
text = ''
line = 'command Pizza {}'.format(text)
@@ -642,6 +670,7 @@ def test_index_based_completion_multiple(cmd2_app):
matches = sorted(cmd2_app.index_based_complete(text, line, begidx, endidx, index_dict))
assert matches == sorted(sport_item_strs)
+
def test_index_based_completion_nomatch(cmd2_app):
text = 'q'
line = 'command {}'.format(text)
@@ -649,6 +678,7 @@ def test_index_based_completion_nomatch(cmd2_app):
begidx = endidx - len(text)
assert cmd2_app.index_based_complete(text, line, begidx, endidx, index_dict) == []
+
def test_index_based_default_completer(cmd2_app, request):
test_dir = os.path.dirname(request.module.__file__)
@@ -658,8 +688,10 @@ def test_index_based_default_completer(cmd2_app, request):
endidx = len(line)
begidx = endidx - len(text)
- assert cmd2_app.index_based_complete(text, line, begidx, endidx,
- index_dict, all_else=cmd2_app.path_complete) == [text + 'onftest.py']
+ assert cmd2_app.index_based_complete(text, line, begidx, endidx, index_dict, all_else=cmd2_app.path_complete) == [
+ text + 'onftest.py'
+ ]
+
def test_index_based_callable_completer(cmd2_app, request):
test_dir = os.path.dirname(request.module.__file__)
@@ -687,6 +719,7 @@ def test_tokens_for_completion_quoted(cmd2_app):
assert expected_tokens == tokens
assert expected_raw_tokens == raw_tokens
+
def test_tokens_for_completion_unclosed_quote(cmd2_app):
text = 'Pi'
line = 'list_food "{}'.format(text)
@@ -700,6 +733,7 @@ def test_tokens_for_completion_unclosed_quote(cmd2_app):
assert expected_tokens == tokens
assert expected_raw_tokens == raw_tokens
+
def test_tokens_for_completion_punctuation(cmd2_app):
"""Test that redirectors and terminators are word delimiters"""
text = 'file'
@@ -714,6 +748,7 @@ def test_tokens_for_completion_punctuation(cmd2_app):
assert expected_tokens == tokens
assert expected_raw_tokens == raw_tokens
+
def test_tokens_for_completion_quoted_punctuation(cmd2_app):
"""Test that quoted punctuation characters are not word delimiters"""
text = '>file'
@@ -728,6 +763,7 @@ def test_tokens_for_completion_quoted_punctuation(cmd2_app):
assert expected_tokens == tokens
assert expected_raw_tokens == raw_tokens
+
def test_add_opening_quote_basic_no_text(cmd2_app):
text = ''
line = 'test_basic {}'.format(text)
@@ -736,8 +772,8 @@ def test_add_opening_quote_basic_no_text(cmd2_app):
# The whole list will be returned with no opening quotes added
first_match = complete_tester(text, line, begidx, endidx, cmd2_app)
- assert first_match is not None and cmd2_app.completion_matches == sorted(food_item_strs,
- key=cmd2_app.default_sort_key)
+ assert first_match is not None and cmd2_app.completion_matches == sorted(food_item_strs, key=cmd2_app.default_sort_key)
+
def test_add_opening_quote_basic_nothing_added(cmd2_app):
text = 'P'
@@ -748,6 +784,7 @@ def test_add_opening_quote_basic_nothing_added(cmd2_app):
first_match = complete_tester(text, line, begidx, endidx, cmd2_app)
assert first_match is not None and cmd2_app.completion_matches == ['Pizza', 'Potato']
+
def test_add_opening_quote_basic_quote_added(cmd2_app):
text = 'Ha'
line = 'test_basic {}'.format(text)
@@ -758,6 +795,7 @@ def test_add_opening_quote_basic_quote_added(cmd2_app):
first_match = complete_tester(text, line, begidx, endidx, cmd2_app)
assert first_match is not None and cmd2_app.completion_matches == expected
+
def test_add_opening_quote_basic_single_quote_added(cmd2_app):
text = 'Ch'
line = 'test_basic {}'.format(text)
@@ -768,6 +806,7 @@ def test_add_opening_quote_basic_single_quote_added(cmd2_app):
first_match = complete_tester(text, line, begidx, endidx, cmd2_app)
assert first_match is not None and cmd2_app.completion_matches == expected
+
def test_add_opening_quote_basic_text_is_common_prefix(cmd2_app):
# This tests when the text entered is the same as the common prefix of the matches
text = 'Ham'
@@ -779,6 +818,7 @@ def test_add_opening_quote_basic_text_is_common_prefix(cmd2_app):
first_match = complete_tester(text, line, begidx, endidx, cmd2_app)
assert first_match is not None and cmd2_app.completion_matches == expected
+
def test_add_opening_quote_delimited_no_text(cmd2_app):
text = ''
line = 'test_delimited {}'.format(text)
@@ -787,8 +827,8 @@ def test_add_opening_quote_delimited_no_text(cmd2_app):
# The whole list will be returned with no opening quotes added
first_match = complete_tester(text, line, begidx, endidx, cmd2_app)
- assert first_match is not None and cmd2_app.completion_matches == sorted(delimited_strs,
- key=cmd2_app.default_sort_key)
+ assert first_match is not None and cmd2_app.completion_matches == sorted(delimited_strs, key=cmd2_app.default_sort_key)
+
def test_add_opening_quote_delimited_nothing_added(cmd2_app):
text = '/ho'
@@ -800,9 +840,12 @@ def test_add_opening_quote_delimited_nothing_added(cmd2_app):
expected_display = sorted(['other user', 'user'], key=cmd2_app.default_sort_key)
first_match = complete_tester(text, line, begidx, endidx, cmd2_app)
- assert first_match is not None and \
- cmd2_app.completion_matches == expected_matches and \
- cmd2_app.display_matches == expected_display
+ assert (
+ first_match is not None
+ and cmd2_app.completion_matches == expected_matches
+ and cmd2_app.display_matches == expected_display
+ )
+
def test_add_opening_quote_delimited_quote_added(cmd2_app):
text = '/home/user/fi'
@@ -814,9 +857,12 @@ def test_add_opening_quote_delimited_quote_added(cmd2_app):
expected_display = sorted(['file.txt', 'file space.txt'], key=cmd2_app.default_sort_key)
first_match = complete_tester(text, line, begidx, endidx, cmd2_app)
- assert first_match is not None and \
- os.path.commonprefix(cmd2_app.completion_matches) == expected_common_prefix and \
- cmd2_app.display_matches == expected_display
+ assert (
+ first_match is not None
+ and os.path.commonprefix(cmd2_app.completion_matches) == expected_common_prefix
+ and cmd2_app.display_matches == expected_display
+ )
+
def test_add_opening_quote_delimited_text_is_common_prefix(cmd2_app):
# This tests when the text entered is the same as the common prefix of the matches
@@ -829,9 +875,12 @@ def test_add_opening_quote_delimited_text_is_common_prefix(cmd2_app):
expected_display = sorted(['file.txt', 'file space.txt'], key=cmd2_app.default_sort_key)
first_match = complete_tester(text, line, begidx, endidx, cmd2_app)
- assert first_match is not None and \
- os.path.commonprefix(cmd2_app.completion_matches) == expected_common_prefix and \
- cmd2_app.display_matches == expected_display
+ assert (
+ first_match is not None
+ and os.path.commonprefix(cmd2_app.completion_matches) == expected_common_prefix
+ and cmd2_app.display_matches == expected_display
+ )
+
def test_add_opening_quote_delimited_space_in_prefix(cmd2_app):
# This test when a space appears before the part of the string that is the display match
@@ -844,9 +893,12 @@ def test_add_opening_quote_delimited_space_in_prefix(cmd2_app):
expected_display = ['maps', 'tests']
first_match = complete_tester(text, line, begidx, endidx, cmd2_app)
- assert first_match is not None and \
- os.path.commonprefix(cmd2_app.completion_matches) == expected_common_prefix and \
- cmd2_app.display_matches == expected_display
+ assert (
+ first_match is not None
+ and os.path.commonprefix(cmd2_app.completion_matches) == expected_common_prefix
+ and cmd2_app.display_matches == expected_display
+ )
+
def test_no_completer(cmd2_app):
text = ''
@@ -858,6 +910,7 @@ def test_no_completer(cmd2_app):
first_match = complete_tester(text, line, begidx, endidx, cmd2_app)
assert first_match is not None and cmd2_app.completion_matches == expected
+
def test_quote_as_command(cmd2_app):
text = ''
line = '" {}'.format(text)
@@ -898,36 +951,39 @@ def test_complete_multiline_on_multiple_lines(cmd2_app):
# Used by redirect_complete tests
class RedirCompType(enum.Enum):
- SHELL_CMD = 1,
- PATH = 2,
- DEFAULT = 3,
+ SHELL_CMD = (1,)
+ PATH = (2,)
+ DEFAULT = (3,)
NONE = 4
-@pytest.mark.parametrize('line, comp_type', [
- ('fake', RedirCompType.DEFAULT),
- ('fake arg', RedirCompType.DEFAULT),
- ('fake |', RedirCompType.SHELL_CMD),
- ('fake | grep', RedirCompType.PATH),
- ('fake | grep arg', RedirCompType.PATH),
- ('fake | grep >', RedirCompType.PATH),
- ('fake | grep > >', RedirCompType.NONE),
- ('fake | grep > file', RedirCompType.NONE),
- ('fake | grep > file >', RedirCompType.NONE),
- ('fake | grep > file |', RedirCompType.SHELL_CMD),
- ('fake | grep > file | grep', RedirCompType.PATH),
- ('fake | |', RedirCompType.NONE),
- ('fake | >', RedirCompType.NONE),
- ('fake >', RedirCompType.PATH),
- ('fake >>', RedirCompType.PATH),
- ('fake > >', RedirCompType.NONE),
- ('fake > |', RedirCompType.SHELL_CMD),
- ('fake >> file |', RedirCompType.SHELL_CMD),
- ('fake >> file | grep', RedirCompType.PATH),
- ('fake > file', RedirCompType.NONE),
- ('fake > file >', RedirCompType.NONE),
- ('fake > file >>', RedirCompType.NONE),
-])
+@pytest.mark.parametrize(
+ 'line, comp_type',
+ [
+ ('fake', RedirCompType.DEFAULT),
+ ('fake arg', RedirCompType.DEFAULT),
+ ('fake |', RedirCompType.SHELL_CMD),
+ ('fake | grep', RedirCompType.PATH),
+ ('fake | grep arg', RedirCompType.PATH),
+ ('fake | grep >', RedirCompType.PATH),
+ ('fake | grep > >', RedirCompType.NONE),
+ ('fake | grep > file', RedirCompType.NONE),
+ ('fake | grep > file >', RedirCompType.NONE),
+ ('fake | grep > file |', RedirCompType.SHELL_CMD),
+ ('fake | grep > file | grep', RedirCompType.PATH),
+ ('fake | |', RedirCompType.NONE),
+ ('fake | >', RedirCompType.NONE),
+ ('fake >', RedirCompType.PATH),
+ ('fake >>', RedirCompType.PATH),
+ ('fake > >', RedirCompType.NONE),
+ ('fake > |', RedirCompType.SHELL_CMD),
+ ('fake >> file |', RedirCompType.SHELL_CMD),
+ ('fake >> file | grep', RedirCompType.PATH),
+ ('fake > file', RedirCompType.NONE),
+ ('fake > file >', RedirCompType.NONE),
+ ('fake > file >>', RedirCompType.NONE),
+ ],
+)
def test_redirect_complete(cmd2_app, monkeypatch, line, comp_type):
# Test both cases of allow_redirection
cmd2_app.allow_redirection = True
@@ -974,6 +1030,7 @@ def test_complete_set_value(cmd2_app):
assert first_match == "SUCCESS "
assert cmd2_app.completion_hint == "Hint:\n value a settable param\n"
+
def test_complete_set_value_invalid_settable(cmd2_app, capsys):
text = ''
line = 'set fake {}'.format(text)
@@ -986,12 +1043,14 @@ def test_complete_set_value_invalid_settable(cmd2_app, capsys):
out, err = capsys.readouterr()
assert "fake is not a settable parameter" in out
+
@pytest.fixture
def sc_app():
c = SubcommandsExample()
c.stdout = utils.StdSim(c.stdout)
return c
+
def test_cmd2_subcommand_completion_single_end(sc_app):
text = 'f'
line = 'base {}'.format(text)
@@ -1003,6 +1062,7 @@ def test_cmd2_subcommand_completion_single_end(sc_app):
# It is at end of line, so extra space is present
assert first_match is not None and sc_app.completion_matches == ['foo ']
+
def test_cmd2_subcommand_completion_multiple(sc_app):
text = ''
line = 'base {}'.format(text)
@@ -1012,6 +1072,7 @@ def test_cmd2_subcommand_completion_multiple(sc_app):
first_match = complete_tester(text, line, begidx, endidx, sc_app)
assert first_match is not None and sc_app.completion_matches == ['bar', 'foo', 'sport']
+
def test_cmd2_subcommand_completion_nomatch(sc_app):
text = 'z'
line = 'base {}'.format(text)
@@ -1033,6 +1094,7 @@ def test_help_subcommand_completion_single(sc_app):
# It is at end of line, so extra space is present
assert first_match is not None and sc_app.completion_matches == ['base ']
+
def test_help_subcommand_completion_multiple(sc_app):
text = ''
line = 'help base {}'.format(text)
@@ -1052,6 +1114,7 @@ def test_help_subcommand_completion_nomatch(sc_app):
first_match = complete_tester(text, line, begidx, endidx, sc_app)
assert first_match is None
+
def test_subcommand_tab_completion(sc_app):
# This makes sure the correct completer for the sport subcommand is called
text = 'Foot'
@@ -1085,9 +1148,8 @@ def test_subcommand_tab_completion_space_in_text(sc_app):
first_match = complete_tester(text, line, begidx, endidx, sc_app)
- assert first_match is not None and \
- sc_app.completion_matches == ['Ball" '] and \
- sc_app.display_matches == ['Space Ball']
+ assert first_match is not None and sc_app.completion_matches == ['Ball" '] and sc_app.display_matches == ['Space Ball']
+
####################################################
@@ -1207,6 +1269,7 @@ def test_help_subcommand_completion_multiple_scu(scu_app):
first_match = complete_tester(text, line, begidx, endidx, scu_app)
assert first_match is not None and scu_app.completion_matches == ['bar', 'foo', 'sport']
+
def test_help_subcommand_completion_with_flags_before_command(scu_app):
text = ''
line = 'help -h -v base {}'.format(text)
@@ -1216,6 +1279,7 @@ def test_help_subcommand_completion_with_flags_before_command(scu_app):
first_match = complete_tester(text, line, begidx, endidx, scu_app)
assert first_match is not None and scu_app.completion_matches == ['bar', 'foo', 'sport']
+
def test_complete_help_subcommands_with_blank_command(scu_app):
text = ''
line = 'help "" {}'.format(text)
@@ -1269,6 +1333,4 @@ def test_subcommand_tab_completion_space_in_text_scu(scu_app):
first_match = complete_tester(text, line, begidx, endidx, scu_app)
- assert first_match is not None and \
- scu_app.completion_matches == ['Ball" '] and \
- scu_app.display_matches == ['Space Ball']
+ assert first_match is not None and scu_app.completion_matches == ['Ball" '] and scu_app.display_matches == ['Space Ball']
diff --git a/tests/test_history.py b/tests/test_history.py
index 6fa16ad8..cba6f3ce 100755
--- a/tests/test_history.py
+++ b/tests/test_history.py
@@ -9,6 +9,7 @@ import tempfile
import pytest
import cmd2
+
# Python 3.5 had some regressions in the unitest.mock module, so use
# 3rd party mock if available
from cmd2.parsing import StatementParser
@@ -21,44 +22,57 @@ except ImportError:
from unittest import mock
-
#
# readline tests
#
def test_readline_remove_history_item(base_app):
from cmd2.rl_utils import readline
+
assert readline.get_current_history_length() == 0
readline.add_history('this is a test')
assert readline.get_current_history_length() == 1
readline.remove_history_item(0)
assert readline.get_current_history_length() == 0
+
#
# test History() class
#
@pytest.fixture
def hist():
- from cmd2.parsing import Statement
from cmd2.cmd2 import History, HistoryItem
- h = History([HistoryItem(Statement('', raw='first'), 1),
- HistoryItem(Statement('', raw='second'), 2),
- HistoryItem(Statement('', raw='third'), 3),
- HistoryItem(Statement('', raw='fourth'),4)])
+ from cmd2.parsing import Statement
+
+ h = History(
+ [
+ HistoryItem(Statement('', raw='first'), 1),
+ HistoryItem(Statement('', raw='second'), 2),
+ HistoryItem(Statement('', raw='third'), 3),
+ HistoryItem(Statement('', raw='fourth'), 4),
+ ]
+ )
return h
+
@pytest.fixture
def persisted_hist():
- from cmd2.parsing import Statement
from cmd2.cmd2 import History, HistoryItem
- h = History([HistoryItem(Statement('', raw='first'), 1),
- HistoryItem(Statement('', raw='second'), 2),
- HistoryItem(Statement('', raw='third'), 3),
- HistoryItem(Statement('', raw='fourth'),4)])
+ from cmd2.parsing import Statement
+
+ h = History(
+ [
+ HistoryItem(Statement('', raw='first'), 1),
+ HistoryItem(Statement('', raw='second'), 2),
+ HistoryItem(Statement('', raw='third'), 3),
+ HistoryItem(Statement('', raw='fourth'), 4),
+ ]
+ )
h.start_session()
h.append(Statement('', raw='fifth'))
h.append(Statement('', raw='sixth'))
return h
+
def test_history_class_span(hist):
for tryit in ['*', ':', '-', 'all', 'ALL']:
assert hist.span(tryit) == hist
@@ -135,6 +149,7 @@ def test_history_class_span(hist):
with pytest.raises(ValueError):
hist.span(tryit)
+
def test_persisted_history_span(persisted_hist):
for tryit in ['*', ':', '-', 'all', 'ALL']:
assert persisted_hist.span(tryit, include_persisted=True) == persisted_hist
@@ -191,6 +206,7 @@ def test_persisted_history_span(persisted_hist):
with pytest.raises(ValueError):
persisted_hist.span(tryit)
+
def test_history_class_get(hist):
assert hist.get('1').statement.raw == 'first'
assert hist.get(3).statement.raw == 'third'
@@ -217,6 +233,7 @@ def test_history_class_get(hist):
with pytest.raises(TypeError):
hist.get(None)
+
def test_history_str_search(hist):
items = hist.str_search('ir')
assert len(items) == 2
@@ -227,6 +244,7 @@ def test_history_str_search(hist):
assert len(items) == 1
assert items[0].statement.raw == 'fourth'
+
def test_history_regex_search(hist):
items = hist.regex_search('/i.*d/')
assert len(items) == 1
@@ -236,52 +254,59 @@ def test_history_regex_search(hist):
assert len(items) == 1
assert items[0].statement.raw == 'second'
+
def test_history_max_length_zero(hist):
hist.truncate(0)
assert len(hist) == 0
+
def test_history_max_length_negative(hist):
hist.truncate(-1)
assert len(hist) == 0
+
def test_history_max_length(hist):
hist.truncate(2)
assert len(hist) == 2
assert hist.get(1).statement.raw == 'third'
assert hist.get(2).statement.raw == 'fourth'
+
#
# test HistoryItem()
#
@pytest.fixture
def histitem():
- from cmd2.parsing import Statement
from cmd2.history import HistoryItem
- statement = Statement('history',
- raw='help history',
- command='help',
- arg_list=['history'],
- )
+ from cmd2.parsing import Statement
+
+ statement = Statement('history', raw='help history', command='help', arg_list=['history'],)
histitem = HistoryItem(statement, 1)
return histitem
+
@pytest.fixture
def parser():
from cmd2.parsing import StatementParser
+
parser = StatementParser(
terminators=[';', '&'],
multiline_commands=['multiline'],
- aliases={'helpalias': 'help',
- '42': 'theanswer',
- 'l': '!ls -al',
- 'anothermultiline': 'multiline',
- 'fake': 'run_pyscript'},
- shortcuts={'?': 'help', '!': 'shell'}
+ aliases={
+ 'helpalias': 'help',
+ '42': 'theanswer',
+ 'l': '!ls -al',
+ 'anothermultiline': 'multiline',
+ 'fake': 'run_pyscript',
+ },
+ shortcuts={'?': 'help', '!': 'shell'},
)
return parser
+
def test_multiline_histitem(parser):
from cmd2.history import History
+
line = 'multiline foo\nbar\n\n'
statement = parser.parse(line)
history = History()
@@ -292,8 +317,10 @@ def test_multiline_histitem(parser):
pr_lines = hist_item.pr().splitlines()
assert pr_lines[0].endswith('multiline foo bar')
+
def test_multiline_histitem_verbose(parser):
from cmd2.history import History
+
line = 'multiline foo\nbar\n\n'
statement = parser.parse(line)
history = History()
@@ -305,14 +332,12 @@ def test_multiline_histitem_verbose(parser):
assert pr_lines[0].endswith('multiline foo')
assert pr_lines[1] == 'bar'
+
def test_history_item_instantiate():
- from cmd2.parsing import Statement
from cmd2.history import HistoryItem
- statement = Statement('history',
- raw='help history',
- command='help',
- arg_list=['history'],
- )
+ from cmd2.parsing import Statement
+
+ statement = Statement('history', raw='help history', command='help', arg_list=['history'],)
with pytest.raises(TypeError):
_ = HistoryItem()
with pytest.raises(TypeError):
@@ -322,11 +347,13 @@ def test_history_item_instantiate():
with pytest.raises(TypeError):
_ = HistoryItem(statement=statement, idx='hi')
+
def test_history_item_properties(histitem):
assert histitem.raw == 'help history'
assert histitem.expanded == 'help history'
assert str(histitem) == 'help history'
+
#
# test history command
#
@@ -334,113 +361,144 @@ def test_base_history(base_app):
run_cmd(base_app, 'help')
run_cmd(base_app, 'shortcuts')
out, err = run_cmd(base_app, 'history')
- expected = normalize("""
+ expected = normalize(
+ """
1 help
2 shortcuts
-""")
+"""
+ )
assert out == expected
out, err = run_cmd(base_app, 'history he')
- expected = normalize("""
+ expected = normalize(
+ """
1 help
-""")
+"""
+ )
assert out == expected
out, err = run_cmd(base_app, 'history sh')
- expected = normalize("""
+ expected = normalize(
+ """
2 shortcuts
-""")
+"""
+ )
assert out == expected
+
def test_history_script_format(base_app):
run_cmd(base_app, 'help')
run_cmd(base_app, 'shortcuts')
out, err = run_cmd(base_app, 'history -s')
- expected = normalize("""
+ expected = normalize(
+ """
help
shortcuts
-""")
+"""
+ )
assert out == expected
+
def test_history_with_string_argument(base_app):
run_cmd(base_app, 'help')
run_cmd(base_app, 'shortcuts')
run_cmd(base_app, 'help history')
out, err = run_cmd(base_app, 'history help')
- expected = normalize("""
+ expected = normalize(
+ """
1 help
3 help history
-""")
+"""
+ )
assert out == expected
+
def test_history_expanded_with_string_argument(base_app):
run_cmd(base_app, 'alias create sc shortcuts')
run_cmd(base_app, 'help')
run_cmd(base_app, 'help history')
run_cmd(base_app, 'sc')
out, err = run_cmd(base_app, 'history -v shortcuts')
- expected = normalize("""
+ expected = normalize(
+ """
1 alias create sc shortcuts
4 sc
4x shortcuts
-""")
+"""
+ )
assert out == expected
+
def test_history_expanded_with_regex_argument(base_app):
run_cmd(base_app, 'alias create sc shortcuts')
run_cmd(base_app, 'help')
run_cmd(base_app, 'help history')
run_cmd(base_app, 'sc')
out, err = run_cmd(base_app, 'history -v /sh.*cuts/')
- expected = normalize("""
+ expected = normalize(
+ """
1 alias create sc shortcuts
4 sc
4x shortcuts
-""")
+"""
+ )
assert out == expected
+
def test_history_with_integer_argument(base_app):
run_cmd(base_app, 'help')
run_cmd(base_app, 'shortcuts')
out, err = run_cmd(base_app, 'history 1')
- expected = normalize("""
+ expected = normalize(
+ """
1 help
-""")
+"""
+ )
assert out == expected
+
def test_history_with_integer_span(base_app):
run_cmd(base_app, 'help')
run_cmd(base_app, 'shortcuts')
run_cmd(base_app, 'help history')
out, err = run_cmd(base_app, 'history 1..2')
- expected = normalize("""
+ expected = normalize(
+ """
1 help
2 shortcuts
-""")
+"""
+ )
assert out == expected
+
def test_history_with_span_start(base_app):
run_cmd(base_app, 'help')
run_cmd(base_app, 'shortcuts')
run_cmd(base_app, 'help history')
out, err = run_cmd(base_app, 'history 2:')
- expected = normalize("""
+ expected = normalize(
+ """
2 shortcuts
3 help history
-""")
+"""
+ )
assert out == expected
+
def test_history_with_span_end(base_app):
run_cmd(base_app, 'help')
run_cmd(base_app, 'shortcuts')
run_cmd(base_app, 'help history')
out, err = run_cmd(base_app, 'history :2')
- expected = normalize("""
+ expected = normalize(
+ """
1 help
2 shortcuts
-""")
+"""
+ )
assert out == expected
+
def test_history_with_span_index_error(base_app):
run_cmd(base_app, 'help')
run_cmd(base_app, 'help history')
@@ -448,6 +506,7 @@ def test_history_with_span_index_error(base_app):
with pytest.raises(ValueError):
base_app.onecmd('history "hal :"')
+
def test_history_output_file():
app = cmd2.Cmd(multiline_commands=['alias'])
run_cmd(app, 'help')
@@ -463,6 +522,7 @@ def test_history_output_file():
content = normalize(f.read())
assert content == expected
+
def test_history_bad_output_file(base_app):
run_cmd(base_app, 'help')
run_cmd(base_app, 'shortcuts')
@@ -474,6 +534,7 @@ def test_history_bad_output_file(base_app):
assert not out
assert "Error saving" in err[0]
+
def test_history_edit(monkeypatch):
app = cmd2.Cmd(multiline_commands=['alias'])
@@ -499,6 +560,7 @@ def test_history_edit(monkeypatch):
edit_mock.assert_called_once()
run_script_mock.assert_called_once()
+
def test_history_run_all_commands(base_app):
# make sure we refuse to run all commands as a default
run_cmd(base_app, 'shortcuts')
@@ -509,11 +571,13 @@ def test_history_run_all_commands(base_app):
# then we should have a list of shortcuts in our output
assert out == []
+
def test_history_run_one_command(base_app):
out1, err1 = run_cmd(base_app, 'help')
out2, err2 = run_cmd(base_app, 'history -r 1')
assert out1 == out2
+
def test_history_clear(hist_file):
# Add commands to history
app = cmd2.Cmd(persistent_history_file=hist_file)
@@ -532,6 +596,7 @@ def test_history_clear(hist_file):
assert out == []
assert not os.path.exists(hist_file)
+
def test_history_verbose_with_other_options(base_app):
# make sure -v shows a usage error if any other options are present
options_to_test = ['-r', '-e', '-o file', '-t file', '-c', '-x']
@@ -541,6 +606,7 @@ def test_history_verbose_with_other_options(base_app):
assert out[0] == '-v can not be used with any other options'
assert out[1].startswith('Usage:')
+
def test_history_verbose(base_app):
# validate function of -v option
run_cmd(base_app, 'alias create s shortcuts')
@@ -549,6 +615,7 @@ def test_history_verbose(base_app):
assert len(out) == 3
# TODO test for basic formatting once we figure it out
+
def test_history_script_with_invalid_options(base_app):
# make sure -s shows a usage error if -c, -r, -e, -o, or -t are present
options_to_test = ['-r', '-e', '-o file', '-t file', '-c']
@@ -558,6 +625,7 @@ def test_history_script_with_invalid_options(base_app):
assert out[0] == '-s and -x can not be used with -c, -r, -e, -o, or -t'
assert out[1].startswith('Usage:')
+
def test_history_script(base_app):
cmds = ['alias create s shortcuts', 's']
for cmd in cmds:
@@ -565,6 +633,7 @@ def test_history_script(base_app):
out, err = run_cmd(base_app, 'history -s')
assert out == cmds
+
def test_history_expanded_with_invalid_options(base_app):
# make sure -x shows a usage error if -c, -r, -e, -o, or -t are present
options_to_test = ['-r', '-e', '-o file', '-t file', '-c']
@@ -574,6 +643,7 @@ def test_history_expanded_with_invalid_options(base_app):
assert out[0] == '-s and -x can not be used with -c, -r, -e, -o, or -t'
assert out[1].startswith('Usage:')
+
def test_history_expanded(base_app):
# validate function of -x option
cmds = ['alias create s shortcuts', 's']
@@ -583,6 +653,7 @@ def test_history_expanded(base_app):
expected = [' 1 alias create s shortcuts', ' 2 shortcuts']
assert out == expected
+
def test_history_script_expanded(base_app):
# validate function of -s -x options together
cmds = ['alias create s shortcuts', 's']
@@ -592,10 +663,12 @@ def test_history_script_expanded(base_app):
expected = ['alias create s shortcuts', 'shortcuts']
assert out == expected
+
def test_base_help_history(base_app):
out, err = run_cmd(base_app, 'help history')
assert out == normalize(HELP_HISTORY)
+
def test_exclude_from_history(base_app, monkeypatch):
# Run history command
run_cmd(base_app, 'history')
@@ -612,6 +685,7 @@ def test_exclude_from_history(base_app, monkeypatch):
expected = normalize(""" 1 help""")
assert out == expected
+
#
# test history initialization
#
@@ -626,6 +700,7 @@ def hist_file():
except FileNotFoundError:
pass
+
def test_history_file_is_directory(capsys):
with tempfile.TemporaryDirectory() as test_dir:
# Create a new cmd2 app
@@ -633,6 +708,7 @@ def test_history_file_is_directory(capsys):
_, err = capsys.readouterr()
assert 'is a directory' in err
+
def test_history_can_create_directory(mocker):
# Mock out atexit.register so the persistent file doesn't written when this function
# exists because we will be deleting the directory it needs to go to.
@@ -654,6 +730,7 @@ def test_history_can_create_directory(mocker):
# Cleanup
os.rmdir(hist_file_dir)
+
def test_history_cannot_create_directory(mocker, capsys):
mock_open = mocker.patch('os.makedirs')
mock_open.side_effect = OSError
@@ -663,6 +740,7 @@ def test_history_cannot_create_directory(mocker, capsys):
_, err = capsys.readouterr()
assert 'Error creating persistent history file directory' in err
+
def test_history_file_permission_error(mocker, capsys):
mock_open = mocker.patch('builtins.open')
mock_open.side_effect = PermissionError
@@ -672,6 +750,7 @@ def test_history_file_permission_error(mocker, capsys):
assert not out
assert 'Can not read' in err
+
def test_history_file_conversion_no_truncate_on_init(hist_file, capsys):
# make sure we don't truncate the plain text history file on init
# it shouldn't get converted to pickle format until we save history
@@ -688,14 +767,15 @@ def test_history_file_conversion_no_truncate_on_init(hist_file, capsys):
# history should be initialized, but the file on disk should
# still be plain text
with open(hist_file, 'r') as hfobj:
- histlist= hfobj.readlines()
+ histlist = hfobj.readlines()
assert len(histlist) == 3
# history.get() is overridden to be one based, not zero based
- assert histlist[0]== 'help\n'
+ assert histlist[0] == 'help\n'
assert histlist[1] == 'alias\n'
assert histlist[2] == 'alias create s shortcuts\n'
+
def test_history_populates_readline(hist_file):
# - create a cmd2 with persistent history
app = cmd2.Cmd(persistent_history_file=hist_file)
@@ -718,11 +798,13 @@ def test_history_populates_readline(hist_file):
# so we check to make sure that cmd2 populated the readline history
# using the same rules
from cmd2.rl_utils import readline
+
assert readline.get_current_history_length() == 3
assert readline.get_history_item(1) == 'help'
assert readline.get_history_item(2) == 'shortcuts'
assert readline.get_history_item(3) == 'alias'
+
#
# test cmd2's ability to write out history on exit
# we are testing the _persist_history_on_exit() method, and
@@ -737,6 +819,7 @@ def test_persist_history_ensure_no_error_if_no_histfile(base_app, capsys):
assert not out
assert not err
+
def test_persist_history_permission_error(hist_file, mocker, capsys):
app = cmd2.Cmd(persistent_history_file=hist_file)
run_cmd(app, 'help')
diff --git a/tests/test_parsing.py b/tests/test_parsing.py
index 2eccec7c..379ee2c7 100755
--- a/tests/test_parsing.py
+++ b/tests/test_parsing.py
@@ -16,12 +16,14 @@ def parser():
parser = StatementParser(
terminators=[';', '&'],
multiline_commands=['multiline'],
- aliases={'helpalias': 'help',
- '42': 'theanswer',
- 'l': '!ls -al',
- 'anothermultiline': 'multiline',
- 'fake': 'run_pyscript'},
- shortcuts={'?': 'help', '!': 'shell'}
+ aliases={
+ 'helpalias': 'help',
+ '42': 'theanswer',
+ 'l': '!ls -al',
+ 'anothermultiline': 'multiline',
+ 'fake': 'run_pyscript',
+ },
+ shortcuts={'?': 'help', '!': 'shell'},
)
return parser
@@ -68,34 +70,40 @@ def test_parse_empty_string_default(default_parser):
assert statement.argv == statement.arg_list
-@pytest.mark.parametrize('line,tokens', [
- ('command', ['command']),
- (constants.COMMENT_CHAR + 'comment', []),
- ('not ' + constants.COMMENT_CHAR + ' a comment', ['not', constants.COMMENT_CHAR, 'a', 'comment']),
- ('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']),
-])
+@pytest.mark.parametrize(
+ 'line,tokens',
+ [
+ ('command', ['command']),
+ (constants.COMMENT_CHAR + 'comment', []),
+ ('not ' + constants.COMMENT_CHAR + ' a comment', ['not', constants.COMMENT_CHAR, 'a', 'comment']),
+ ('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
-@pytest.mark.parametrize('line,tokens', [
- ('command', ['command']),
- ('# comment', []),
- ('not ' + constants.COMMENT_CHAR + ' a comment', ['not', constants.COMMENT_CHAR, 'a', 'comment']),
- ('42 arg1 arg2', ['theanswer', 'arg1', 'arg2']),
- ('l', ['shell', 'ls', '-al']),
- ('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']),
- ('l|less', ['shell', 'ls', '-al', '|', 'less']),
-])
+@pytest.mark.parametrize(
+ 'line,tokens',
+ [
+ ('command', ['command']),
+ ('# comment', []),
+ ('not ' + constants.COMMENT_CHAR + ' a comment', ['not', constants.COMMENT_CHAR, 'a', 'comment']),
+ ('42 arg1 arg2', ['theanswer', 'arg1', 'arg2']),
+ ('l', ['shell', 'ls', '-al']),
+ ('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']),
+ ('l|less', ['shell', 'ls', '-al', '|', 'less']),
+ ],
+)
def test_tokenize(parser, line, tokens):
tokens_to_test = parser.tokenize(line)
assert tokens_to_test == tokens
@@ -106,22 +114,16 @@ def test_tokenize_unclosed_quotes(parser):
_ = parser.tokenize('command with "unclosed quotes')
-@pytest.mark.parametrize('tokens,command,args', [
- ([], '', ''),
- (['command'], 'command', ''),
- (['command', 'arg1', 'arg2'], 'command', 'arg1 arg2')
-])
+@pytest.mark.parametrize(
+ 'tokens,command,args', [([], '', ''), (['command'], 'command', ''), (['command', 'arg1', 'arg2'], 'command', 'arg1 arg2')]
+)
def test_command_and_args(parser, tokens, command, args):
(parsed_command, parsed_args) = parser._command_and_args(tokens)
assert command == parsed_command
assert args == parsed_args
-@pytest.mark.parametrize('line', [
- 'plainword',
- '"one word"',
- "'one word'",
-])
+@pytest.mark.parametrize('line', ['plainword', '"one word"', "'one word'",])
def test_parse_single_word(parser, line):
statement = parser.parse(line)
assert statement.command == line
@@ -139,12 +141,9 @@ def test_parse_single_word(parser, line):
assert statement.command_and_args == line
-@pytest.mark.parametrize('line,terminator', [
- ('termbare;', ';'),
- ('termbare ;', ';'),
- ('termbare&', '&'),
- ('termbare &', '&'),
-])
+@pytest.mark.parametrize(
+ 'line,terminator', [('termbare;', ';'), ('termbare ;', ';'), ('termbare&', '&'), ('termbare &', '&'),]
+)
def test_parse_word_plus_terminator(parser, line, terminator):
statement = parser.parse(line)
assert statement.command == 'termbare'
@@ -155,12 +154,10 @@ def test_parse_word_plus_terminator(parser, line, terminator):
assert statement.expanded_command_line == statement.command + statement.terminator
-@pytest.mark.parametrize('line,terminator', [
- ('termbare; suffx', ';'),
- ('termbare ;suffx', ';'),
- ('termbare& suffx', '&'),
- ('termbare &suffx', '&'),
-])
+@pytest.mark.parametrize(
+ 'line,terminator',
+ [('termbare; suffx', ';'), ('termbare ;suffx', ';'), ('termbare& suffx', '&'), ('termbare &suffx', '&'),],
+)
def test_parse_suffix_after_terminator(parser, line, terminator):
statement = parser.parse(line)
assert statement.command == 'termbare'
@@ -224,10 +221,7 @@ def test_parse_embedded_comment_char(parser):
assert statement.arg_list == statement.argv[1:]
-@pytest.mark.parametrize('line', [
- 'simple | piped',
- 'simple|piped',
-])
+@pytest.mark.parametrize('line', ['simple | piped', 'simple|piped',])
def test_parse_simple_pipe(parser, line):
statement = parser.parse(line)
assert statement.command == 'simple'
@@ -263,12 +257,9 @@ def test_parse_complex_pipe(parser):
assert statement.pipe_to == 'piped'
-@pytest.mark.parametrize('line,output', [
- ('help > out.txt', '>'),
- ('help>out.txt', '>'),
- ('help >> out.txt', '>>'),
- ('help>>out.txt', '>>'),
-])
+@pytest.mark.parametrize(
+ 'line,output', [('help > out.txt', '>'), ('help>out.txt', '>'), ('help >> out.txt', '>>'), ('help>>out.txt', '>>'),]
+)
def test_parse_redirect(parser, line, output):
statement = parser.parse(line)
assert statement.command == 'help'
@@ -279,10 +270,7 @@ def test_parse_redirect(parser, line, output):
assert statement.expanded_command_line == statement.command + ' ' + statement.output + ' ' + statement.output_to
-@pytest.mark.parametrize('dest', [
- 'afile.txt', # without dashes
- 'python-cmd2/afile.txt', # with dashes in path
-])
+@pytest.mark.parametrize('dest', ['afile.txt', 'python-cmd2/afile.txt',]) # without dashes # with dashes in path
def test_parse_redirect_with_args(parser, dest):
line = 'output into > {}'.format(dest)
statement = parser.parse(line)
@@ -482,16 +470,19 @@ def test_parse_redirect_inside_terminator(parser):
assert statement.terminator == ';'
-@pytest.mark.parametrize('line,terminator', [
- ('multiline with | inside;', ';'),
- ('multiline with | inside ;', ';'),
- ('multiline with | inside;;;', ';'),
- ('multiline with | inside;; ;;', ';'),
- ('multiline with | inside&', '&'),
- ('multiline with | inside &;', '&'),
- ('multiline with | inside&&;', '&'),
- ('multiline with | inside &; &;', '&'),
-])
+@pytest.mark.parametrize(
+ 'line,terminator',
+ [
+ ('multiline with | inside;', ';'),
+ ('multiline with | inside ;', ';'),
+ ('multiline with | inside;;;', ';'),
+ ('multiline with | inside;; ;;', ';'),
+ ('multiline with | inside&', '&'),
+ ('multiline with | inside &;', '&'),
+ ('multiline with | inside&&;', '&'),
+ ('multiline with | inside &; &;', '&'),
+ ],
+)
def test_parse_multiple_terminators(parser, line, terminator):
statement = parser.parse(line)
assert statement.multiline_command == 'multiline'
@@ -527,13 +518,16 @@ def test_parse_basic_multiline_command(parser):
assert statement.terminator == '\n'
-@pytest.mark.parametrize('line,terminator', [
- ('multiline has > inside;', ';'),
- ('multiline has > inside;;;', ';'),
- ('multiline has > inside;; ;;', ';'),
- ('multiline has > inside &', '&'),
- ('multiline has > inside & &', '&'),
-])
+@pytest.mark.parametrize(
+ 'line,terminator',
+ [
+ ('multiline has > inside;', ';'),
+ ('multiline has > inside;;;', ';'),
+ ('multiline has > inside;; ;;', ';'),
+ ('multiline has > inside &', '&'),
+ ('multiline has > inside & &', '&'),
+ ],
+)
def test_parse_multiline_command_ignores_redirectors_within_it(parser, line, terminator):
statement = parser.parse(line)
assert statement.multiline_command == 'multiline'
@@ -556,14 +550,17 @@ def test_parse_multiline_terminated_by_empty_line(parser):
assert statement.terminator == '\n'
-@pytest.mark.parametrize('line,terminator', [
- ('multiline command "with\nembedded newline";', ';'),
- ('multiline command "with\nembedded newline";;;', ';'),
- ('multiline command "with\nembedded newline";; ;;', ';'),
- ('multiline command "with\nembedded newline" &', '&'),
- ('multiline command "with\nembedded newline" & &', '&'),
- ('multiline command "with\nembedded newline"\n\n', '\n'),
-])
+@pytest.mark.parametrize(
+ 'line,terminator',
+ [
+ ('multiline command "with\nembedded newline";', ';'),
+ ('multiline command "with\nembedded newline";;;', ';'),
+ ('multiline command "with\nembedded newline";; ;;', ';'),
+ ('multiline command "with\nembedded newline" &', '&'),
+ ('multiline command "with\nembedded newline" & &', '&'),
+ ('multiline command "with\nembedded newline"\n\n', '\n'),
+ ],
+)
def test_parse_multiline_with_embedded_newline(parser, line, terminator):
statement = parser.parse(line)
assert statement.multiline_command == 'multiline'
@@ -633,15 +630,18 @@ def test_empty_statement_raises_exception():
app._complete_statement(' ')
-@pytest.mark.parametrize('line,command,args', [
- ('helpalias', 'help', ''),
- ('helpalias mycommand', 'help', 'mycommand'),
- ('42', 'theanswer', ''),
- ('42 arg1 arg2', 'theanswer', 'arg1 arg2'),
- ('!ls', 'shell', 'ls'),
- ('!ls -al /tmp', 'shell', 'ls -al /tmp'),
- ('l', 'shell', 'ls -al')
-])
+@pytest.mark.parametrize(
+ 'line,command,args',
+ [
+ ('helpalias', 'help', ''),
+ ('helpalias mycommand', 'help', 'mycommand'),
+ ('42', 'theanswer', ''),
+ ('42 arg1 arg2', 'theanswer', 'arg1 arg2'),
+ ('!ls', 'shell', 'ls'),
+ ('!ls -al /tmp', 'shell', 'ls -al /tmp'),
+ ('l', 'shell', 'ls -al'),
+ ],
+)
def test_parse_alias_and_shortcut_expansion(parser, line, command, args):
statement = parser.parse(line)
assert statement.command == command
@@ -659,12 +659,10 @@ def test_parse_alias_on_multiline_command(parser):
assert statement.terminator == ''
-@pytest.mark.parametrize('line,output', [
- ('helpalias > out.txt', '>'),
- ('helpalias>out.txt', '>'),
- ('helpalias >> out.txt', '>>'),
- ('helpalias>>out.txt', '>>'),
-])
+@pytest.mark.parametrize(
+ 'line,output',
+ [('helpalias > out.txt', '>'), ('helpalias>out.txt', '>'), ('helpalias >> out.txt', '>>'), ('helpalias>>out.txt', '>>'),],
+)
def test_parse_alias_redirection(parser, line, output):
statement = parser.parse(line)
assert statement.command == 'help'
@@ -674,10 +672,7 @@ def test_parse_alias_redirection(parser, line, output):
assert statement.output_to == 'out.txt'
-@pytest.mark.parametrize('line', [
- 'helpalias | less',
- 'helpalias|less',
-])
+@pytest.mark.parametrize('line', ['helpalias | less', 'helpalias|less',])
def test_parse_alias_pipe(parser, line):
statement = parser.parse(line)
assert statement.command == 'help'
@@ -686,14 +681,9 @@ def test_parse_alias_pipe(parser, line):
assert statement.pipe_to == 'less'
-@pytest.mark.parametrize('line', [
- 'helpalias;',
- 'helpalias;;',
- 'helpalias;; ;',
- 'helpalias ;',
- 'helpalias ; ;',
- 'helpalias ;; ;',
-])
+@pytest.mark.parametrize(
+ 'line', ['helpalias;', 'helpalias;;', 'helpalias;; ;', 'helpalias ;', 'helpalias ; ;', 'helpalias ;; ;',]
+)
def test_parse_alias_terminator_no_whitespace(parser, line):
statement = parser.parse(line)
assert statement.command == 'help'
@@ -789,16 +779,19 @@ def test_parse_command_only_quoted_args(parser):
assert statement.output_to == ''
-@pytest.mark.parametrize('line,args', [
- ('helpalias > out.txt', '> out.txt'),
- ('helpalias>out.txt', '>out.txt'),
- ('helpalias >> out.txt', '>> out.txt'),
- ('helpalias>>out.txt', '>>out.txt'),
- ('help|less', '|less'),
- ('helpalias;', ';'),
- ('help ;;', ';;'),
- ('help; ;;', '; ;;'),
-])
+@pytest.mark.parametrize(
+ 'line,args',
+ [
+ ('helpalias > out.txt', '> out.txt'),
+ ('helpalias>out.txt', '>out.txt'),
+ ('helpalias >> out.txt', '>> out.txt'),
+ ('helpalias>>out.txt', '>>out.txt'),
+ ('help|less', '|less'),
+ ('helpalias;', ';'),
+ ('help ;;', ';;'),
+ ('help; ;;', '; ;;'),
+ ],
+)
def test_parse_command_only_specialchars(parser, line, args):
statement = parser.parse_command_only(line)
assert statement == args
@@ -814,19 +807,7 @@ def test_parse_command_only_specialchars(parser, line, args):
assert statement.output_to == ''
-@pytest.mark.parametrize('line', [
- '',
- ';',
- ';;',
- ';; ;',
- '&',
- '& &',
- ' && &',
- '>',
- "'",
- '"',
- '|',
-])
+@pytest.mark.parametrize('line', ['', ';', ';;', ';; ;', '&', '& &', ' && &', '>', "'", '"', '|',])
def test_parse_command_only_empty(parser, line):
statement = parser.parse_command_only(line)
assert statement == ''
@@ -940,6 +921,7 @@ def test_is_valid_command_valid(parser):
def test_macro_normal_arg_pattern():
# This pattern matches digits surrounded by exactly 1 brace on a side and 1 or more braces on the opposite side
from cmd2.parsing import MacroArg
+
pattern = MacroArg.macro_normal_arg_pattern
# Valid strings
@@ -991,6 +973,7 @@ def test_macro_normal_arg_pattern():
def test_macro_escaped_arg_pattern():
# This pattern matches digits surrounded by 2 or more braces on both sides
from cmd2.parsing import MacroArg
+
pattern = MacroArg.macro_escaped_arg_pattern
# Valid strings
diff --git a/tests/test_plugin.py b/tests/test_plugin.py
index 279f2f79..6f2b2f32 100644
--- a/tests/test_plugin.py
+++ b/tests/test_plugin.py
@@ -18,9 +18,9 @@ except ImportError:
from unittest import mock
-
class Plugin:
"""A mixin class for testing hook registration and calling"""
+
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.reset_counters()
@@ -222,14 +222,16 @@ class Plugin:
self.called_cmdfinalization += 1
raise ValueError
- def cmdfinalization_hook_system_exit(self, data: cmd2.plugin.CommandFinalizationData) -> \
- cmd2.plugin.CommandFinalizationData:
+ def cmdfinalization_hook_system_exit(
+ self, data: cmd2.plugin.CommandFinalizationData
+ ) -> cmd2.plugin.CommandFinalizationData:
"""A command finalization hook which raises a SystemExit"""
self.called_cmdfinalization += 1
raise SystemExit
- def cmdfinalization_hook_keyboard_interrupt(self, data: cmd2.plugin.CommandFinalizationData) -> \
- cmd2.plugin.CommandFinalizationData:
+ def cmdfinalization_hook_keyboard_interrupt(
+ self, data: cmd2.plugin.CommandFinalizationData
+ ) -> cmd2.plugin.CommandFinalizationData:
"""A command finalization hook which raises a KeyboardInterrupt"""
self.called_cmdfinalization += 1
raise KeyboardInterrupt
@@ -238,8 +240,9 @@ class Plugin:
"""A command finalization hook with no parameters."""
pass
- def cmdfinalization_hook_too_many_parameters(self, one: plugin.CommandFinalizationData, two: str) -> \
- plugin.CommandFinalizationData:
+ def cmdfinalization_hook_too_many_parameters(
+ self, one: plugin.CommandFinalizationData, two: str
+ ) -> plugin.CommandFinalizationData:
"""A command finalization hook with too many parameters."""
return one
@@ -262,6 +265,7 @@ class Plugin:
class PluggedApp(Plugin, cmd2.Cmd):
"""A sample app with a plugin mixed in"""
+
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@@ -281,6 +285,7 @@ class PluggedApp(Plugin, cmd2.Cmd):
"""Repeat back the arguments"""
self.poutput(namespace.cmd2_statement.get())
+
###
#
# test pre and postloop hooks
@@ -291,11 +296,13 @@ def test_register_preloop_hook_too_many_parameters():
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):
# Need to patch sys.argv so cmd2 doesn't think it was called with arguments equal to the py.test args
testargs = ["prog", "say hello", 'quit']
@@ -309,6 +316,7 @@ def test_preloop_hook(capsys):
assert out == 'one\nhello\n'
assert not err
+
def test_preloop_hooks(capsys):
# Need to patch sys.argv so cmd2 doesn't think it was called with arguments equal to the py.test args
testargs = ["prog", "say hello", 'quit']
@@ -323,16 +331,19 @@ def test_preloop_hooks(capsys):
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):
# Need to patch sys.argv so cmd2 doesn't think it was called with arguments equal to the py.test args
testargs = ["prog", "say hello", 'quit']
@@ -346,6 +357,7 @@ def test_postloop_hook(capsys):
assert out == 'hello\none\n'
assert not err
+
def test_postloop_hooks(capsys):
# Need to patch sys.argv so cmd2 doesn't think it was called with arguments equal to the py.test args
testargs = ["prog", "say hello", 'quit']
@@ -360,6 +372,7 @@ def test_postloop_hooks(capsys):
assert out == 'hello\none\ntwo\n'
assert not err
+
###
#
# test preparse hook
@@ -374,6 +387,7 @@ def test_preparse(capsys):
assert not err
assert app.called_preparse == 1
+
###
#
# test postparsing hooks
@@ -384,26 +398,31 @@ def test_postparsing_hook_too_many_parameters():
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')
@@ -429,6 +448,7 @@ def test_postparsing_hook(capsys):
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)
@@ -443,6 +463,7 @@ def test_postparsing_hook_stop_first(capsys):
assert app.called_postparsing == 1
assert stop
+
def test_postparsing_hook_stop_second(capsys):
app = PluggedApp()
app.register_postparsing_hook(app.postparse_hook)
@@ -464,6 +485,7 @@ def test_postparsing_hook_stop_second(capsys):
assert app.called_postparsing == 2
assert stop
+
def test_postparsing_hook_emptystatement_first(capsys):
app = PluggedApp()
app.register_postparsing_hook(app.postparse_hook_emptystatement)
@@ -484,6 +506,7 @@ def test_postparsing_hook_emptystatement_first(capsys):
assert not err
assert app.called_postparsing == 1
+
def test_postparsing_hook_emptystatement_second(capsys):
app = PluggedApp()
app.register_postparsing_hook(app.postparse_hook)
@@ -514,6 +537,7 @@ def test_postparsing_hook_emptystatement_second(capsys):
assert not err
assert app.called_postparsing == 2
+
def test_postparsing_hook_exception(capsys):
app = PluggedApp()
app.register_postparsing_hook(app.postparse_hook_exception)
@@ -534,6 +558,7 @@ def test_postparsing_hook_exception(capsys):
assert err
assert app.called_postparsing == 1
+
###
#
# test precmd hooks
@@ -546,26 +571,31 @@ def test_register_precmd_hook_parameter_count():
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')
@@ -594,6 +624,7 @@ def test_precmd_hook(capsys):
# 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)
@@ -619,6 +650,7 @@ def test_precmd_hook_emptystatement_first(capsys):
# called
assert app.called_precmd == 1
+
def test_precmd_hook_emptystatement_second(capsys):
app = PluggedApp()
app.register_precmd_hook(app.precmd_hook)
@@ -655,6 +687,7 @@ def test_precmd_hook_emptystatement_second(capsys):
# if a registered hook throws an exception, precmd() is never called
assert app.called_precmd == 2
+
###
#
# test postcmd hooks
@@ -667,26 +700,31 @@ def test_register_postcmd_hook_parameter_count():
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')
@@ -715,6 +753,7 @@ def test_postcmd(capsys):
# 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)
@@ -741,6 +780,7 @@ def test_postcmd_exception_first(capsys):
# called
assert app.called_postcmd == 1
+
def test_postcmd_exception_second(capsys):
app = PluggedApp()
app.register_postcmd_hook(app.postcmd_hook)
@@ -766,6 +806,7 @@ def test_postcmd_exception_second(capsys):
# the exception
assert app.called_postcmd == 2
+
##
#
# command finalization
@@ -778,26 +819,31 @@ def test_register_cmdfinalization_hook_parameter_count():
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')
@@ -822,6 +868,7 @@ def test_cmdfinalization(capsys):
assert not err
assert app.called_cmdfinalization == 2
+
def test_cmdfinalization_stop_first(capsys):
app = PluggedApp()
app.register_cmdfinalization_hook(app.cmdfinalization_hook_stop)
@@ -833,6 +880,7 @@ def test_cmdfinalization_stop_first(capsys):
assert app.called_cmdfinalization == 2
assert stop
+
def test_cmdfinalization_stop_second(capsys):
app = PluggedApp()
app.register_cmdfinalization_hook(app.cmdfinalization_hook)
@@ -844,6 +892,7 @@ def test_cmdfinalization_stop_second(capsys):
assert app.called_cmdfinalization == 2
assert stop
+
def test_cmdfinalization_hook_exception(capsys):
app = PluggedApp()
app.register_cmdfinalization_hook(app.cmdfinalization_hook_exception)
@@ -864,6 +913,7 @@ def test_cmdfinalization_hook_exception(capsys):
assert err
assert app.called_cmdfinalization == 1
+
def test_cmdfinalization_hook_system_exit(capsys):
app = PluggedApp()
app.register_cmdfinalization_hook(app.cmdfinalization_hook_system_exit)
@@ -871,6 +921,7 @@ def test_cmdfinalization_hook_system_exit(capsys):
assert stop
assert app.called_cmdfinalization == 1
+
def test_cmdfinalization_hook_keyboard_interrupt(capsys):
app = PluggedApp()
app.register_cmdfinalization_hook(app.cmdfinalization_hook_keyboard_interrupt)
@@ -893,6 +944,7 @@ def test_cmdfinalization_hook_keyboard_interrupt(capsys):
assert stop
assert app.called_cmdfinalization == 1
+
def test_skip_postcmd_hooks(capsys):
app = PluggedApp()
app.register_postcmd_hook(app.postcmd_hook)
@@ -905,6 +957,7 @@ def test_skip_postcmd_hooks(capsys):
assert app.called_postcmd == 0
assert app.called_cmdfinalization == 1
+
def test_cmd2_argparse_exception(capsys):
"""
Verify Cmd2ArgparseErrors raised after calling a command prevent postcmd events from
diff --git a/tests/test_run_pyscript.py b/tests/test_run_pyscript.py
index 8cfd8578..e0b2b3c5 100644
--- a/tests/test_run_pyscript.py
+++ b/tests/test_run_pyscript.py
@@ -20,11 +20,13 @@ except ImportError:
HOOK_OUTPUT = "TEST_OUTPUT"
+
def cmdfinalization_hook(data: plugin.CommandFinalizationData) -> plugin.CommandFinalizationData:
"""A cmdfinalization_hook hook which requests application exit"""
print(HOOK_OUTPUT)
return data
+
def test_run_pyscript(base_app, request):
test_dir = os.path.dirname(request.module.__file__)
python_script = os.path.join(test_dir, 'script.py')
@@ -33,6 +35,7 @@ def test_run_pyscript(base_app, request):
out, err = run_cmd(base_app, "run_pyscript {}".format(python_script))
assert expected in out
+
def test_run_pyscript_recursive_not_allowed(base_app, request):
test_dir = os.path.dirname(request.module.__file__)
python_script = os.path.join(test_dir, 'pyscript', 'recursive.py')
@@ -41,11 +44,13 @@ def test_run_pyscript_recursive_not_allowed(base_app, request):
out, err = run_cmd(base_app, "run_pyscript {}".format(python_script))
assert err[0] == expected
+
def test_run_pyscript_with_nonexist_file(base_app):
python_script = 'does_not_exist.py'
out, err = run_cmd(base_app, "run_pyscript {}".format(python_script))
assert "Error reading script file" in err[0]
+
def test_run_pyscript_with_non_python_file(base_app, request):
m = mock.MagicMock(name='input', return_value='2')
builtins.input = m
@@ -55,6 +60,7 @@ def test_run_pyscript_with_non_python_file(base_app, request):
out, err = run_cmd(base_app, 'run_pyscript {}'.format(filename))
assert "does not have a .py extension" in err[0]
+
@pytest.mark.parametrize('python_script', odd_file_names)
def test_run_pyscript_with_odd_file_names(base_app, python_script):
"""
@@ -69,6 +75,7 @@ def test_run_pyscript_with_odd_file_names(base_app, python_script):
err = ''.join(err)
assert "Error reading script file '{}'".format(python_script) in err
+
def test_run_pyscript_with_exception(base_app, request):
test_dir = os.path.dirname(request.module.__file__)
python_script = os.path.join(test_dir, 'pyscript', 'raises_exception.py')
@@ -76,10 +83,12 @@ def test_run_pyscript_with_exception(base_app, request):
assert err[0].startswith('Traceback')
assert "TypeError: unsupported operand type(s) for +: 'int' and 'str'" in err[-1]
+
def test_run_pyscript_requires_an_argument(base_app):
out, err = run_cmd(base_app, "run_pyscript")
assert "the following arguments are required: script_path" in err[1]
+
def test_run_pyscript_help(base_app, request):
test_dir = os.path.dirname(request.module.__file__)
python_script = os.path.join(test_dir, 'pyscript', 'help.py')
@@ -87,6 +96,7 @@ def test_run_pyscript_help(base_app, request):
out2, err2 = run_cmd(base_app, 'run_pyscript {}'.format(python_script))
assert out1 and out1 == out2
+
def test_run_pyscript_dir(base_app, request):
test_dir = os.path.dirname(request.module.__file__)
python_script = os.path.join(test_dir, 'pyscript', 'pyscript_dir.py')
@@ -94,6 +104,7 @@ def test_run_pyscript_dir(base_app, request):
out, err = run_cmd(base_app, 'run_pyscript {}'.format(python_script))
assert out[0] == "['cmd_echo']"
+
def test_run_pyscript_stdout_capture(base_app, request):
base_app.register_cmdfinalization_hook(cmdfinalization_hook)
test_dir = os.path.dirname(request.module.__file__)
@@ -103,6 +114,7 @@ def test_run_pyscript_stdout_capture(base_app, request):
assert out[0] == "PASSED"
assert out[1] == "PASSED"
+
def test_run_pyscript_stop(base_app, request):
# Verify onecmd_plus_hooks() returns True if any commands in a pyscript return True for stop
test_dir = os.path.dirname(request.module.__file__)
@@ -117,6 +129,7 @@ def test_run_pyscript_stop(base_app, request):
stop = base_app.onecmd_plus_hooks('run_pyscript {}'.format(python_script))
assert stop
+
def test_run_pyscript_environment(base_app, request):
test_dir = os.path.dirname(request.module.__file__)
python_script = os.path.join(test_dir, 'pyscript', 'environment.py')
@@ -124,7 +137,8 @@ def test_run_pyscript_environment(base_app, request):
assert out[0] == "PASSED"
-def test_run_pyscript_app_echo(base_app, request):
+
+def test_run_pyscript_app_echo(base_app, request):
test_dir = os.path.dirname(request.module.__file__)
python_script = os.path.join(test_dir, 'pyscript', 'echo.py')
out, err = run_cmd(base_app, 'run_pyscript {}'.format(python_script))
diff --git a/tests/test_table_creator.py b/tests/test_table_creator.py
index 0d2edfb2..c83aee2c 100644
--- a/tests/test_table_creator.py
+++ b/tests/test_table_creator.py
@@ -46,15 +46,30 @@ def test_column_creation():
def test_column_alignment():
- column_1 = Column("Col 1", width=10,
- header_horiz_align=HorizontalAlignment.LEFT, header_vert_align=VerticalAlignment.TOP,
- data_horiz_align=HorizontalAlignment.LEFT, data_vert_align=VerticalAlignment.TOP)
- column_2 = Column("Col 2", width=10,
- header_horiz_align=HorizontalAlignment.CENTER, header_vert_align=VerticalAlignment.MIDDLE,
- data_horiz_align=HorizontalAlignment.CENTER, data_vert_align=VerticalAlignment.MIDDLE)
- column_3 = Column("Col 3", width=10,
- header_horiz_align=HorizontalAlignment.RIGHT, header_vert_align=VerticalAlignment.BOTTOM,
- data_horiz_align=HorizontalAlignment.RIGHT, data_vert_align=VerticalAlignment.BOTTOM)
+ column_1 = Column(
+ "Col 1",
+ width=10,
+ header_horiz_align=HorizontalAlignment.LEFT,
+ header_vert_align=VerticalAlignment.TOP,
+ data_horiz_align=HorizontalAlignment.LEFT,
+ data_vert_align=VerticalAlignment.TOP,
+ )
+ column_2 = Column(
+ "Col 2",
+ width=10,
+ header_horiz_align=HorizontalAlignment.CENTER,
+ header_vert_align=VerticalAlignment.MIDDLE,
+ data_horiz_align=HorizontalAlignment.CENTER,
+ data_vert_align=VerticalAlignment.MIDDLE,
+ )
+ column_3 = Column(
+ "Col 3",
+ width=10,
+ header_horiz_align=HorizontalAlignment.RIGHT,
+ header_vert_align=VerticalAlignment.BOTTOM,
+ data_horiz_align=HorizontalAlignment.RIGHT,
+ data_vert_align=VerticalAlignment.BOTTOM,
+ )
column_4 = Column("Three\nline\nheader", width=10)
columns = [column_1, column_2, column_3, column_4]
@@ -68,16 +83,20 @@ def test_column_alignment():
# Create a header row
header = tc.generate_row()
- assert header == ('Col 1 Three \n'
- ' Col 2 line \n'
- ' Col 3 header ')
+ assert header == (
+ 'Col 1 Three \n'
+ ' Col 2 line \n'
+ ' Col 3 header '
+ )
# Create a data row
row_data = ["Val 1", "Val 2", "Val 3", "Three\nline\ndata"]
row = tc.generate_row(row_data=row_data)
- assert row == ('Val 1 Three \n'
- ' Val 2 line \n'
- ' Val 3 data ')
+ assert row == (
+ 'Val 1 Three \n'
+ ' Val 2 line \n'
+ ' Val 3 data '
+ )
def test_wrap_text():
@@ -87,19 +106,12 @@ def test_wrap_text():
# Test normal wrapping
row_data = ['Some text to wrap\nA new line that will wrap\nNot wrap\n 1 2 3']
row = tc.generate_row(row_data=row_data)
- assert row == ('Some text \n'
- 'to wrap \n'
- 'A new line\n'
- 'that will \n'
- 'wrap \n'
- 'Not wrap \n'
- ' 1 2 3 ')
+ assert row == ('Some text \n' 'to wrap \n' 'A new line\n' 'that will \n' 'wrap \n' 'Not wrap \n' ' 1 2 3 ')
# Test preserving a multiple space sequence across a line break
row_data = ['First last one']
row = tc.generate_row(row_data=row_data)
- assert row == ('First \n'
- ' last one ')
+ assert row == ('First \n' ' last one ')
def test_wrap_text_max_lines():
@@ -109,32 +121,27 @@ def test_wrap_text_max_lines():
# Test not needing to truncate the final line
row_data = ['First line last line']
row = tc.generate_row(row_data=row_data)
- assert row == ('First line\n'
- 'last line ')
+ assert row == ('First line\n' 'last line ')
# Test having to truncate the last word because it's too long for the final line
row_data = ['First line last lineextratext']
row = tc.generate_row(row_data=row_data)
- assert row == ('First line\n'
- 'last line…')
+ assert row == ('First line\n' 'last line…')
# Test having to truncate the last word because it fits the final line but there is more text not being included
row_data = ['First line thistxtfit extra']
row = tc.generate_row(row_data=row_data)
- assert row == ('First line\n'
- 'thistxtfi…')
+ assert row == ('First line\n' 'thistxtfi…')
# Test having to truncate the last word because it fits the final line but there are more lines not being included
row_data = ['First line thistxtfit\nextra']
row = tc.generate_row(row_data=row_data)
- assert row == ('First line\n'
- 'thistxtfi…')
+ assert row == ('First line\n' 'thistxtfi…')
# Test having space left on the final line and adding an ellipsis because there are more lines not being included
row_data = ['First line last line\nextra line']
row = tc.generate_row(row_data=row_data)
- assert row == ('First line\n'
- 'last line…')
+ assert row == ('First line\n' 'last line…')
def test_wrap_long_word():
@@ -147,8 +154,7 @@ def test_wrap_long_word():
# Test header row
header = tc.generate_row()
- assert header == ('LongColumn \n'
- 'Name Col 2 ')
+ assert header == ('LongColumn \n' 'Name Col 2 ')
# Test data row
row_data = list()
@@ -160,9 +166,22 @@ def test_wrap_long_word():
row_data.append("Word LongerThan10")
row = tc.generate_row(row_data=row_data)
- expected = (ansi.RESET_ALL + ansi.fg.green + "LongerThan" + ansi.RESET_ALL + " Word \n"
- + ansi.RESET_ALL + ansi.fg.green + "10" + ansi.fg.reset + ansi.RESET_ALL + ' ' + ansi.RESET_ALL + ' LongerThan\n'
- ' 10 ')
+ expected = (
+ ansi.RESET_ALL
+ + ansi.fg.green
+ + "LongerThan"
+ + ansi.RESET_ALL
+ + " Word \n"
+ + ansi.RESET_ALL
+ + ansi.fg.green
+ + "10"
+ + ansi.fg.reset
+ + ansi.RESET_ALL
+ + ' '
+ + ansi.RESET_ALL
+ + ' LongerThan\n'
+ ' 10 '
+ )
assert row == expected
@@ -191,8 +210,7 @@ def test_wrap_long_word_max_data_lines():
row_data.append("A LongerThan10RunsOverLast")
row = tc.generate_row(row_data=row_data)
- assert row == ('LongerThan LongerThan LongerThan A LongerT…\n'
- '10FitsLast 10FitsLas… 10RunsOve… ')
+ assert row == ('LongerThan LongerThan LongerThan A LongerT…\n' '10FitsLast 10FitsLas… 10RunsOve… ')
def test_wrap_long_char_wider_than_max_width():
@@ -235,8 +253,7 @@ def test_tabs():
column_2 = Column("Col 2")
tc = TableCreator([column_1, column_2], tab_width=2)
- row = tc.generate_row(fill_char='\t', pre_line='\t',
- inter_cell='\t', post_line='\t')
+ row = tc.generate_row(fill_char='\t', pre_line='\t', inter_cell='\t', post_line='\t')
assert row == ' Col 1 Col 2 '
@@ -252,67 +269,74 @@ def test_simple_table_creation():
st = SimpleTable([column_1, column_2])
table = st.generate_table(row_data)
- assert table == ('Col 1 Col 2 \n'
- '----------------------------------\n'
- 'Col 1 Row 1 Col 2 Row 1 \n'
- '\n'
- 'Col 1 Row 2 Col 2 Row 2 ')
+ assert table == (
+ 'Col 1 Col 2 \n'
+ '----------------------------------\n'
+ 'Col 1 Row 1 Col 2 Row 1 \n'
+ '\n'
+ 'Col 1 Row 2 Col 2 Row 2 '
+ )
# Custom divider
st = SimpleTable([column_1, column_2], divider_char='─')
table = st.generate_table(row_data)
- assert table == ('Col 1 Col 2 \n'
- '──────────────────────────────────\n'
- 'Col 1 Row 1 Col 2 Row 1 \n'
- '\n'
- 'Col 1 Row 2 Col 2 Row 2 ')
+ assert table == (
+ 'Col 1 Col 2 \n'
+ '──────────────────────────────────\n'
+ 'Col 1 Row 1 Col 2 Row 1 \n'
+ '\n'
+ 'Col 1 Row 2 Col 2 Row 2 '
+ )
# No divider
st = SimpleTable([column_1, column_2], divider_char=None)
table = st.generate_table(row_data)
- assert table == ('Col 1 Col 2 \n'
- 'Col 1 Row 1 Col 2 Row 1 \n'
- '\n'
- 'Col 1 Row 2 Col 2 Row 2 ')
+ assert table == (
+ 'Col 1 Col 2 \n' 'Col 1 Row 1 Col 2 Row 1 \n' '\n' 'Col 1 Row 2 Col 2 Row 2 '
+ )
# No row spacing
st = SimpleTable([column_1, column_2])
table = st.generate_table(row_data, row_spacing=0)
- assert table == ('Col 1 Col 2 \n'
- '----------------------------------\n'
- 'Col 1 Row 1 Col 2 Row 1 \n'
- 'Col 1 Row 2 Col 2 Row 2 ')
+ assert table == (
+ 'Col 1 Col 2 \n'
+ '----------------------------------\n'
+ 'Col 1 Row 1 Col 2 Row 1 \n'
+ 'Col 1 Row 2 Col 2 Row 2 '
+ )
# No header
st = SimpleTable([column_1, column_2])
table = st.generate_table(row_data, include_header=False)
- assert table == ('Col 1 Row 1 Col 2 Row 1 \n'
- '\n'
- 'Col 1 Row 2 Col 2 Row 2 ')
+ assert table == ('Col 1 Row 1 Col 2 Row 1 \n' '\n' 'Col 1 Row 2 Col 2 Row 2 ')
# Wide custom divider (divider needs no padding)
st = SimpleTable([column_1, column_2], divider_char='深')
table = st.generate_table(row_data)
- assert table == ('Col 1 Col 2 \n'
- '深深深深深深深深深深深深深深深深深\n'
- 'Col 1 Row 1 Col 2 Row 1 \n'
- '\n'
- 'Col 1 Row 2 Col 2 Row 2 ')
+ assert table == (
+ 'Col 1 Col 2 \n'
+ '深深深深深深深深深深深深深深深深深\n'
+ 'Col 1 Row 1 Col 2 Row 1 \n'
+ '\n'
+ 'Col 1 Row 2 Col 2 Row 2 '
+ )
# Wide custom divider (divider needs padding)
column_2 = Column("Col 2", width=17)
st = SimpleTable([column_1, column_2], divider_char='深')
table = st.generate_table(row_data)
- assert table == ('Col 1 Col 2 \n'
- '深深深深深深深深深深深深深深深深深 \n'
- 'Col 1 Row 1 Col 2 Row 1 \n'
- '\n'
- 'Col 1 Row 2 Col 2 Row 2 ')
+ assert table == (
+ 'Col 1 Col 2 \n'
+ '深深深深深深深深深深深深深深深深深 \n'
+ 'Col 1 Row 1 Col 2 Row 1 \n'
+ '\n'
+ 'Col 1 Row 2 Col 2 Row 2 '
+ )
# Invalid divider character
with pytest.raises(TypeError) as excinfo:
@@ -363,44 +387,52 @@ def test_bordered_table_creation():
# Default options
bt = BorderedTable([column_1, column_2])
table = bt.generate_table(row_data)
- assert table == ('╔═════════════════╤═════════════════╗\n'
- '║ Col 1 │ Col 2 ║\n'
- '╠═════════════════╪═════════════════╣\n'
- '║ Col 1 Row 1 │ Col 2 Row 1 ║\n'
- '╟─────────────────┼─────────────────╢\n'
- '║ Col 1 Row 2 │ Col 2 Row 2 ║\n'
- '╚═════════════════╧═════════════════╝')
+ assert table == (
+ '╔═════════════════╤═════════════════╗\n'
+ '║ Col 1 │ Col 2 ║\n'
+ '╠═════════════════╪═════════════════╣\n'
+ '║ Col 1 Row 1 │ Col 2 Row 1 ║\n'
+ '╟─────────────────┼─────────────────╢\n'
+ '║ Col 1 Row 2 │ Col 2 Row 2 ║\n'
+ '╚═════════════════╧═════════════════╝'
+ )
# No column borders
bt = BorderedTable([column_1, column_2], column_borders=False)
table = bt.generate_table(row_data)
- assert table == ('╔══════════════════════════════════╗\n'
- '║ Col 1 Col 2 ║\n'
- '╠══════════════════════════════════╣\n'
- '║ Col 1 Row 1 Col 2 Row 1 ║\n'
- '╟──────────────────────────────────╢\n'
- '║ Col 1 Row 2 Col 2 Row 2 ║\n'
- '╚══════════════════════════════════╝')
+ assert table == (
+ '╔══════════════════════════════════╗\n'
+ '║ Col 1 Col 2 ║\n'
+ '╠══════════════════════════════════╣\n'
+ '║ Col 1 Row 1 Col 2 Row 1 ║\n'
+ '╟──────────────────────────────────╢\n'
+ '║ Col 1 Row 2 Col 2 Row 2 ║\n'
+ '╚══════════════════════════════════╝'
+ )
# No header
bt = BorderedTable([column_1, column_2])
table = bt.generate_table(row_data, include_header=False)
- assert table == ('╔═════════════════╤═════════════════╗\n'
- '║ Col 1 Row 1 │ Col 2 Row 1 ║\n'
- '╟─────────────────┼─────────────────╢\n'
- '║ Col 1 Row 2 │ Col 2 Row 2 ║\n'
- '╚═════════════════╧═════════════════╝')
+ assert table == (
+ '╔═════════════════╤═════════════════╗\n'
+ '║ Col 1 Row 1 │ Col 2 Row 1 ║\n'
+ '╟─────────────────┼─────────────────╢\n'
+ '║ Col 1 Row 2 │ Col 2 Row 2 ║\n'
+ '╚═════════════════╧═════════════════╝'
+ )
# Non-default padding
bt = BorderedTable([column_1, column_2], padding=2)
table = bt.generate_table(row_data)
- assert table == ('╔═══════════════════╤═══════════════════╗\n'
- '║ Col 1 │ Col 2 ║\n'
- '╠═══════════════════╪═══════════════════╣\n'
- '║ Col 1 Row 1 │ Col 2 Row 1 ║\n'
- '╟───────────────────┼───────────────────╢\n'
- '║ Col 1 Row 2 │ Col 2 Row 2 ║\n'
- '╚═══════════════════╧═══════════════════╝')
+ assert table == (
+ '╔═══════════════════╤═══════════════════╗\n'
+ '║ Col 1 │ Col 2 ║\n'
+ '╠═══════════════════╪═══════════════════╣\n'
+ '║ Col 1 Row 1 │ Col 2 Row 1 ║\n'
+ '╟───────────────────┼───────────────────╢\n'
+ '║ Col 1 Row 2 │ Col 2 Row 2 ║\n'
+ '╚═══════════════════╧═══════════════════╝'
+ )
# Invalid padding
with pytest.raises(ValueError) as excinfo:
@@ -457,50 +489,60 @@ def test_alternating_table_creation():
# Default options
at = AlternatingTable([column_1, column_2])
table = at.generate_table(row_data)
- assert table == ('╔═════════════════╤═════════════════╗\n'
- '║ Col 1 │ Col 2 ║\n'
- '╠═════════════════╪═════════════════╣\n'
- '║ Col 1 Row 1 │ Col 2 Row 1 ║\n'
- '\x1b[100m║ \x1b[49m\x1b[0m\x1b[100mCol 1 Row 2\x1b[49m\x1b[0m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[0m\x1b[100m │ \x1b[49m\x1b[0m\x1b[100mCol 2 Row 2\x1b[49m\x1b[0m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[0m\x1b[100m ║\x1b[49m\n'
- '╚═════════════════╧═════════════════╝')
+ assert table == (
+ '╔═════════════════╤═════════════════╗\n'
+ '║ Col 1 │ Col 2 ║\n'
+ '╠═════════════════╪═════════════════╣\n'
+ '║ Col 1 Row 1 │ Col 2 Row 1 ║\n'
+ '\x1b[100m║ \x1b[49m\x1b[0m\x1b[100mCol 1 Row 2\x1b[49m\x1b[0m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[0m\x1b[100m │ \x1b[49m\x1b[0m\x1b[100mCol 2 Row 2\x1b[49m\x1b[0m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[0m\x1b[100m ║\x1b[49m\n'
+ '╚═════════════════╧═════════════════╝'
+ )
# Other bg colors
at = AlternatingTable([column_1, column_2], bg_odd=ansi.bg.bright_blue, bg_even=ansi.bg.green)
table = at.generate_table(row_data)
- assert table == ('╔═════════════════╤═════════════════╗\n'
- '║ Col 1 │ Col 2 ║\n'
- '╠═════════════════╪═════════════════╣\n'
- '\x1b[104m║ \x1b[49m\x1b[0m\x1b[104mCol 1 Row 1\x1b[49m\x1b[0m\x1b[104m \x1b[49m\x1b[104m \x1b[49m\x1b[104m \x1b[49m\x1b[104m \x1b[49m\x1b[0m\x1b[104m │ \x1b[49m\x1b[0m\x1b[104mCol 2 Row 1\x1b[49m\x1b[0m\x1b[104m \x1b[49m\x1b[104m \x1b[49m\x1b[104m \x1b[49m\x1b[104m \x1b[49m\x1b[0m\x1b[104m ║\x1b[49m\n'
- '\x1b[42m║ \x1b[49m\x1b[0m\x1b[42mCol 1 Row 2\x1b[49m\x1b[0m\x1b[42m \x1b[49m\x1b[42m \x1b[49m\x1b[42m \x1b[49m\x1b[42m \x1b[49m\x1b[0m\x1b[42m │ \x1b[49m\x1b[0m\x1b[42mCol 2 Row 2\x1b[49m\x1b[0m\x1b[42m \x1b[49m\x1b[42m \x1b[49m\x1b[42m \x1b[49m\x1b[42m \x1b[49m\x1b[0m\x1b[42m ║\x1b[49m\n'
- '╚═════════════════╧═════════════════╝')
+ assert table == (
+ '╔═════════════════╤═════════════════╗\n'
+ '║ Col 1 │ Col 2 ║\n'
+ '╠═════════════════╪═════════════════╣\n'
+ '\x1b[104m║ \x1b[49m\x1b[0m\x1b[104mCol 1 Row 1\x1b[49m\x1b[0m\x1b[104m \x1b[49m\x1b[104m \x1b[49m\x1b[104m \x1b[49m\x1b[104m \x1b[49m\x1b[0m\x1b[104m │ \x1b[49m\x1b[0m\x1b[104mCol 2 Row 1\x1b[49m\x1b[0m\x1b[104m \x1b[49m\x1b[104m \x1b[49m\x1b[104m \x1b[49m\x1b[104m \x1b[49m\x1b[0m\x1b[104m ║\x1b[49m\n'
+ '\x1b[42m║ \x1b[49m\x1b[0m\x1b[42mCol 1 Row 2\x1b[49m\x1b[0m\x1b[42m \x1b[49m\x1b[42m \x1b[49m\x1b[42m \x1b[49m\x1b[42m \x1b[49m\x1b[0m\x1b[42m │ \x1b[49m\x1b[0m\x1b[42mCol 2 Row 2\x1b[49m\x1b[0m\x1b[42m \x1b[49m\x1b[42m \x1b[49m\x1b[42m \x1b[49m\x1b[42m \x1b[49m\x1b[0m\x1b[42m ║\x1b[49m\n'
+ '╚═════════════════╧═════════════════╝'
+ )
# No column borders
at = AlternatingTable([column_1, column_2], column_borders=False)
table = at.generate_table(row_data)
- assert table == ('╔══════════════════════════════════╗\n'
- '║ Col 1 Col 2 ║\n'
- '╠══════════════════════════════════╣\n'
- '║ Col 1 Row 1 Col 2 Row 1 ║\n'
- '\x1b[100m║ \x1b[49m\x1b[0m\x1b[100mCol 1 Row 2\x1b[49m\x1b[0m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[0m\x1b[100m \x1b[49m\x1b[0m\x1b[100mCol 2 Row 2\x1b[49m\x1b[0m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[0m\x1b[100m ║\x1b[49m\n'
- '╚══════════════════════════════════╝')
+ assert table == (
+ '╔══════════════════════════════════╗\n'
+ '║ Col 1 Col 2 ║\n'
+ '╠══════════════════════════════════╣\n'
+ '║ Col 1 Row 1 Col 2 Row 1 ║\n'
+ '\x1b[100m║ \x1b[49m\x1b[0m\x1b[100mCol 1 Row 2\x1b[49m\x1b[0m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[0m\x1b[100m \x1b[49m\x1b[0m\x1b[100mCol 2 Row 2\x1b[49m\x1b[0m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[0m\x1b[100m ║\x1b[49m\n'
+ '╚══════════════════════════════════╝'
+ )
# No header
at = AlternatingTable([column_1, column_2])
table = at.generate_table(row_data, include_header=False)
- assert table == ('╔═════════════════╤═════════════════╗\n'
- '║ Col 1 Row 1 │ Col 2 Row 1 ║\n'
- '\x1b[100m║ \x1b[49m\x1b[0m\x1b[100mCol 1 Row 2\x1b[49m\x1b[0m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[0m\x1b[100m │ \x1b[49m\x1b[0m\x1b[100mCol 2 Row 2\x1b[49m\x1b[0m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[0m\x1b[100m ║\x1b[49m\n'
- '╚═════════════════╧═════════════════╝')
+ assert table == (
+ '╔═════════════════╤═════════════════╗\n'
+ '║ Col 1 Row 1 │ Col 2 Row 1 ║\n'
+ '\x1b[100m║ \x1b[49m\x1b[0m\x1b[100mCol 1 Row 2\x1b[49m\x1b[0m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[0m\x1b[100m │ \x1b[49m\x1b[0m\x1b[100mCol 2 Row 2\x1b[49m\x1b[0m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[0m\x1b[100m ║\x1b[49m\n'
+ '╚═════════════════╧═════════════════╝'
+ )
# Non-default padding
at = AlternatingTable([column_1, column_2], padding=2)
table = at.generate_table(row_data)
- assert table == ('╔═══════════════════╤═══════════════════╗\n'
- '║ Col 1 │ Col 2 ║\n'
- '╠═══════════════════╪═══════════════════╣\n'
- '║ Col 1 Row 1 │ Col 2 Row 1 ║\n'
- '\x1b[100m║ \x1b[49m\x1b[0m\x1b[100mCol 1 Row 2\x1b[49m\x1b[0m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[0m\x1b[100m │ \x1b[49m\x1b[0m\x1b[100mCol 2 Row 2\x1b[49m\x1b[0m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[0m\x1b[100m ║\x1b[49m\n'
- '╚═══════════════════╧═══════════════════╝')
+ assert table == (
+ '╔═══════════════════╤═══════════════════╗\n'
+ '║ Col 1 │ Col 2 ║\n'
+ '╠═══════════════════╪═══════════════════╣\n'
+ '║ Col 1 Row 1 │ Col 2 Row 1 ║\n'
+ '\x1b[100m║ \x1b[49m\x1b[0m\x1b[100mCol 1 Row 2\x1b[49m\x1b[0m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[0m\x1b[100m │ \x1b[49m\x1b[0m\x1b[100mCol 2 Row 2\x1b[49m\x1b[0m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[0m\x1b[100m ║\x1b[49m\n'
+ '╚═══════════════════╧═══════════════════╝'
+ )
# Invalid padding
with pytest.raises(ValueError) as excinfo:
diff --git a/tests/test_transcript.py b/tests/test_transcript.py
index 55d60e18..5b19e11e 100644
--- a/tests/test_transcript.py
+++ b/tests/test_transcript.py
@@ -46,7 +46,7 @@ class CmdLineApp(cmd2.Cmd):
"""Repeats what you tell me to."""
arg = ' '.join(arg)
if opts.piglatin:
- arg = '%s%say' % (arg[1:], arg[0])
+ arg = '{}{}ay'.format(arg[1:], arg[0])
if opts.shout:
arg = arg.upper()
repetitions = opts.repeat or 1
@@ -66,16 +66,16 @@ class CmdLineApp(cmd2.Cmd):
def do_mumble(self, opts, arg):
"""Mumbles what you tell me to."""
repetitions = opts.repeat or 1
- #arg = arg.split()
+ # arg = arg.split()
for _ in range(min(repetitions, self.maxrepeats)):
output = []
- if random.random() < .33:
+ if random.random() < 0.33:
output.append(random.choice(self.MUMBLE_FIRST))
for word in arg:
- if random.random() < .40:
+ if random.random() < 0.40:
output.append(random.choice(self.MUMBLES))
output.append(word)
- if random.random() < .25:
+ if random.random() < 0.25:
output.append(random.choice(self.MUMBLE_LAST))
self.poutput(' '.join(output))
@@ -98,23 +98,27 @@ def test_commands_at_invocation():
out = app.stdout.getvalue()
assert out == expected
-@pytest.mark.parametrize('filename,feedback_to_output', [
- ('bol_eol.txt', False),
- ('characterclass.txt', False),
- ('dotstar.txt', False),
- ('extension_notation.txt', False),
- ('from_cmdloop.txt', True),
- ('multiline_no_regex.txt', False),
- ('multiline_regex.txt', False),
- ('no_output.txt', False),
- ('no_output_last.txt', False),
- ('regex_set.txt', False),
- ('singleslash.txt', False),
- ('slashes_escaped.txt', False),
- ('slashslash.txt', False),
- ('spaces.txt', False),
- ('word_boundaries.txt', False),
- ])
+
+@pytest.mark.parametrize(
+ 'filename,feedback_to_output',
+ [
+ ('bol_eol.txt', False),
+ ('characterclass.txt', False),
+ ('dotstar.txt', False),
+ ('extension_notation.txt', False),
+ ('from_cmdloop.txt', True),
+ ('multiline_no_regex.txt', False),
+ ('multiline_regex.txt', False),
+ ('no_output.txt', False),
+ ('no_output_last.txt', False),
+ ('regex_set.txt', False),
+ ('singleslash.txt', False),
+ ('slashes_escaped.txt', False),
+ ('slashslash.txt', False),
+ ('spaces.txt', False),
+ ('word_boundaries.txt', False),
+ ],
+)
def test_transcript(request, capsys, filename, feedback_to_output):
# Get location of the transcript
test_dir = os.path.dirname(request.module.__file__)
@@ -141,6 +145,7 @@ def test_transcript(request, capsys, filename, feedback_to_output):
assert err.startswith(expected_start)
assert err.endswith(expected_end)
+
def test_history_transcript():
app = CmdLineApp()
app.stdout = StdSim(app.stdout)
@@ -168,6 +173,7 @@ this is a \/multiline\/ command
assert xscript == expected
+
def test_history_transcript_bad_filename():
app = CmdLineApp()
app.stdout = StdSim(app.stdout)
@@ -247,27 +253,30 @@ def test_generate_transcript_stop(capsys):
assert err.startswith("Interrupting this command\nCommand 2 triggered a stop")
-@pytest.mark.parametrize('expected, transformed', [
- # strings with zero or one slash or with escaped slashes means no regular
- # expression present, so the result should just be what re.escape returns.
- # we don't use static strings in these tests because re.escape behaves
- # differently in python 3.7 than in prior versions
- ( 'text with no slashes', re.escape('text with no slashes') ),
- ( 'specials .*', re.escape('specials .*') ),
- ( 'use 2/3 cup', re.escape('use 2/3 cup') ),
- ( '/tmp is nice', re.escape('/tmp is nice') ),
- ( 'slash at end/', re.escape('slash at end/') ),
- # escaped slashes
- ( r'not this slash\/ or this one\/', re.escape('not this slash/ or this one/' ) ),
- # regexes
- ( '/.*/', '.*' ),
- ( 'specials ^ and + /[0-9]+/', re.escape('specials ^ and + ') + '[0-9]+' ),
- ( r'/a{6}/ but not \/a{6} with /.*?/ more', 'a{6}' + re.escape(' but not /a{6} with ') + '.*?' + re.escape(' more') ),
- ( r'not \/, use /\|?/, not \/', re.escape('not /, use ') + r'\|?' + re.escape(', not /') ),
- # inception: slashes in our regex. backslashed on input, bare on output
- ( r'not \/, use /\/?/, not \/', re.escape('not /, use ') + '/?' + re.escape(', not /') ),
- ( r'lots /\/?/ more /.*/ stuff', re.escape('lots ') + '/?' + re.escape(' more ') + '.*' + re.escape(' stuff') ),
- ])
+@pytest.mark.parametrize(
+ 'expected, transformed',
+ [
+ # strings with zero or one slash or with escaped slashes means no regular
+ # expression present, so the result should just be what re.escape returns.
+ # we don't use static strings in these tests because re.escape behaves
+ # differently in python 3.7 than in prior versions
+ ('text with no slashes', re.escape('text with no slashes')),
+ ('specials .*', re.escape('specials .*')),
+ ('use 2/3 cup', re.escape('use 2/3 cup')),
+ ('/tmp is nice', re.escape('/tmp is nice')),
+ ('slash at end/', re.escape('slash at end/')),
+ # escaped slashes
+ (r'not this slash\/ or this one\/', re.escape('not this slash/ or this one/')),
+ # regexes
+ ('/.*/', '.*'),
+ ('specials ^ and + /[0-9]+/', re.escape('specials ^ and + ') + '[0-9]+'),
+ (r'/a{6}/ but not \/a{6} with /.*?/ more', 'a{6}' + re.escape(' but not /a{6} with ') + '.*?' + re.escape(' more')),
+ (r'not \/, use /\|?/, not \/', re.escape('not /, use ') + r'\|?' + re.escape(', not /')),
+ # inception: slashes in our regex. backslashed on input, bare on output
+ (r'not \/, use /\/?/, not \/', re.escape('not /, use ') + '/?' + re.escape(', not /')),
+ (r'lots /\/?/ more /.*/ stuff', re.escape('lots ') + '/?' + re.escape(' more ') + '.*' + re.escape(' stuff')),
+ ],
+)
def test_parse_transcript_expected(expected, transformed):
app = CmdLineApp()
diff --git a/tests/test_utils.py b/tests/test_utils.py
index 5336ccfd..383ea6d7 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -26,37 +26,44 @@ def test_strip_quotes_no_quotes():
stripped = cu.strip_quotes(base_str)
assert base_str == stripped
+
def test_strip_quotes_with_quotes():
base_str = '"' + HELLO_WORLD + '"'
stripped = cu.strip_quotes(base_str)
assert stripped == HELLO_WORLD
+
def test_remove_duplicates_no_duplicates():
no_dups = [5, 4, 3, 2, 1]
assert cu.remove_duplicates(no_dups) == no_dups
+
def test_remove_duplicates_with_duplicates():
duplicates = [1, 1, 2, 3, 9, 9, 7, 8]
assert cu.remove_duplicates(duplicates) == [1, 2, 3, 9, 7, 8]
+
def test_unicode_normalization():
s1 = 'café'
s2 = 'cafe\u0301'
assert s1 != s2
assert cu.norm_fold(s1) == cu.norm_fold(s2)
+
def test_unicode_casefold():
micro = 'µ'
micro_cf = micro.casefold()
assert micro != micro_cf
assert cu.norm_fold(micro) == cu.norm_fold(micro_cf)
+
def test_alphabetical_sort():
my_list = ['café', 'µ', 'A', 'micro', 'unity', 'cafeteria']
assert cu.alphabetical_sort(my_list) == ['A', 'cafeteria', 'café', 'micro', 'unity', 'µ']
my_list = ['a3', 'a22', 'A2', 'A11', 'a1']
assert cu.alphabetical_sort(my_list) == ['a1', 'A11', 'A2', 'a22', 'a3']
+
def test_try_int_or_force_to_lower_case():
str1 = '17'
assert cu.try_int_or_force_to_lower_case(str1) == 17
@@ -67,6 +74,7 @@ def test_try_int_or_force_to_lower_case():
str1 = ''
assert cu.try_int_or_force_to_lower_case(str1) == ''
+
def test_natural_keys():
my_list = ['café', 'µ', 'A', 'micro', 'unity', 'x1', 'X2', 'X11', 'X0', 'x22']
my_list.sort(key=cu.natural_keys)
@@ -75,24 +83,28 @@ def test_natural_keys():
my_list.sort(key=cu.natural_keys)
assert my_list == ['a1', 'A2', 'a3', 'A11', 'a22']
+
def test_natural_sort():
my_list = ['café', 'µ', 'A', 'micro', 'unity', 'x1', 'X2', 'X11', 'X0', 'x22']
assert cu.natural_sort(my_list) == ['A', 'café', 'micro', 'unity', 'X0', 'x1', 'X2', 'X11', 'x22', 'µ']
my_list = ['a3', 'a22', 'A2', 'A11', 'a1']
assert cu.natural_sort(my_list) == ['a1', 'A2', 'a3', 'A11', 'a22']
+
def test_is_quoted_short():
my_str = ''
assert not cu.is_quoted(my_str)
your_str = '"'
assert not cu.is_quoted(your_str)
+
def test_is_quoted_yes():
my_str = '"This is a test"'
assert cu.is_quoted(my_str)
your_str = "'of the emergengy broadcast system'"
assert cu.is_quoted(your_str)
+
def test_is_quoted_no():
my_str = '"This is a test'
assert not cu.is_quoted(my_str)
@@ -101,6 +113,7 @@ def test_is_quoted_no():
simple_str = "hello world"
assert not cu.is_quoted(simple_str)
+
def test_quote_string():
my_str = "Hello World"
assert cu.quote_string(my_str) == '"' + my_str + '"'
@@ -111,12 +124,14 @@ def test_quote_string():
my_str = '"Hello World"'
assert cu.quote_string(my_str) == "'" + my_str + "'"
+
def test_quote_string_if_needed_yes():
my_str = "Hello World"
assert cu.quote_string_if_needed(my_str) == '"' + my_str + '"'
your_str = '"foo" bar'
assert cu.quote_string_if_needed(your_str) == "'" + your_str + "'"
+
def test_quote_string_if_needed_no():
my_str = "HelloWorld"
assert cu.quote_string_if_needed(my_str) == my_str
@@ -135,22 +150,26 @@ def test_stdsim_write_str(stdout_sim):
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()
assert stdout_sim.getbytes() == b_str
+
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)
@@ -176,6 +195,7 @@ def test_stdsim_read_bytes(stdout_sim):
assert stdout_sim.readbytes() == b_str
assert stdout_sim.getbytes() == b''
+
def test_stdsim_clear(stdout_sim):
my_str = 'Hello World'
stdout_sim.write(my_str)
@@ -183,6 +203,7 @@ def test_stdsim_clear(stdout_sim):
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'
@@ -190,10 +211,12 @@ def test_stdsim_getattr_exist(stdout_sim):
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()
+
def test_stdsim_pause_storage(stdout_sim):
# Test pausing storage for string data
my_str = 'Hello World'
@@ -217,11 +240,13 @@ def test_stdsim_pause_storage(stdout_sim):
stdout_sim.buffer.write(b_str)
assert stdout_sim.getbytes() == b''
+
def test_stdsim_line_buffering(base_app):
# This exercises the case of writing binary data that contains new lines/carriage returns to a StdSim
# when line buffering is on. The output should immediately be flushed to the underlying stream.
import os
import tempfile
+
file = tempfile.NamedTemporaryFile(mode='wt')
file.line_buffering = True
@@ -256,6 +281,7 @@ def pr_none():
pr = cu.ProcReader(proc, None, None)
return pr
+
def test_proc_reader_send_sigint(pr_none):
assert pr_none._proc.poll() is None
pr_none.send_sigint()
@@ -274,6 +300,7 @@ def test_proc_reader_send_sigint(pr_none):
else:
assert ret_code == -signal.SIGINT
+
def test_proc_reader_terminate(pr_none):
assert pr_none._proc.poll() is None
pr_none.terminate()
@@ -297,11 +324,13 @@ def test_proc_reader_terminate(pr_none):
def context_flag():
return cu.ContextFlag()
+
def test_context_flag_bool(context_flag):
assert not context_flag
with context_flag:
assert context_flag
+
def test_context_flag_exit_err(context_flag):
with pytest.raises(ValueError):
context_flag.__exit__()
@@ -313,30 +342,35 @@ def test_truncate_line():
truncated = cu.truncate_line(line, max_width)
assert truncated == 'lo' + HORIZONTAL_ELLIPSIS
+
def test_truncate_line_already_fits():
line = 'long'
max_width = 4
truncated = cu.truncate_line(line, max_width)
assert truncated == line
+
def test_truncate_line_with_newline():
line = 'fo\no'
max_width = 2
with pytest.raises(ValueError):
cu.truncate_line(line, max_width)
+
def test_truncate_line_width_is_too_small():
line = 'foo'
max_width = 0
with pytest.raises(ValueError):
cu.truncate_line(line, max_width)
+
def test_truncate_line_wide_text():
line = '苹苹other'
max_width = 6
truncated = cu.truncate_line(line, max_width)
assert truncated == '苹苹o' + HORIZONTAL_ELLIPSIS
+
def test_truncate_line_split_wide_text():
"""Test when truncation results in a string which is shorter than max_width"""
line = '1苹2苹'
@@ -344,12 +378,14 @@ def test_truncate_line_split_wide_text():
truncated = cu.truncate_line(line, max_width)
assert truncated == '1' + HORIZONTAL_ELLIPSIS
+
def test_truncate_line_tabs():
line = 'has\ttab'
max_width = 9
truncated = cu.truncate_line(line, max_width)
assert truncated == 'has t' + HORIZONTAL_ELLIPSIS
+
def test_truncate_with_style():
from cmd2 import ansi
@@ -374,6 +410,7 @@ def test_truncate_with_style():
truncated = cu.truncate_line(line, max_width)
assert truncated == 'lo' + HORIZONTAL_ELLIPSIS + after_style
+
def test_align_text_fill_char_is_tab():
text = 'foo'
fill_char = '\t'
@@ -381,6 +418,7 @@ def test_align_text_fill_char_is_tab():
aligned = cu.align_text(text, cu.TextAlignment.LEFT, fill_char=fill_char, width=width)
assert aligned == text + ' '
+
def test_align_text_with_style():
from cmd2 import ansi
@@ -422,8 +460,8 @@ def test_align_text_with_style():
line_1_text = ansi.fg.bright_blue + 'line1'
line_2_text = ansi.fg.bright_blue + 'line2' + ansi.FG_RESET
- assert aligned == (left_fill + line_1_text + right_fill + '\n' +
- left_fill + line_2_text + right_fill)
+ assert aligned == (left_fill + line_1_text + right_fill + '\n' + left_fill + line_2_text + right_fill)
+
def test_align_text_width_is_too_small():
text = 'foo'
@@ -432,6 +470,7 @@ def test_align_text_width_is_too_small():
with pytest.raises(ValueError):
cu.align_text(text, cu.TextAlignment.LEFT, fill_char=fill_char, width=width)
+
def test_align_text_fill_char_is_too_long():
text = 'foo'
fill_char = 'fill'
@@ -439,6 +478,7 @@ def test_align_text_fill_char_is_too_long():
with pytest.raises(TypeError):
cu.align_text(text, cu.TextAlignment.LEFT, fill_char=fill_char, width=width)
+
def test_align_text_fill_char_is_newline():
text = 'foo'
fill_char = '\n'
@@ -446,6 +486,7 @@ def test_align_text_fill_char_is_newline():
with pytest.raises(ValueError):
cu.align_text(text, cu.TextAlignment.LEFT, fill_char=fill_char, width=width)
+
def test_align_text_has_tabs():
text = '\t\tfoo'
fill_char = '-'
@@ -453,6 +494,7 @@ def test_align_text_has_tabs():
aligned = cu.align_text(text, cu.TextAlignment.LEFT, fill_char=fill_char, width=width, tab_width=2)
assert aligned == ' ' + 'foo' + '---'
+
def test_align_text_blank():
text = ''
fill_char = '-'
@@ -460,6 +502,7 @@ def test_align_text_blank():
aligned = cu.align_text(text, cu.TextAlignment.LEFT, fill_char=fill_char, width=width)
assert aligned == fill_char * width
+
def test_align_text_wider_than_width():
text = 'long text field'
fill_char = '-'
@@ -467,6 +510,7 @@ def test_align_text_wider_than_width():
aligned = cu.align_text(text, cu.TextAlignment.LEFT, fill_char=fill_char, width=width)
assert aligned == text
+
def test_align_text_wider_than_width_truncate():
text = 'long text field'
fill_char = '-'
@@ -474,6 +518,7 @@ def test_align_text_wider_than_width_truncate():
aligned = cu.align_text(text, cu.TextAlignment.LEFT, fill_char=fill_char, width=width, truncate=True)
assert aligned == 'long te' + HORIZONTAL_ELLIPSIS
+
def test_align_text_wider_than_width_truncate_add_fill():
"""Test when truncation results in a string which is shorter than width and align_text adds filler"""
text = '1苹2苹'
@@ -482,6 +527,7 @@ def test_align_text_wider_than_width_truncate_add_fill():
aligned = cu.align_text(text, cu.TextAlignment.LEFT, fill_char=fill_char, width=width, truncate=True)
assert aligned == '1' + HORIZONTAL_ELLIPSIS + fill_char
+
def test_align_text_has_unprintable():
text = 'foo\x02'
fill_char = '-'
@@ -489,9 +535,12 @@ def test_align_text_has_unprintable():
with pytest.raises(ValueError):
cu.align_text(text, cu.TextAlignment.LEFT, fill_char=fill_char, width=width)
+
def test_align_text_term_width():
import shutil
+
from cmd2 import ansi
+
text = 'foo'
fill_char = ' '
@@ -501,6 +550,7 @@ def test_align_text_term_width():
aligned = cu.align_text(text, cu.TextAlignment.LEFT, fill_char=fill_char)
assert aligned == text + expected_fill
+
def test_align_left():
text = 'foo'
fill_char = '-'
@@ -508,13 +558,14 @@ def test_align_left():
aligned = cu.align_left(text, fill_char=fill_char, width=width)
assert aligned == text + fill_char + fill_char
+
def test_align_left_multiline():
text = "foo\nshoes"
fill_char = '-'
width = 7
aligned = cu.align_left(text, fill_char=fill_char, width=width)
- assert aligned == ('foo----\n'
- 'shoes--')
+ assert aligned == ('foo----\n' 'shoes--')
+
def test_align_left_wide_text():
text = '苹'
@@ -523,6 +574,7 @@ def test_align_left_wide_text():
aligned = cu.align_left(text, fill_char=fill_char, width=width)
assert aligned == text + fill_char + fill_char
+
def test_align_left_wide_fill():
text = 'foo'
fill_char = '苹'
@@ -530,6 +582,7 @@ def test_align_left_wide_fill():
aligned = cu.align_left(text, fill_char=fill_char, width=width)
assert aligned == text + fill_char
+
def test_align_left_wide_fill_needs_padding():
"""Test when fill_char's display width does not divide evenly into gap"""
text = 'foo'
@@ -538,6 +591,7 @@ def test_align_left_wide_fill_needs_padding():
aligned = cu.align_left(text, fill_char=fill_char, width=width)
assert aligned == text + fill_char + ' '
+
def test_align_center():
text = 'foo'
fill_char = '-'
@@ -545,13 +599,14 @@ def test_align_center():
aligned = cu.align_center(text, fill_char=fill_char, width=width)
assert aligned == fill_char + text + fill_char
+
def test_align_center_multiline():
text = "foo\nshoes"
fill_char = '-'
width = 7
aligned = cu.align_center(text, fill_char=fill_char, width=width)
- assert aligned == ('--foo--\n'
- '-shoes-')
+ assert aligned == ('--foo--\n' '-shoes-')
+
def test_align_center_wide_text():
text = '苹'
@@ -560,6 +615,7 @@ def test_align_center_wide_text():
aligned = cu.align_center(text, fill_char=fill_char, width=width)
assert aligned == fill_char + text + fill_char
+
def test_align_center_wide_fill():
text = 'foo'
fill_char = '苹'
@@ -567,6 +623,7 @@ def test_align_center_wide_fill():
aligned = cu.align_center(text, fill_char=fill_char, width=width)
assert aligned == fill_char + text + fill_char
+
def test_align_center_wide_fill_needs_right_padding():
"""Test when fill_char's display width does not divide evenly into right gap"""
text = 'foo'
@@ -575,6 +632,7 @@ def test_align_center_wide_fill_needs_right_padding():
aligned = cu.align_center(text, fill_char=fill_char, width=width)
assert aligned == fill_char + text + fill_char + ' '
+
def test_align_center_wide_fill_needs_left_and_right_padding():
"""Test when fill_char's display width does not divide evenly into either gap"""
text = 'foo'
@@ -583,6 +641,7 @@ def test_align_center_wide_fill_needs_left_and_right_padding():
aligned = cu.align_center(text, fill_char=fill_char, width=width)
assert aligned == fill_char + ' ' + text + fill_char + ' '
+
def test_align_right():
text = 'foo'
fill_char = '-'
@@ -590,13 +649,14 @@ def test_align_right():
aligned = cu.align_right(text, fill_char=fill_char, width=width)
assert aligned == fill_char + fill_char + text
+
def test_align_right_multiline():
text = "foo\nshoes"
fill_char = '-'
width = 7
aligned = cu.align_right(text, fill_char=fill_char, width=width)
- assert aligned == ('----foo\n'
- '--shoes')
+ assert aligned == ('----foo\n' '--shoes')
+
def test_align_right_wide_text():
text = '苹'
@@ -605,6 +665,7 @@ def test_align_right_wide_text():
aligned = cu.align_right(text, fill_char=fill_char, width=width)
assert aligned == fill_char + fill_char + text
+
def test_align_right_wide_fill():
text = 'foo'
fill_char = '苹'
@@ -612,6 +673,7 @@ def test_align_right_wide_fill():
aligned = cu.align_right(text, fill_char=fill_char, width=width)
assert aligned == fill_char + text
+
def test_align_right_wide_fill_needs_padding():
"""Test when fill_char's display width does not divide evenly into gap"""
text = 'foo'
@@ -627,26 +689,31 @@ def test_str_to_bool_true():
assert cu.str_to_bool('TRUE')
assert cu.str_to_bool('tRuE')
+
def test_str_to_bool_false():
assert not cu.str_to_bool('false')
assert not cu.str_to_bool('False')
assert not cu.str_to_bool('FALSE')
assert not cu.str_to_bool('fAlSe')
+
def test_str_to_bool_invalid():
with pytest.raises(ValueError):
cu.str_to_bool('other')
+
def test_str_to_bool_bad_input():
with pytest.raises(ValueError):
cu.str_to_bool(1)
+
def test_find_editor_specified():
expected_editor = os.path.join('fake_dir', 'editor')
with mock.patch.dict(os.environ, {'EDITOR': expected_editor}):
editor = cu.find_editor()
assert editor == expected_editor
+
def test_find_editor_not_specified():
# Use existing path env setting. Something in the editor list should be found.
editor = cu.find_editor()
diff --git a/tests/test_utils_defining_class.py b/tests/test_utils_defining_class.py
index 0fbcf83b..5d667678 100644
--- a/tests/test_utils_defining_class.py
+++ b/tests/test_utils_defining_class.py
@@ -77,7 +77,7 @@ def test_get_defining_class():
assert cu.get_defining_class(partial_unbound) is ParentClass
assert cu.get_defining_class(nested_partial_unbound) is ParentClass
- partial_bound = functools.partial(parent_instance.parent_only_func, 1)
- nested_partial_bound = functools.partial(partial_bound, 2)
+ partial_bound = functools.partial(parent_instance.parent_only_func, 1)
+ nested_partial_bound = functools.partial(partial_bound, 2)
assert cu.get_defining_class(partial_bound) is ParentClass
assert cu.get_defining_class(nested_partial_bound) is ParentClass