summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorxNinjaKittyx <xNinjaKittyx@users.noreply.github.com>2020-12-15 17:21:33 -0800
committerxNinjaKittyx <xNinjaKittyx@users.noreply.github.com>2020-12-15 18:20:13 -0800
commit9aa54a5b27468d61337528cb1e1b5b9b11a80978 (patch)
tree567693115cc101efb9254a96d96d80e9f9ccd557 /tests
parent03c65c60b39e369958b056c5c844d36d515c8a63 (diff)
downloadcmd2-git-ci_improvements.tar.gz
Adds pre-commit config to run various lintersci_improvements
This ads black, isort, pyupgrade, and flake8 to pre-commit-config.yaml There are also some small changes to travis.yml and tasks.py to reduce some repeated configurations that should be consolidated into setup.cfg. Most other changes are automated by the linter scripts.
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