From b7fa4e46593151086a4186c9d90dc72b809c9b45 Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Tue, 2 Jul 2019 10:47:52 -0400 Subject: Moved basic_complete to utils --- tests/test_completion.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'tests') diff --git a/tests/test_completion.py b/tests/test_completion.py index eea34ba6..9bf6fc5f 100644 --- a/tests/test_completion.py +++ b/tests/test_completion.py @@ -67,7 +67,7 @@ class CompletionsExample(cmd2.Cmd): pass def complete_test_basic(self, text, line, begidx, endidx): - return self.basic_complete(text, line, begidx, endidx, food_item_strs) + return utils.basic_complete(text, line, begidx, endidx, food_item_strs) def do_test_delimited(self, args): pass @@ -80,7 +80,7 @@ class CompletionsExample(cmd2.Cmd): def complete_test_sort_key(self, text, line, begidx, endidx): num_strs = ['2', '11', '1'] - return self.basic_complete(text, line, begidx, endidx, num_strs) + return utils.basic_complete(text, line, begidx, endidx, num_strs) def do_test_raise_exception(self, args): pass @@ -524,7 +524,7 @@ def test_basic_completion_single(cmd2_app): endidx = len(line) begidx = endidx - len(text) - assert cmd2_app.basic_complete(text, line, begidx, endidx, food_item_strs) == ['Pizza'] + assert utils.basic_complete(text, line, begidx, endidx, food_item_strs) == ['Pizza'] def test_basic_completion_multiple(cmd2_app): text = '' @@ -532,7 +532,7 @@ def test_basic_completion_multiple(cmd2_app): endidx = len(line) begidx = endidx - len(text) - matches = sorted(cmd2_app.basic_complete(text, line, begidx, endidx, food_item_strs)) + 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): @@ -541,7 +541,7 @@ def test_basic_completion_nomatch(cmd2_app): endidx = len(line) begidx = endidx - len(text) - assert cmd2_app.basic_complete(text, line, begidx, endidx, food_item_strs) == [] + assert utils.basic_complete(text, line, begidx, endidx, food_item_strs) == [] def test_delimiter_completion(cmd2_app): text = '/home/' -- cgit v1.2.1 From c233b75147f01e8b34beb8ada6cec3468371c896 Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Thu, 4 Jul 2019 15:22:01 -0400 Subject: Fixing unit tests --- tests/test_completion.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'tests') diff --git a/tests/test_completion.py b/tests/test_completion.py index 9bf6fc5f..03208a88 100644 --- a/tests/test_completion.py +++ b/tests/test_completion.py @@ -516,7 +516,7 @@ def test_path_completion_directories_only(cmd2_app, request): expected = [text + 'cripts' + os.path.sep] - assert cmd2_app.path_complete(text, line, begidx, endidx, os.path.isdir) == expected + assert cmd2_app.path_complete(text, line, begidx, endidx, path_filter=os.path.isdir) == expected def test_basic_completion_single(cmd2_app): text = 'Pi' @@ -592,7 +592,7 @@ def test_flag_based_default_completer(cmd2_app, request): begidx = endidx - len(text) assert cmd2_app.flag_based_complete(text, line, begidx, endidx, - flag_dict, cmd2_app.path_complete) == [text + 'onftest.py'] + 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__) @@ -642,7 +642,7 @@ def test_index_based_default_completer(cmd2_app, request): begidx = endidx - len(text) assert cmd2_app.index_based_complete(text, line, begidx, endidx, - index_dict, cmd2_app.path_complete) == [text + 'onftest.py'] + 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__) @@ -1072,8 +1072,7 @@ class SubcommandsWithUnknownExample(cmd2.Cmd): # create the parser for the "sport" sub-command parser_sport = base_subparsers.add_parser('sport', help='sport help') - sport_arg = parser_sport.add_argument('sport', help='Enter name of a sport') - setattr(sport_arg, 'arg_choices', sport_item_strs) + sport_arg = parser_sport.add_argument('sport', help='Enter name of a sport', choices=sport_item_strs) @cmd2.with_argparser_and_unknown_args(base_parser) def do_base(self, args): -- cgit v1.2.1 From 60d731da49436322cdd11da3c4b8c67388d36534 Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Thu, 4 Jul 2019 15:24:19 -0400 Subject: Fixing unit tests --- tests/test_acargparse.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) (limited to 'tests') diff --git a/tests/test_acargparse.py b/tests/test_acargparse.py index 436158db..c8f09f76 100644 --- a/tests/test_acargparse.py +++ b/tests/test_acargparse.py @@ -3,56 +3,57 @@ Unit/functional testing for argparse customizations in cmd2 """ import pytest -from cmd2.argparse_completer import ACArgumentParser, is_potential_flag +from cmd2.argparse_custom import Cmd2ArgParser +from cmd2.argparse_completer import is_potential_flag def test_acarg_narg_empty_tuple(): with pytest.raises(ValueError) as excinfo: - parser = ACArgumentParser(prog='test') + parser = Cmd2ArgParser(prog='test') parser.add_argument('invalid_tuple', nargs=()) assert 'Ranged values for nargs must be a tuple of 2 integers' in str(excinfo.value) def test_acarg_narg_single_tuple(): with pytest.raises(ValueError) as excinfo: - parser = ACArgumentParser(prog='test') + parser = Cmd2ArgParser(prog='test') parser.add_argument('invalid_tuple', nargs=(1,)) assert 'Ranged values for nargs must be a tuple of 2 integers' in str(excinfo.value) def test_acarg_narg_tuple_triple(): with pytest.raises(ValueError) as excinfo: - parser = ACArgumentParser(prog='test') + parser = Cmd2ArgParser(prog='test') parser.add_argument('invalid_tuple', nargs=(1, 2, 3)) assert 'Ranged values for nargs must be a tuple of 2 integers' in str(excinfo.value) def test_acarg_narg_tuple_order(): with pytest.raises(ValueError) as excinfo: - parser = ACArgumentParser(prog='test') + parser = Cmd2ArgParser(prog='test') parser.add_argument('invalid_tuple', nargs=(2, 1)) assert 'Invalid nargs range. The first value must be less than the second' in str(excinfo.value) def test_acarg_narg_tuple_negative(): with pytest.raises(ValueError) as excinfo: - parser = ACArgumentParser(prog='test') + parser = Cmd2ArgParser(prog='test') parser.add_argument('invalid_tuple', nargs=(-1, 1)) assert 'Negative numbers are invalid for nargs range' in str(excinfo.value) def test_acarg_narg_tuple_zero_base(): - parser = ACArgumentParser(prog='test') + parser = Cmd2ArgParser(prog='test') parser.add_argument('tuple', nargs=(0, 3)) def test_acarg_narg_tuple_zero_to_one(): - parser = ACArgumentParser(prog='test') + parser = Cmd2ArgParser(prog='test') parser.add_argument('tuple', nargs=(0, 1)) def test_is_potential_flag(): - parser = ACArgumentParser() + parser = Cmd2ArgParser() # Not valid flags assert not is_potential_flag('', parser) -- cgit v1.2.1 From 627d4bda1ac790e34a7b87358defe4b436737292 Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Fri, 5 Jul 2019 15:12:56 -0400 Subject: Fixed unit tests --- tests/test_autocompletion.py | 101 +------------------------------------------ 1 file changed, 1 insertion(+), 100 deletions(-) (limited to 'tests') diff --git a/tests/test_autocompletion.py b/tests/test_autocompletion.py index 4e1ceff0..22047657 100644 --- a/tests/test_autocompletion.py +++ b/tests/test_autocompletion.py @@ -32,22 +32,6 @@ optional arguments: single value - maximum duration [a, b] - duration range''' -MEDIA_MOVIES_ADD_HELP = '''Usage: media movies add -d DIRECTOR{1..2} - [-h] - title {G, PG, PG-13, R, NC-17} ... - -positional arguments: - title Movie Title - {G, PG, PG-13, R, NC-17} - Movie Rating - actor Actors - -required arguments: - -d, --director DIRECTOR{1..2} - Director - -optional arguments: - -h, --help show this help message and exit''' def test_help_required_group(cmd2_app): out1, err1 = run_cmd(cmd2_app, 'suggest -h') @@ -60,15 +44,6 @@ def test_help_required_group(cmd2_app): assert out1 == normalize(SUGGEST_HELP) -def test_help_required_group_long(cmd2_app): - out1, err1 = run_cmd(cmd2_app, 'media movies add -h') - out2, err2 = run_cmd(cmd2_app, 'help media movies add') - - assert out1 == out2 - assert out1[0].startswith('Usage: media movies add') - assert out1 == normalize(MEDIA_MOVIES_ADD_HELP) - - def test_autocomp_flags(cmd2_app): text = '-' line = 'suggest {}'.format(text) @@ -79,6 +54,7 @@ def test_autocomp_flags(cmd2_app): assert first_match is not None and \ cmd2_app.completion_matches == ['--duration', '--help', '--type', '-d', '-h', '-t'] + def test_autcomp_hint(cmd2_app, capsys): text = '' line = 'suggest -d {}'.format(text) @@ -152,59 +128,6 @@ def test_autcomp_narg_beyond_max(cmd2_app): assert 'Error: unrecognized arguments: 5' in err[1] -def test_autocomp_subcmd_nested(cmd2_app): - text = '' - line = 'media movies {}'.format(text) - endidx = len(line) - begidx = endidx - len(text) - - first_match = complete_tester(text, line, begidx, endidx, cmd2_app) - assert first_match is not None and \ - cmd2_app.completion_matches == ['add', 'delete', 'list', 'load'] - - -def test_autocomp_subcmd_flag_choices_append(cmd2_app): - text = '' - line = 'media movies list -r {}'.format(text) - endidx = len(line) - begidx = endidx - len(text) - - first_match = complete_tester(text, line, begidx, endidx, cmd2_app) - assert first_match is not None and \ - cmd2_app.completion_matches == ['G', 'NC-17', 'PG', 'PG-13', 'R'] - -def test_autocomp_subcmd_flag_choices_append_exclude(cmd2_app): - text = '' - line = 'media movies list -r PG PG-13 {}'.format(text) - endidx = len(line) - begidx = endidx - len(text) - - first_match = complete_tester(text, line, begidx, endidx, cmd2_app) - assert first_match is not None and \ - cmd2_app.completion_matches == ['G', 'NC-17', 'R'] - - -def test_autocomp_subcmd_flag_comp_func(cmd2_app): - text = 'A' - line = 'media movies list -a "{}'.format(text) - endidx = len(line) - begidx = endidx - len(text) - - first_match = complete_tester(text, line, begidx, endidx, cmd2_app) - assert first_match is not None and \ - cmd2_app.completion_matches == ['Adam Driver', 'Alec Guinness', 'Andy Serkis', 'Anthony Daniels'] - - -def test_autocomp_subcmd_flag_comp_list(cmd2_app): - text = 'G' - line = 'media movies list -d {}'.format(text) - endidx = len(line) - begidx = endidx - len(text) - - first_match = complete_tester(text, line, begidx, endidx, cmd2_app) - assert first_match is not None and first_match == '"Gareth Edwards' - - def test_autocomp_subcmd_flag_comp_func_attr(cmd2_app): text = 'A' line = 'video movies list -a "{}'.format(text) @@ -247,28 +170,6 @@ def test_autocomp_pos_after_flag(cmd2_app): cmd2_app.completion_matches == ['John Boyega" '] -def test_autocomp_custom_func_list_arg(cmd2_app): - text = 'SW_' - line = 'library show add {}'.format(text) - endidx = len(line) - begidx = endidx - len(text) - - first_match = complete_tester(text, line, begidx, endidx, cmd2_app) - assert first_match is not None and \ - cmd2_app.completion_matches == ['SW_CW', 'SW_REB', 'SW_TCW'] - - -def test_autocomp_custom_func_list_and_dict_arg(cmd2_app): - text = '' - line = 'library show add SW_REB {}'.format(text) - endidx = len(line) - begidx = endidx - len(text) - - first_match = complete_tester(text, line, begidx, endidx, cmd2_app) - assert first_match is not None and \ - cmd2_app.completion_matches == ['S01E02', 'S01E03', 'S02E01', 'S02E03'] - - def test_autocomp_custom_func_dict_arg(cmd2_app): text = '/home/user/' line = 'video movies load {}'.format(text) -- cgit v1.2.1 From 1d560965bf5e03d82c4c353899ee9c7a6bf70a14 Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Fri, 5 Jul 2019 16:50:30 -0400 Subject: Simplifying unit tests for AutoCompleter --- tests/test_autocompletion.py | 427 +++++++++++++++++++++++-------------------- 1 file changed, 228 insertions(+), 199 deletions(-) (limited to 'tests') diff --git a/tests/test_autocompletion.py b/tests/test_autocompletion.py index 22047657..30cb5dad 100644 --- a/tests/test_autocompletion.py +++ b/tests/test_autocompletion.py @@ -3,244 +3,273 @@ """ Unit/functional testing for argparse completer in cmd2 """ +import argparse +from typing import List + import pytest +import cmd2 +from cmd2 import with_argparser +from cmd2.argparse_custom import Cmd2ArgParser from cmd2.utils import StdSim -from .conftest import run_cmd, normalize, complete_tester - -from examples.tab_autocompletion import TabCompleteExample - -@pytest.fixture -def cmd2_app(): - app = TabCompleteExample() - app.stdout = StdSim(app.stdout) - return app +from .conftest import run_cmd, complete_tester +# Lists used in our tests +static_choices_list = ['static', 'choices'] +choices_from_function = ['choices', 'function'] +choices_from_method = ['choices', 'method'] -SUGGEST_HELP = '''Usage: suggest -t {movie, show} [-h] [-d DURATION{1..2}] -Suggest command demonstrates argparse customizations. -See hybrid_suggest and orig_suggest to compare the help output. +def choices_function() -> List[str]: + """Function that provides choices""" + return choices_from_function -required arguments: - -t, --type {movie, show} -optional arguments: - -h, --help show this help message and exit - -d, --duration DURATION{1..2} - Duration constraint in minutes. - single value - maximum duration - [a, b] - duration range''' +class AutoCompleteTester(cmd2.Cmd): + """Cmd2 app that exercises AutoCompleter class""" + def __init__(self): + super().__init__() + def choices_method(self) -> List[str]: + """Method that provides choices""" + return choices_from_method -def test_help_required_group(cmd2_app): - out1, err1 = run_cmd(cmd2_app, 'suggest -h') - out2, err2 = run_cmd(cmd2_app, 'help suggest') + # Basic command with no subcommands that exercises tab completing choices from various sources + basic_parser = Cmd2ArgParser() + basic_parser.add_argument("-n", "--no_choices", help="a flag with no choices") + basic_parser.add_argument("-l", "--choices_list", help="a flag populated with a choices list", + choices=static_choices_list) + basic_parser.add_argument("-f", "--choices_function", help="a flag populated with a choices function", + choices_function=choices_function) + basic_parser.add_argument("-m", "--choices_method", help="a flag populated with a choices method", + choices_method=choices_method) - assert out1 == out2 - assert out1[0].startswith('Usage: suggest') - assert out1[1] == '' - assert out1[2].startswith('Suggest command demonstrates argparse customizations.') - assert out1 == normalize(SUGGEST_HELP) - - -def test_autocomp_flags(cmd2_app): - text = '-' - line = 'suggest {}'.format(text) - endidx = len(line) - begidx = endidx - len(text) - - first_match = complete_tester(text, line, begidx, endidx, cmd2_app) - assert first_match is not None and \ - cmd2_app.completion_matches == ['--duration', '--help', '--type', '-d', '-h', '-t'] - - -def test_autcomp_hint(cmd2_app, capsys): - text = '' - line = 'suggest -d {}'.format(text) - endidx = len(line) - begidx = endidx - len(text) - - first_match = complete_tester(text, line, begidx, endidx, cmd2_app) - out, err = capsys.readouterr() + basic_parser.add_argument("no_choice_pos", help="a positional with no choices") + basic_parser.add_argument("choices_list_pos", help="a positional populated with a choices list", + choices=static_choices_list) + basic_parser.add_argument("choices_function_pos", help="a positional populated with a choices function", + choices_function=choices_function) + basic_parser.add_argument("choices_method_pos", help="a positional populated with a choices method", + choices_method=choices_method) - assert out == ''' -Hint: - -d, --duration DURATION Duration constraint in minutes. - single value - maximum duration - [a, b] - duration range + @with_argparser(basic_parser) + def do_basic(self, args: argparse.Namespace) -> None: + pass -''' -def test_autcomp_flag_comp(cmd2_app, capsys): - text = '--d' - line = 'suggest {}'.format(text) - endidx = len(line) - begidx = endidx - len(text) +@pytest.fixture +def ac_app(): + app = AutoCompleteTester() + app.stdout = StdSim(app.stdout) + return app - first_match = complete_tester(text, line, begidx, endidx, cmd2_app) - out, err = capsys.readouterr() - assert first_match is not None and \ - cmd2_app.completion_matches == ['--duration '] +def test_help_basic(ac_app): + out1, err1 = run_cmd(ac_app, 'basic -h') + out2, err2 = run_cmd(ac_app, 'help basic') + assert out1 == out2 -def test_autocomp_flags_choices(cmd2_app): - text = '' - line = 'suggest -t {}'.format(text) +def test_autocomp_flags(ac_app): + text = '-' + line = 'basic {}'.format(text) endidx = len(line) begidx = endidx - len(text) - first_match = complete_tester(text, line, begidx, endidx, cmd2_app) + first_match = complete_tester(text, line, begidx, endidx, ac_app) assert first_match is not None and \ - cmd2_app.completion_matches == ['movie', 'show'] + ac_app.completion_matches == ['--choices_function', '--choices_list', '--choices_method', '--help', + '--no_choices', '-f', '-h', '-l', '-m', '-n'] -def test_autcomp_hint_in_narg_range(cmd2_app, capsys): +def test_autcomp_hint(ac_app, capsys): text = '' - line = 'suggest -d 2 {}'.format(text) + line = 'basic -n {}'.format(text) endidx = len(line) begidx = endidx - len(text) - first_match = complete_tester(text, line, begidx, endidx, cmd2_app) + first_match = complete_tester(text, line, begidx, endidx, ac_app) out, err = capsys.readouterr() - assert out == ''' -Hint: - -d, --duration DURATION Duration constraint in minutes. - single value - maximum duration - [a, b] - duration range - -''' - -def test_autocomp_flags_narg_max(cmd2_app): - text = '' - line = 'suggest d 2 3 {}'.format(text) - endidx = len(line) - begidx = endidx - len(text) - - first_match = complete_tester(text, line, begidx, endidx, cmd2_app) assert first_match is None + assert 'a flag with no choices' in out -def test_autcomp_narg_beyond_max(cmd2_app): - out, err = run_cmd(cmd2_app, 'suggest -t movie -d 3 4 5') - assert 'Error: unrecognized arguments: 5' in err[1] - - -def test_autocomp_subcmd_flag_comp_func_attr(cmd2_app): - text = 'A' - line = 'video movies list -a "{}'.format(text) +def test_autcomp_flag_comp(ac_app): + text = '--ch' + line = 'basic {}'.format(text) endidx = len(line) begidx = endidx - len(text) - first_match = complete_tester(text, line, begidx, endidx, cmd2_app) + first_match = complete_tester(text, line, begidx, endidx, ac_app) assert first_match is not None and \ - cmd2_app.completion_matches == ['Adam Driver', 'Alec Guinness', 'Andy Serkis', 'Anthony Daniels'] - - -def test_autocomp_subcmd_flag_comp_list_attr(cmd2_app): - text = 'G' - line = 'video movies list -d {}'.format(text) - endidx = len(line) - begidx = endidx - len(text) - - first_match = complete_tester(text, line, begidx, endidx, cmd2_app) - assert first_match is not None and first_match == '"Gareth Edwards' - - -def test_autocomp_pos_consumed(cmd2_app): + ac_app.completion_matches == ['--choices_function', '--choices_list', '--choices_method'] + + +@pytest.mark.parametrize('flag, completions', [ + ('-l', static_choices_list), + ('--choices_list', static_choices_list), + ('-f', choices_from_function), + ('--choices_function', choices_from_function), + ('-m', choices_from_method), + ('--choices_method', choices_from_method), +]) +def test_autocomp_flags_choices(ac_app, flag, completions): text = '' - line = 'library movie add SW_EP01 {}'.format(text) + line = 'basic {} {}'.format(flag, text) endidx = len(line) begidx = endidx - len(text) - first_match = complete_tester(text, line, begidx, endidx, cmd2_app) - assert first_match is None - - -def test_autocomp_pos_after_flag(cmd2_app): - text = 'Joh' - line = 'video movies add -d "George Lucas" -- "Han Solo" PG "Emilia Clarke" "{}'.format(text) - endidx = len(line) - begidx = endidx - len(text) - - first_match = complete_tester(text, line, begidx, endidx, cmd2_app) + first_match = complete_tester(text, line, begidx, endidx, ac_app) assert first_match is not None and \ - cmd2_app.completion_matches == ['John Boyega" '] - - -def test_autocomp_custom_func_dict_arg(cmd2_app): - text = '/home/user/' - line = 'video movies load {}'.format(text) - endidx = len(line) - begidx = endidx - len(text) - - first_match = complete_tester(text, line, begidx, endidx, cmd2_app) - assert first_match is not None and \ - cmd2_app.completion_matches == ['/home/user/another.db', '/home/user/file space.db', '/home/user/file.db'] - - -def test_argparse_remainder_flag_completion(cmd2_app): - import cmd2 - import argparse - - # Test flag completion as first arg of positional with nargs=argparse.REMAINDER - text = '--h' - line = 'help command {}'.format(text) - endidx = len(line) - begidx = endidx - len(text) - - # --h should not complete into --help because we are in the argparse.REMAINDER section - assert complete_tester(text, line, begidx, endidx, cmd2_app) is None - - # Test flag completion within an already started positional with nargs=argparse.REMAINDER - text = '--h' - line = 'help command subcommand {}'.format(text) - endidx = len(line) - begidx = endidx - len(text) - - # --h should not complete into --help because we are in the argparse.REMAINDER section - assert complete_tester(text, line, begidx, endidx, cmd2_app) is None - - # Test a flag with nargs=argparse.REMAINDER - parser = argparse.ArgumentParser() - parser.add_argument('-f', nargs=argparse.REMAINDER) - - # Overwrite eof's parser for this test - cmd2.Cmd.do_eof.argparser = parser - - text = '--h' - line = 'eof -f {}'.format(text) - endidx = len(line) - begidx = endidx - len(text) - - # --h should not complete into --help because we are in the argparse.REMAINDER section - assert complete_tester(text, line, begidx, endidx, cmd2_app) is None - - -def test_completion_after_double_dash(cmd2_app): - """ - Test completion after --, which argparse says (all args after -- are non-options) - All of these tests occur outside of an argparse.REMAINDER section since those tests - are handled in test_argparse_remainder_flag_completion - """ - - # Test -- as the last token - text = '--' - line = 'help {}'.format(text) - endidx = len(line) - begidx = endidx - len(text) - - # Since -- is the last token, then it should show flag choices - first_match = complete_tester(text, line, begidx, endidx, cmd2_app) - assert first_match is not None and '--help' in cmd2_app.completion_matches - - # Test -- to end all flag completion - text = '--' - line = 'help -- {}'.format(text) - endidx = len(line) - begidx = endidx - len(text) - - # Since -- appeared before the -- being completed, nothing should be completed - assert complete_tester(text, line, begidx, endidx, cmd2_app) is None + ac_app.completion_matches == sorted(completions, key=ac_app.matches_sort_key) + + +# def test_autcomp_hint_in_narg_range(cmd2_app, capsys): +# text = '' +# line = 'suggest -d 2 {}'.format(text) +# endidx = len(line) +# begidx = endidx - len(text) +# +# first_match = complete_tester(text, line, begidx, endidx, cmd2_app) +# out, err = capsys.readouterr() +# +# assert out == ''' +# Hint: +# -d, --duration DURATION Duration constraint in minutes. +# single value - maximum duration +# [a, b] - duration range +# +# ''' +# +# def test_autocomp_flags_narg_max(cmd2_app): +# text = '' +# line = 'suggest d 2 3 {}'.format(text) +# endidx = len(line) +# begidx = endidx - len(text) +# +# first_match = complete_tester(text, line, begidx, endidx, cmd2_app) +# assert first_match is None +# +# +# def test_autcomp_narg_beyond_max(cmd2_app): +# out, err = run_cmd(cmd2_app, 'suggest -t movie -d 3 4 5') +# assert 'Error: unrecognized arguments: 5' in err[1] +# +# +# def test_autocomp_subcmd_flag_comp_func_attr(cmd2_app): +# text = 'A' +# line = 'video movies list -a "{}'.format(text) +# endidx = len(line) +# begidx = endidx - len(text) +# +# first_match = complete_tester(text, line, begidx, endidx, cmd2_app) +# assert first_match is not None and \ +# cmd2_app.completion_matches == ['Adam Driver', 'Alec Guinness', 'Andy Serkis', 'Anthony Daniels'] +# +# +# def test_autocomp_subcmd_flag_comp_list_attr(cmd2_app): +# text = 'G' +# line = 'video movies list -d {}'.format(text) +# endidx = len(line) +# begidx = endidx - len(text) +# +# first_match = complete_tester(text, line, begidx, endidx, cmd2_app) +# assert first_match is not None and first_match == '"Gareth Edwards' +# +# +# def test_autocomp_pos_consumed(cmd2_app): +# text = '' +# line = 'library movie add SW_EP01 {}'.format(text) +# endidx = len(line) +# begidx = endidx - len(text) +# +# first_match = complete_tester(text, line, begidx, endidx, cmd2_app) +# assert first_match is None +# +# +# def test_autocomp_pos_after_flag(cmd2_app): +# text = 'Joh' +# line = 'video movies add -d "George Lucas" -- "Han Solo" PG "Emilia Clarke" "{}'.format(text) +# endidx = len(line) +# begidx = endidx - len(text) +# +# first_match = complete_tester(text, line, begidx, endidx, cmd2_app) +# assert first_match is not None and \ +# cmd2_app.completion_matches == ['John Boyega" '] +# +# +# def test_autocomp_custom_func_dict_arg(cmd2_app): +# text = '/home/user/' +# line = 'video movies load {}'.format(text) +# endidx = len(line) +# begidx = endidx - len(text) +# +# first_match = complete_tester(text, line, begidx, endidx, cmd2_app) +# assert first_match is not None and \ +# cmd2_app.completion_matches == ['/home/user/another.db', '/home/user/file space.db', '/home/user/file.db'] +# +# +# def test_argparse_remainder_flag_completion(cmd2_app): +# import cmd2 +# import argparse +# +# # Test flag completion as first arg of positional with nargs=argparse.REMAINDER +# text = '--h' +# line = 'help command {}'.format(text) +# endidx = len(line) +# begidx = endidx - len(text) +# +# # --h should not complete into --help because we are in the argparse.REMAINDER section +# assert complete_tester(text, line, begidx, endidx, cmd2_app) is None +# +# # Test flag completion within an already started positional with nargs=argparse.REMAINDER +# text = '--h' +# line = 'help command subcommand {}'.format(text) +# endidx = len(line) +# begidx = endidx - len(text) +# +# # --h should not complete into --help because we are in the argparse.REMAINDER section +# assert complete_tester(text, line, begidx, endidx, cmd2_app) is None +# +# # Test a flag with nargs=argparse.REMAINDER +# parser = argparse.ArgumentParser() +# parser.add_argument('-f', nargs=argparse.REMAINDER) +# +# # Overwrite eof's parser for this test +# cmd2.Cmd.do_eof.argparser = parser +# +# text = '--h' +# line = 'eof -f {}'.format(text) +# endidx = len(line) +# begidx = endidx - len(text) +# +# # --h should not complete into --help because we are in the argparse.REMAINDER section +# assert complete_tester(text, line, begidx, endidx, cmd2_app) is None +# +# +# def test_completion_after_double_dash(cmd2_app): +# """ +# Test completion after --, which argparse says (all args after -- are non-options) +# All of these tests occur outside of an argparse.REMAINDER section since those tests +# are handled in test_argparse_remainder_flag_completion +# """ +# +# # Test -- as the last token +# text = '--' +# line = 'help {}'.format(text) +# endidx = len(line) +# begidx = endidx - len(text) +# +# # Since -- is the last token, then it should show flag choices +# first_match = complete_tester(text, line, begidx, endidx, cmd2_app) +# assert first_match is not None and '--help' in cmd2_app.completion_matches +# +# # Test -- to end all flag completion +# text = '--' +# line = 'help -- {}'.format(text) +# endidx = len(line) +# begidx = endidx - len(text) +# +# # Since -- appeared before the -- being completed, nothing should be completed +# assert complete_tester(text, line, begidx, endidx, cmd2_app) is None -- cgit v1.2.1 From 655243cb6f586e33c68928f838fcd7d921da1101 Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Fri, 5 Jul 2019 16:58:35 -0400 Subject: Reorganized argparse completion and custom unit tests --- tests/test_acargparse.py | 67 --------- tests/test_argparse_completer.py | 290 +++++++++++++++++++++++++++++++++++++++ tests/test_argparse_custom.py | 51 +++++++ tests/test_autocompletion.py | 275 ------------------------------------- 4 files changed, 341 insertions(+), 342 deletions(-) delete mode 100644 tests/test_acargparse.py create mode 100644 tests/test_argparse_completer.py create mode 100644 tests/test_argparse_custom.py delete mode 100644 tests/test_autocompletion.py (limited to 'tests') diff --git a/tests/test_acargparse.py b/tests/test_acargparse.py deleted file mode 100644 index c8f09f76..00000000 --- a/tests/test_acargparse.py +++ /dev/null @@ -1,67 +0,0 @@ -# flake8: noqa E302 -""" -Unit/functional testing for argparse customizations in cmd2 -""" -import pytest -from cmd2.argparse_custom import Cmd2ArgParser -from cmd2.argparse_completer import is_potential_flag - - -def test_acarg_narg_empty_tuple(): - with pytest.raises(ValueError) as excinfo: - parser = Cmd2ArgParser(prog='test') - parser.add_argument('invalid_tuple', nargs=()) - assert 'Ranged values for nargs must be a tuple of 2 integers' in str(excinfo.value) - - -def test_acarg_narg_single_tuple(): - with pytest.raises(ValueError) as excinfo: - parser = Cmd2ArgParser(prog='test') - parser.add_argument('invalid_tuple', nargs=(1,)) - assert 'Ranged values for nargs must be a tuple of 2 integers' in str(excinfo.value) - - -def test_acarg_narg_tuple_triple(): - with pytest.raises(ValueError) as excinfo: - parser = Cmd2ArgParser(prog='test') - parser.add_argument('invalid_tuple', nargs=(1, 2, 3)) - assert 'Ranged values for nargs must be a tuple of 2 integers' in str(excinfo.value) - - -def test_acarg_narg_tuple_order(): - with pytest.raises(ValueError) as excinfo: - parser = Cmd2ArgParser(prog='test') - parser.add_argument('invalid_tuple', nargs=(2, 1)) - assert 'Invalid nargs range. The first value must be less than the second' in str(excinfo.value) - - -def test_acarg_narg_tuple_negative(): - with pytest.raises(ValueError) as excinfo: - parser = Cmd2ArgParser(prog='test') - parser.add_argument('invalid_tuple', nargs=(-1, 1)) - assert 'Negative numbers are invalid for nargs range' in str(excinfo.value) - - -def test_acarg_narg_tuple_zero_base(): - parser = Cmd2ArgParser(prog='test') - parser.add_argument('tuple', nargs=(0, 3)) - - -def test_acarg_narg_tuple_zero_to_one(): - parser = Cmd2ArgParser(prog='test') - parser.add_argument('tuple', nargs=(0, 1)) - - -def test_is_potential_flag(): - parser = Cmd2ArgParser() - - # Not valid flags - assert not is_potential_flag('', parser) - assert not is_potential_flag('non-flag', parser) - assert not is_potential_flag('-', parser) - assert not is_potential_flag('--has space', parser) - assert not is_potential_flag('-2', parser) - - # Valid flags - assert is_potential_flag('-flag', parser) - assert is_potential_flag('--flag', parser) diff --git a/tests/test_argparse_completer.py b/tests/test_argparse_completer.py new file mode 100644 index 00000000..4f1ed44a --- /dev/null +++ b/tests/test_argparse_completer.py @@ -0,0 +1,290 @@ +# coding=utf-8 +# flake8: noqa E302 +""" +Unit/functional testing for argparse completer in cmd2 +""" +import argparse +from typing import List + +import pytest + +import cmd2 +from cmd2 import with_argparser +from cmd2.argparse_completer import is_potential_flag +from cmd2.argparse_custom import Cmd2ArgParser +from cmd2.utils import StdSim +from .conftest import run_cmd, complete_tester + +# Lists used in our tests +static_choices_list = ['static', 'choices'] +choices_from_function = ['choices', 'function'] +choices_from_method = ['choices', 'method'] + + +def choices_function() -> List[str]: + """Function that provides choices""" + return choices_from_function + + +class AutoCompleteTester(cmd2.Cmd): + """Cmd2 app that exercises AutoCompleter class""" + def __init__(self): + super().__init__() + + def choices_method(self) -> List[str]: + """Method that provides choices""" + return choices_from_method + + # Basic command with no subcommands that exercises tab completing choices from various sources + basic_parser = Cmd2ArgParser() + basic_parser.add_argument("-n", "--no_choices", help="a flag with no choices") + basic_parser.add_argument("-l", "--choices_list", help="a flag populated with a choices list", + choices=static_choices_list) + basic_parser.add_argument("-f", "--choices_function", help="a flag populated with a choices function", + choices_function=choices_function) + basic_parser.add_argument("-m", "--choices_method", help="a flag populated with a choices method", + choices_method=choices_method) + + basic_parser.add_argument("no_choice_pos", help="a positional with no choices") + basic_parser.add_argument("choices_list_pos", help="a positional populated with a choices list", + choices=static_choices_list) + basic_parser.add_argument("choices_function_pos", help="a positional populated with a choices function", + choices_function=choices_function) + basic_parser.add_argument("choices_method_pos", help="a positional populated with a choices method", + choices_method=choices_method) + + @with_argparser(basic_parser) + def do_basic(self, args: argparse.Namespace) -> None: + pass + + +@pytest.fixture +def ac_app(): + app = AutoCompleteTester() + app.stdout = StdSim(app.stdout) + return app + + +def test_help_basic(ac_app): + out1, err1 = run_cmd(ac_app, 'basic -h') + out2, err2 = run_cmd(ac_app, 'help basic') + assert out1 == out2 + + +def test_autocomp_flags(ac_app): + text = '-' + line = 'basic {}'.format(text) + endidx = len(line) + begidx = endidx - len(text) + + first_match = complete_tester(text, line, begidx, endidx, ac_app) + assert first_match is not None and \ + ac_app.completion_matches == ['--choices_function', '--choices_list', '--choices_method', '--help', + '--no_choices', '-f', '-h', '-l', '-m', '-n'] + + +def test_autcomp_hint(ac_app, capsys): + text = '' + line = 'basic -n {}'.format(text) + endidx = len(line) + begidx = endidx - len(text) + + first_match = complete_tester(text, line, begidx, endidx, ac_app) + out, err = capsys.readouterr() + + assert first_match is None + assert 'a flag with no choices' in out + + +def test_autcomp_flag_comp(ac_app): + text = '--ch' + line = 'basic {}'.format(text) + endidx = len(line) + begidx = endidx - len(text) + + first_match = complete_tester(text, line, begidx, endidx, ac_app) + assert first_match is not None and \ + ac_app.completion_matches == ['--choices_function', '--choices_list', '--choices_method'] + + +@pytest.mark.parametrize('flag, completions', [ + ('-l', static_choices_list), + ('--choices_list', static_choices_list), + ('-f', choices_from_function), + ('--choices_function', choices_from_function), + ('-m', choices_from_method), + ('--choices_method', choices_from_method), +]) +def test_autocomp_flags_choices(ac_app, flag, completions): + text = '' + line = 'basic {} {}'.format(flag, text) + endidx = len(line) + begidx = endidx - len(text) + + first_match = complete_tester(text, line, begidx, endidx, ac_app) + assert first_match is not None and \ + ac_app.completion_matches == sorted(completions, key=ac_app.matches_sort_key) + + +# def test_autcomp_hint_in_narg_range(cmd2_app, capsys): +# text = '' +# line = 'suggest -d 2 {}'.format(text) +# endidx = len(line) +# begidx = endidx - len(text) +# +# first_match = complete_tester(text, line, begidx, endidx, cmd2_app) +# out, err = capsys.readouterr() +# +# assert out == ''' +# Hint: +# -d, --duration DURATION Duration constraint in minutes. +# single value - maximum duration +# [a, b] - duration range +# +# ''' +# +# def test_autocomp_flags_narg_max(cmd2_app): +# text = '' +# line = 'suggest d 2 3 {}'.format(text) +# endidx = len(line) +# begidx = endidx - len(text) +# +# first_match = complete_tester(text, line, begidx, endidx, cmd2_app) +# assert first_match is None +# +# +# def test_autcomp_narg_beyond_max(cmd2_app): +# out, err = run_cmd(cmd2_app, 'suggest -t movie -d 3 4 5') +# assert 'Error: unrecognized arguments: 5' in err[1] +# +# +# def test_autocomp_subcmd_flag_comp_func_attr(cmd2_app): +# text = 'A' +# line = 'video movies list -a "{}'.format(text) +# endidx = len(line) +# begidx = endidx - len(text) +# +# first_match = complete_tester(text, line, begidx, endidx, cmd2_app) +# assert first_match is not None and \ +# cmd2_app.completion_matches == ['Adam Driver', 'Alec Guinness', 'Andy Serkis', 'Anthony Daniels'] +# +# +# def test_autocomp_subcmd_flag_comp_list_attr(cmd2_app): +# text = 'G' +# line = 'video movies list -d {}'.format(text) +# endidx = len(line) +# begidx = endidx - len(text) +# +# first_match = complete_tester(text, line, begidx, endidx, cmd2_app) +# assert first_match is not None and first_match == '"Gareth Edwards' +# +# +# def test_autocomp_pos_consumed(cmd2_app): +# text = '' +# line = 'library movie add SW_EP01 {}'.format(text) +# endidx = len(line) +# begidx = endidx - len(text) +# +# first_match = complete_tester(text, line, begidx, endidx, cmd2_app) +# assert first_match is None +# +# +# def test_autocomp_pos_after_flag(cmd2_app): +# text = 'Joh' +# line = 'video movies add -d "George Lucas" -- "Han Solo" PG "Emilia Clarke" "{}'.format(text) +# endidx = len(line) +# begidx = endidx - len(text) +# +# first_match = complete_tester(text, line, begidx, endidx, cmd2_app) +# assert first_match is not None and \ +# cmd2_app.completion_matches == ['John Boyega" '] +# +# +# def test_autocomp_custom_func_dict_arg(cmd2_app): +# text = '/home/user/' +# line = 'video movies load {}'.format(text) +# endidx = len(line) +# begidx = endidx - len(text) +# +# first_match = complete_tester(text, line, begidx, endidx, cmd2_app) +# assert first_match is not None and \ +# cmd2_app.completion_matches == ['/home/user/another.db', '/home/user/file space.db', '/home/user/file.db'] +# +# +# def test_argparse_remainder_flag_completion(cmd2_app): +# import cmd2 +# import argparse +# +# # Test flag completion as first arg of positional with nargs=argparse.REMAINDER +# text = '--h' +# line = 'help command {}'.format(text) +# endidx = len(line) +# begidx = endidx - len(text) +# +# # --h should not complete into --help because we are in the argparse.REMAINDER section +# assert complete_tester(text, line, begidx, endidx, cmd2_app) is None +# +# # Test flag completion within an already started positional with nargs=argparse.REMAINDER +# text = '--h' +# line = 'help command subcommand {}'.format(text) +# endidx = len(line) +# begidx = endidx - len(text) +# +# # --h should not complete into --help because we are in the argparse.REMAINDER section +# assert complete_tester(text, line, begidx, endidx, cmd2_app) is None +# +# # Test a flag with nargs=argparse.REMAINDER +# parser = argparse.ArgumentParser() +# parser.add_argument('-f', nargs=argparse.REMAINDER) +# +# # Overwrite eof's parser for this test +# cmd2.Cmd.do_eof.argparser = parser +# +# text = '--h' +# line = 'eof -f {}'.format(text) +# endidx = len(line) +# begidx = endidx - len(text) +# +# # --h should not complete into --help because we are in the argparse.REMAINDER section +# assert complete_tester(text, line, begidx, endidx, cmd2_app) is None +# +# +# def test_completion_after_double_dash(cmd2_app): +# """ +# Test completion after --, which argparse says (all args after -- are non-options) +# All of these tests occur outside of an argparse.REMAINDER section since those tests +# are handled in test_argparse_remainder_flag_completion +# """ +# +# # Test -- as the last token +# text = '--' +# line = 'help {}'.format(text) +# endidx = len(line) +# begidx = endidx - len(text) +# +# # Since -- is the last token, then it should show flag choices +# first_match = complete_tester(text, line, begidx, endidx, cmd2_app) +# assert first_match is not None and '--help' in cmd2_app.completion_matches +# +# # Test -- to end all flag completion +# text = '--' +# line = 'help -- {}'.format(text) +# endidx = len(line) +# begidx = endidx - len(text) +# +# # Since -- appeared before the -- being completed, nothing should be completed +# assert complete_tester(text, line, begidx, endidx, cmd2_app) is None + +def test_is_potential_flag(): + parser = Cmd2ArgParser() + + # Not valid flags + assert not is_potential_flag('', parser) + assert not is_potential_flag('non-flag', parser) + assert not is_potential_flag('-', parser) + assert not is_potential_flag('--has space', parser) + assert not is_potential_flag('-2', parser) + + # Valid flags + assert is_potential_flag('-flag', parser) + assert is_potential_flag('--flag', parser) \ No newline at end of file diff --git a/tests/test_argparse_custom.py b/tests/test_argparse_custom.py new file mode 100644 index 00000000..85587d49 --- /dev/null +++ b/tests/test_argparse_custom.py @@ -0,0 +1,51 @@ +# flake8: noqa E302 +""" +Unit/functional testing for argparse customizations in cmd2 +""" +import pytest +from cmd2.argparse_custom import Cmd2ArgParser + + +def test_acarg_narg_empty_tuple(): + with pytest.raises(ValueError) as excinfo: + parser = Cmd2ArgParser(prog='test') + parser.add_argument('invalid_tuple', nargs=()) + assert 'Ranged values for nargs must be a tuple of 2 integers' in str(excinfo.value) + + +def test_acarg_narg_single_tuple(): + with pytest.raises(ValueError) as excinfo: + parser = Cmd2ArgParser(prog='test') + parser.add_argument('invalid_tuple', nargs=(1,)) + assert 'Ranged values for nargs must be a tuple of 2 integers' in str(excinfo.value) + + +def test_acarg_narg_tuple_triple(): + with pytest.raises(ValueError) as excinfo: + parser = Cmd2ArgParser(prog='test') + parser.add_argument('invalid_tuple', nargs=(1, 2, 3)) + assert 'Ranged values for nargs must be a tuple of 2 integers' in str(excinfo.value) + + +def test_acarg_narg_tuple_order(): + with pytest.raises(ValueError) as excinfo: + parser = Cmd2ArgParser(prog='test') + parser.add_argument('invalid_tuple', nargs=(2, 1)) + assert 'Invalid nargs range. The first value must be less than the second' in str(excinfo.value) + + +def test_acarg_narg_tuple_negative(): + with pytest.raises(ValueError) as excinfo: + parser = Cmd2ArgParser(prog='test') + parser.add_argument('invalid_tuple', nargs=(-1, 1)) + assert 'Negative numbers are invalid for nargs range' in str(excinfo.value) + + +def test_acarg_narg_tuple_zero_base(): + parser = Cmd2ArgParser(prog='test') + parser.add_argument('tuple', nargs=(0, 3)) + + +def test_acarg_narg_tuple_zero_to_one(): + parser = Cmd2ArgParser(prog='test') + parser.add_argument('tuple', nargs=(0, 1)) diff --git a/tests/test_autocompletion.py b/tests/test_autocompletion.py deleted file mode 100644 index 30cb5dad..00000000 --- a/tests/test_autocompletion.py +++ /dev/null @@ -1,275 +0,0 @@ -# coding=utf-8 -# flake8: noqa E302 -""" -Unit/functional testing for argparse completer in cmd2 -""" -import argparse -from typing import List - -import pytest - -import cmd2 -from cmd2 import with_argparser -from cmd2.argparse_custom import Cmd2ArgParser -from cmd2.utils import StdSim -from .conftest import run_cmd, complete_tester - -# Lists used in our tests -static_choices_list = ['static', 'choices'] -choices_from_function = ['choices', 'function'] -choices_from_method = ['choices', 'method'] - - -def choices_function() -> List[str]: - """Function that provides choices""" - return choices_from_function - - -class AutoCompleteTester(cmd2.Cmd): - """Cmd2 app that exercises AutoCompleter class""" - def __init__(self): - super().__init__() - - def choices_method(self) -> List[str]: - """Method that provides choices""" - return choices_from_method - - # Basic command with no subcommands that exercises tab completing choices from various sources - basic_parser = Cmd2ArgParser() - basic_parser.add_argument("-n", "--no_choices", help="a flag with no choices") - basic_parser.add_argument("-l", "--choices_list", help="a flag populated with a choices list", - choices=static_choices_list) - basic_parser.add_argument("-f", "--choices_function", help="a flag populated with a choices function", - choices_function=choices_function) - basic_parser.add_argument("-m", "--choices_method", help="a flag populated with a choices method", - choices_method=choices_method) - - basic_parser.add_argument("no_choice_pos", help="a positional with no choices") - basic_parser.add_argument("choices_list_pos", help="a positional populated with a choices list", - choices=static_choices_list) - basic_parser.add_argument("choices_function_pos", help="a positional populated with a choices function", - choices_function=choices_function) - basic_parser.add_argument("choices_method_pos", help="a positional populated with a choices method", - choices_method=choices_method) - - @with_argparser(basic_parser) - def do_basic(self, args: argparse.Namespace) -> None: - pass - - -@pytest.fixture -def ac_app(): - app = AutoCompleteTester() - app.stdout = StdSim(app.stdout) - return app - - -def test_help_basic(ac_app): - out1, err1 = run_cmd(ac_app, 'basic -h') - out2, err2 = run_cmd(ac_app, 'help basic') - assert out1 == out2 - - -def test_autocomp_flags(ac_app): - text = '-' - line = 'basic {}'.format(text) - endidx = len(line) - begidx = endidx - len(text) - - first_match = complete_tester(text, line, begidx, endidx, ac_app) - assert first_match is not None and \ - ac_app.completion_matches == ['--choices_function', '--choices_list', '--choices_method', '--help', - '--no_choices', '-f', '-h', '-l', '-m', '-n'] - - -def test_autcomp_hint(ac_app, capsys): - text = '' - line = 'basic -n {}'.format(text) - endidx = len(line) - begidx = endidx - len(text) - - first_match = complete_tester(text, line, begidx, endidx, ac_app) - out, err = capsys.readouterr() - - assert first_match is None - assert 'a flag with no choices' in out - - -def test_autcomp_flag_comp(ac_app): - text = '--ch' - line = 'basic {}'.format(text) - endidx = len(line) - begidx = endidx - len(text) - - first_match = complete_tester(text, line, begidx, endidx, ac_app) - assert first_match is not None and \ - ac_app.completion_matches == ['--choices_function', '--choices_list', '--choices_method'] - - -@pytest.mark.parametrize('flag, completions', [ - ('-l', static_choices_list), - ('--choices_list', static_choices_list), - ('-f', choices_from_function), - ('--choices_function', choices_from_function), - ('-m', choices_from_method), - ('--choices_method', choices_from_method), -]) -def test_autocomp_flags_choices(ac_app, flag, completions): - text = '' - line = 'basic {} {}'.format(flag, text) - endidx = len(line) - begidx = endidx - len(text) - - first_match = complete_tester(text, line, begidx, endidx, ac_app) - assert first_match is not None and \ - ac_app.completion_matches == sorted(completions, key=ac_app.matches_sort_key) - - -# def test_autcomp_hint_in_narg_range(cmd2_app, capsys): -# text = '' -# line = 'suggest -d 2 {}'.format(text) -# endidx = len(line) -# begidx = endidx - len(text) -# -# first_match = complete_tester(text, line, begidx, endidx, cmd2_app) -# out, err = capsys.readouterr() -# -# assert out == ''' -# Hint: -# -d, --duration DURATION Duration constraint in minutes. -# single value - maximum duration -# [a, b] - duration range -# -# ''' -# -# def test_autocomp_flags_narg_max(cmd2_app): -# text = '' -# line = 'suggest d 2 3 {}'.format(text) -# endidx = len(line) -# begidx = endidx - len(text) -# -# first_match = complete_tester(text, line, begidx, endidx, cmd2_app) -# assert first_match is None -# -# -# def test_autcomp_narg_beyond_max(cmd2_app): -# out, err = run_cmd(cmd2_app, 'suggest -t movie -d 3 4 5') -# assert 'Error: unrecognized arguments: 5' in err[1] -# -# -# def test_autocomp_subcmd_flag_comp_func_attr(cmd2_app): -# text = 'A' -# line = 'video movies list -a "{}'.format(text) -# endidx = len(line) -# begidx = endidx - len(text) -# -# first_match = complete_tester(text, line, begidx, endidx, cmd2_app) -# assert first_match is not None and \ -# cmd2_app.completion_matches == ['Adam Driver', 'Alec Guinness', 'Andy Serkis', 'Anthony Daniels'] -# -# -# def test_autocomp_subcmd_flag_comp_list_attr(cmd2_app): -# text = 'G' -# line = 'video movies list -d {}'.format(text) -# endidx = len(line) -# begidx = endidx - len(text) -# -# first_match = complete_tester(text, line, begidx, endidx, cmd2_app) -# assert first_match is not None and first_match == '"Gareth Edwards' -# -# -# def test_autocomp_pos_consumed(cmd2_app): -# text = '' -# line = 'library movie add SW_EP01 {}'.format(text) -# endidx = len(line) -# begidx = endidx - len(text) -# -# first_match = complete_tester(text, line, begidx, endidx, cmd2_app) -# assert first_match is None -# -# -# def test_autocomp_pos_after_flag(cmd2_app): -# text = 'Joh' -# line = 'video movies add -d "George Lucas" -- "Han Solo" PG "Emilia Clarke" "{}'.format(text) -# endidx = len(line) -# begidx = endidx - len(text) -# -# first_match = complete_tester(text, line, begidx, endidx, cmd2_app) -# assert first_match is not None and \ -# cmd2_app.completion_matches == ['John Boyega" '] -# -# -# def test_autocomp_custom_func_dict_arg(cmd2_app): -# text = '/home/user/' -# line = 'video movies load {}'.format(text) -# endidx = len(line) -# begidx = endidx - len(text) -# -# first_match = complete_tester(text, line, begidx, endidx, cmd2_app) -# assert first_match is not None and \ -# cmd2_app.completion_matches == ['/home/user/another.db', '/home/user/file space.db', '/home/user/file.db'] -# -# -# def test_argparse_remainder_flag_completion(cmd2_app): -# import cmd2 -# import argparse -# -# # Test flag completion as first arg of positional with nargs=argparse.REMAINDER -# text = '--h' -# line = 'help command {}'.format(text) -# endidx = len(line) -# begidx = endidx - len(text) -# -# # --h should not complete into --help because we are in the argparse.REMAINDER section -# assert complete_tester(text, line, begidx, endidx, cmd2_app) is None -# -# # Test flag completion within an already started positional with nargs=argparse.REMAINDER -# text = '--h' -# line = 'help command subcommand {}'.format(text) -# endidx = len(line) -# begidx = endidx - len(text) -# -# # --h should not complete into --help because we are in the argparse.REMAINDER section -# assert complete_tester(text, line, begidx, endidx, cmd2_app) is None -# -# # Test a flag with nargs=argparse.REMAINDER -# parser = argparse.ArgumentParser() -# parser.add_argument('-f', nargs=argparse.REMAINDER) -# -# # Overwrite eof's parser for this test -# cmd2.Cmd.do_eof.argparser = parser -# -# text = '--h' -# line = 'eof -f {}'.format(text) -# endidx = len(line) -# begidx = endidx - len(text) -# -# # --h should not complete into --help because we are in the argparse.REMAINDER section -# assert complete_tester(text, line, begidx, endidx, cmd2_app) is None -# -# -# def test_completion_after_double_dash(cmd2_app): -# """ -# Test completion after --, which argparse says (all args after -- are non-options) -# All of these tests occur outside of an argparse.REMAINDER section since those tests -# are handled in test_argparse_remainder_flag_completion -# """ -# -# # Test -- as the last token -# text = '--' -# line = 'help {}'.format(text) -# endidx = len(line) -# begidx = endidx - len(text) -# -# # Since -- is the last token, then it should show flag choices -# first_match = complete_tester(text, line, begidx, endidx, cmd2_app) -# assert first_match is not None and '--help' in cmd2_app.completion_matches -# -# # Test -- to end all flag completion -# text = '--' -# line = 'help -- {}'.format(text) -# endidx = len(line) -# begidx = endidx - len(text) -# -# # Since -- appeared before the -- being completed, nothing should be completed -# assert complete_tester(text, line, begidx, endidx, cmd2_app) is None -- cgit v1.2.1 From fa564a6f1b41f7a437b4b69b7d3f3bd5c0cfa3d7 Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Fri, 5 Jul 2019 19:18:01 -0400 Subject: Refactoring and more unit tests --- tests/test_argparse_completer.py | 88 ++++++++++++++++++++++++++-------------- 1 file changed, 58 insertions(+), 30 deletions(-) (limited to 'tests') diff --git a/tests/test_argparse_completer.py b/tests/test_argparse_completer.py index 4f1ed44a..f2aa40a3 100644 --- a/tests/test_argparse_completer.py +++ b/tests/test_argparse_completer.py @@ -31,30 +31,42 @@ class AutoCompleteTester(cmd2.Cmd): def __init__(self): super().__init__() + ############################################################################################################ + # Begin code related to testing help and subcommand completion + ############################################################################################################ + basic_parser = Cmd2ArgParser(prog='basic') + basic_subparsers = basic_parser.add_subparsers() + + + ############################################################################################################ + # Begin code related to testing choices, choices_function, and choices_method parameters + ############################################################################################################ def choices_method(self) -> List[str]: """Method that provides choices""" return choices_from_method - # Basic command with no subcommands that exercises tab completing choices from various sources - basic_parser = Cmd2ArgParser() - basic_parser.add_argument("-n", "--no_choices", help="a flag with no choices") - basic_parser.add_argument("-l", "--choices_list", help="a flag populated with a choices list", - choices=static_choices_list) - basic_parser.add_argument("-f", "--choices_function", help="a flag populated with a choices function", - choices_function=choices_function) - basic_parser.add_argument("-m", "--choices_method", help="a flag populated with a choices method", - choices_method=choices_method) - - basic_parser.add_argument("no_choice_pos", help="a positional with no choices") - basic_parser.add_argument("choices_list_pos", help="a positional populated with a choices list", - choices=static_choices_list) - basic_parser.add_argument("choices_function_pos", help="a positional populated with a choices function", - choices_function=choices_function) - basic_parser.add_argument("choices_method_pos", help="a positional populated with a choices method", - choices_method=choices_method) - - @with_argparser(basic_parser) - def do_basic(self, args: argparse.Namespace) -> None: + choices_parser = Cmd2ArgParser() + + # Flags args for choices command + choices_parser.add_argument("-n", "--no_choices", help="a flag with no choices") + choices_parser.add_argument("-l", "--choices_list", help="a flag populated with a choices list", + choices=static_choices_list) + choices_parser.add_argument("-f", "--choices_function", help="a flag populated with a choices function", + choices_function=choices_function) + choices_parser.add_argument("-m", "--choices_method", help="a flag populated with a choices method", + choices_method=choices_method) + + # Positional args for choices command + choices_parser.add_argument("no_choice_pos", help="a positional with no choices") + choices_parser.add_argument("choices_list_pos", help="a positional populated with a choices list", + choices=static_choices_list) + choices_parser.add_argument("choices_function_pos", help="a positional populated with a choices function", + choices_function=choices_function) + choices_parser.add_argument("choices_method_pos", help="a positional populated with a choices method", + choices_method=choices_method) + + @with_argparser(choices_parser) + def do_choices(self, args: argparse.Namespace) -> None: pass @@ -66,14 +78,14 @@ def ac_app(): def test_help_basic(ac_app): - out1, err1 = run_cmd(ac_app, 'basic -h') - out2, err2 = run_cmd(ac_app, 'help basic') + out1, err1 = run_cmd(ac_app, 'choices -h') + out2, err2 = run_cmd(ac_app, 'help choices') assert out1 == out2 def test_autocomp_flags(ac_app): text = '-' - line = 'basic {}'.format(text) + line = 'choices {}'.format(text) endidx = len(line) begidx = endidx - len(text) @@ -83,9 +95,9 @@ def test_autocomp_flags(ac_app): '--no_choices', '-f', '-h', '-l', '-m', '-n'] -def test_autcomp_hint(ac_app, capsys): +def test_autcomp_flag_hint(ac_app, capsys): text = '' - line = 'basic -n {}'.format(text) + line = 'choices -n {}'.format(text) endidx = len(line) begidx = endidx - len(text) @@ -96,9 +108,9 @@ def test_autcomp_hint(ac_app, capsys): assert 'a flag with no choices' in out -def test_autcomp_flag_comp(ac_app): +def test_autcomp_flag_completion(ac_app): text = '--ch' - line = 'basic {}'.format(text) + line = 'choices {}'.format(text) endidx = len(line) begidx = endidx - len(text) @@ -106,7 +118,6 @@ def test_autcomp_flag_comp(ac_app): assert first_match is not None and \ ac_app.completion_matches == ['--choices_function', '--choices_list', '--choices_method'] - @pytest.mark.parametrize('flag, completions', [ ('-l', static_choices_list), ('--choices_list', static_choices_list), @@ -115,9 +126,26 @@ def test_autcomp_flag_comp(ac_app): ('-m', choices_from_method), ('--choices_method', choices_from_method), ]) -def test_autocomp_flags_choices(ac_app, flag, completions): +def test_autocomp_flag_choices_completion(ac_app, flag, completions): + text = '' + line = 'choices {} {}'.format(flag, text) + endidx = len(line) + begidx = endidx - len(text) + + first_match = complete_tester(text, line, begidx, endidx, ac_app) + assert first_match is not None and \ + ac_app.completion_matches == sorted(completions, key=ac_app.matches_sort_key) + + +@pytest.mark.parametrize('pos, completions', [ + (2, static_choices_list), # choices_list_pos + (3, choices_from_function), # choices_function_pos + (4, choices_from_method), # choices_method_pos +]) +def test_autocomp_positional_choices_completion(ac_app, pos, completions): + # Test completions of positional arguments by generating a line were preceding positionals are already filled text = '' - line = 'basic {} {}'.format(flag, text) + line = 'choices {} {}'.format('foo ' * (pos - 1), text) endidx = len(line) begidx = endidx - len(text) -- cgit v1.2.1 From 5ef4267360e87d8c2af13d0b7f6a6cd8d80fd016 Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Sat, 6 Jul 2019 10:39:56 -0400 Subject: Made alias and macro tab completion lookup results use CompletionItems --- tests/test_cmd2.py | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) (limited to 'tests') diff --git a/tests/test_cmd2.py b/tests/test_cmd2.py index c9a41033..6353d884 100644 --- a/tests/test_cmd2.py +++ b/tests/test_cmd2.py @@ -21,6 +21,7 @@ except ImportError: import cmd2 from cmd2 import ansi, clipboard, constants, utils +from cmd2.argparse_completer import CompletionItem from .conftest import run_cmd, normalize, verify_help_text, HELP_HISTORY, SHORTCUTS_TXT, SHOW_TXT, SHOW_LONG def CreateOutsimApp(): @@ -1504,19 +1505,31 @@ invalid_command_name = [ 'noembedded"quotes', ] -def test_get_alias_names(base_app): +def test_get_alias_completion_items(base_app): assert len(base_app.aliases) == 0 run_cmd(base_app, 'alias create fake run_pyscript') run_cmd(base_app, 'alias create ls !ls -hal') assert len(base_app.aliases) == 2 - assert sorted(base_app._get_alias_names()) == ['fake', 'ls'] -def test_get_macro_names(base_app): + expected = sorted([CompletionItem('fake', 'run_pyscript'), CompletionItem('ls', '!ls -hal')]) + results = sorted(base_app._get_alias_completion_items()) + + for index, cur_res in enumerate(results): + assert cur_res == expected[index] + assert cur_res.description == expected[index].description + +def test_get_macro_completion_items(base_app): assert len(base_app.macros) == 0 run_cmd(base_app, 'macro create foo !echo foo') run_cmd(base_app, 'macro create bar !echo bar') assert len(base_app.macros) == 2 - assert sorted(base_app._get_macro_names()) == ['bar', 'foo'] + + expected = sorted([CompletionItem('foo', '!echo foo'), CompletionItem('bar', '!echo bar')]) + results = sorted(base_app._get_macro_completion_items()) + + for index, cur_res in enumerate(results): + assert cur_res == expected[index] + assert cur_res.description == expected[index].description def test_get_settable_names(base_app): assert sorted(base_app._get_settable_names()) == sorted(base_app.settable.keys()) -- cgit v1.2.1 From 67445d49af8db72f9e27a8d47449d0b5ed1e6b9c Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Sat, 6 Jul 2019 11:28:05 -0400 Subject: Display set command tab-completion results as CompletionItems --- tests/test_cmd2.py | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) (limited to 'tests') diff --git a/tests/test_cmd2.py b/tests/test_cmd2.py index 6353d884..4e05283e 100644 --- a/tests/test_cmd2.py +++ b/tests/test_cmd2.py @@ -21,7 +21,6 @@ except ImportError: import cmd2 from cmd2 import ansi, clipboard, constants, utils -from cmd2.argparse_completer import CompletionItem from .conftest import run_cmd, normalize, verify_help_text, HELP_HISTORY, SHORTCUTS_TXT, SHOW_TXT, SHOW_LONG def CreateOutsimApp(): @@ -1506,33 +1505,32 @@ invalid_command_name = [ ] def test_get_alias_completion_items(base_app): - assert len(base_app.aliases) == 0 run_cmd(base_app, 'alias create fake run_pyscript') run_cmd(base_app, 'alias create ls !ls -hal') - assert len(base_app.aliases) == 2 - expected = sorted([CompletionItem('fake', 'run_pyscript'), CompletionItem('ls', '!ls -hal')]) - results = sorted(base_app._get_alias_completion_items()) + results = base_app._get_alias_completion_items() + assert len(results) == len(base_app.aliases) - for index, cur_res in enumerate(results): - assert cur_res == expected[index] - assert cur_res.description == expected[index].description + for cur_res in results: + assert cur_res in base_app.aliases + assert cur_res.description == base_app.aliases[cur_res] def test_get_macro_completion_items(base_app): - assert len(base_app.macros) == 0 run_cmd(base_app, 'macro create foo !echo foo') run_cmd(base_app, 'macro create bar !echo bar') - assert len(base_app.macros) == 2 - expected = sorted([CompletionItem('foo', '!echo foo'), CompletionItem('bar', '!echo bar')]) - results = sorted(base_app._get_macro_completion_items()) + results = base_app._get_macro_completion_items() + assert len(results) == len(base_app.macros) - for index, cur_res in enumerate(results): - assert cur_res == expected[index] - assert cur_res.description == expected[index].description + for cur_res in results: + assert cur_res in base_app.macros + assert cur_res.description == base_app.macros[cur_res].value def test_get_settable_names(base_app): - assert sorted(base_app._get_settable_names()) == sorted(base_app.settable.keys()) + results = base_app._get_settable_completion_items() + for cur_res in results: + assert cur_res in base_app.settable + assert cur_res.description == base_app.settable[cur_res] def test_alias_no_subcommand(base_app): out, err = run_cmd(base_app, 'alias') -- cgit v1.2.1 From 53f6c07559f2c15656423dbbb92471758e8c6d20 Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Sat, 6 Jul 2019 13:22:41 -0400 Subject: Added ability to limit how many CompletionItems display at a time --- tests/test_cmd2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/test_cmd2.py b/tests/test_cmd2.py index 4e05283e..f1d366f7 100644 --- a/tests/test_cmd2.py +++ b/tests/test_cmd2.py @@ -1526,7 +1526,7 @@ 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_names(base_app): +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.settable -- cgit v1.2.1 From 748a3e4e2a99b8067bc2738f85de667870d7f1d1 Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Sat, 6 Jul 2019 16:58:31 -0400 Subject: Updating unit tests --- tests/test_argparse_completer.py | 156 +++++++++++++++++++++++---------------- 1 file changed, 94 insertions(+), 62 deletions(-) (limited to 'tests') diff --git a/tests/test_argparse_completer.py b/tests/test_argparse_completer.py index f2aa40a3..839cdf7a 100644 --- a/tests/test_argparse_completer.py +++ b/tests/test_argparse_completer.py @@ -12,13 +12,16 @@ import cmd2 from cmd2 import with_argparser from cmd2.argparse_completer import is_potential_flag from cmd2.argparse_custom import Cmd2ArgParser -from cmd2.utils import StdSim +from cmd2.utils import StdSim, basic_complete from .conftest import run_cmd, complete_tester # Lists used in our tests -static_choices_list = ['static', 'choices'] -choices_from_function = ['choices', 'function'] -choices_from_method = ['choices', 'method'] +static_choices_list = ['static', 'choices', 'stop', 'here'] +choices_from_function = ['choices', 'function', 'chatty', 'smith'] +choices_from_method = ['choices', 'method', 'most', 'improved'] + +completions_from_function = ['completions', 'function', 'fairly', 'complete'] +completions_from_method = ['completions', 'method', 'missed', 'spot'] def choices_function() -> List[str]: @@ -26,18 +29,17 @@ def choices_function() -> List[str]: return choices_from_function +def completer_function(text: str, line: str, begidx: int, endidx: int) -> List[str]: + """Tab completion function""" + return basic_complete(text, line, begidx, endidx, completions_from_function) + + +# noinspection PyMethodMayBeStatic class AutoCompleteTester(cmd2.Cmd): """Cmd2 app that exercises AutoCompleter class""" def __init__(self): super().__init__() - ############################################################################################################ - # Begin code related to testing help and subcommand completion - ############################################################################################################ - basic_parser = Cmd2ArgParser(prog='basic') - basic_subparsers = basic_parser.add_subparsers() - - ############################################################################################################ # Begin code related to testing choices, choices_function, and choices_method parameters ############################################################################################################ @@ -48,27 +50,50 @@ class AutoCompleteTester(cmd2.Cmd): choices_parser = Cmd2ArgParser() # Flags args for choices command - choices_parser.add_argument("-n", "--no_choices", help="a flag with no choices") - choices_parser.add_argument("-l", "--choices_list", help="a flag populated with a choices list", + choices_parser.add_argument("-l", "--list", help="a flag populated with a choices list", choices=static_choices_list) - choices_parser.add_argument("-f", "--choices_function", help="a flag populated with a choices function", + choices_parser.add_argument("-f", "--function", help="a flag populated with a choices function", choices_function=choices_function) - choices_parser.add_argument("-m", "--choices_method", help="a flag populated with a choices method", + choices_parser.add_argument("-m", "--method", help="a flag populated with a choices method", choices_method=choices_method) # Positional args for choices command - choices_parser.add_argument("no_choice_pos", help="a positional with no choices") - choices_parser.add_argument("choices_list_pos", help="a positional populated with a choices list", + choices_parser.add_argument("list_pos", help="a positional populated with a choices list", choices=static_choices_list) - choices_parser.add_argument("choices_function_pos", help="a positional populated with a choices function", + choices_parser.add_argument("function_pos", help="a positional populated with a choices function", choices_function=choices_function) - choices_parser.add_argument("choices_method_pos", help="a positional populated with a choices method", + choices_parser.add_argument("method_pos", help="a positional populated with a choices method", choices_method=choices_method) @with_argparser(choices_parser) def do_choices(self, args: argparse.Namespace) -> None: pass + ############################################################################################################ + # Begin code related to testing completer_function and completer_method parameters + ############################################################################################################ + def completer_method(self, text: str, line: str, begidx: int, endidx: int) -> List[str]: + """Tab completion method""" + return basic_complete(text, line, begidx, endidx, completions_from_method) + + completer_parser = Cmd2ArgParser() + + # Flags args for completer command + completer_parser.add_argument("-f", "--function", help="a flag populated with a choices function", + completer_function=completer_function) + completer_parser.add_argument("-m", "--method", help="a flag populated with a choices 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) + + @with_argparser(completer_parser) + def do_completer(self, args: argparse.Namespace) -> None: + pass + @pytest.fixture def ac_app(): @@ -83,76 +108,83 @@ def test_help_basic(ac_app): assert out1 == out2 -def test_autocomp_flags(ac_app): - text = '-' +@pytest.mark.parametrize('text, completions', [ + ('-', ['--function', '--help', '--list', '--method', '-f', '-h', '-l', '-m']), + ('--', ['--function', '--help', '--list', '--method']), + ('-f', ['-f ']), + ('--f', ['--function ']), +]) +def test_autcomp_flag_completion(ac_app, text, completions): line = 'choices {}'.format(text) endidx = len(line) begidx = endidx - len(text) first_match = complete_tester(text, line, begidx, endidx, ac_app) - assert first_match is not None and \ - ac_app.completion_matches == ['--choices_function', '--choices_list', '--choices_method', '--help', - '--no_choices', '-f', '-h', '-l', '-m', '-n'] + assert first_match is not None and ac_app.completion_matches == completions -def test_autcomp_flag_hint(ac_app, capsys): - text = '' - line = 'choices -n {}'.format(text) +@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']), +]) +def test_autocomp_flag_choices_completion(ac_app, flag, text, completions): + line = 'choices {} {}'.format(flag, text) endidx = len(line) begidx = endidx - len(text) first_match = complete_tester(text, line, begidx, endidx, ac_app) - out, err = capsys.readouterr() - - assert first_match is None - assert 'a flag with no choices' in out + assert first_match is not None and ac_app.completion_matches == sorted(completions, key=ac_app.matches_sort_key) -def test_autcomp_flag_completion(ac_app): - text = '--ch' - line = 'choices {}'.format(text) +@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']), +]) +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) endidx = len(line) begidx = endidx - len(text) first_match = complete_tester(text, line, begidx, endidx, ac_app) - assert first_match is not None and \ - ac_app.completion_matches == ['--choices_function', '--choices_list', '--choices_method'] - -@pytest.mark.parametrize('flag, completions', [ - ('-l', static_choices_list), - ('--choices_list', static_choices_list), - ('-f', choices_from_function), - ('--choices_function', choices_from_function), - ('-m', choices_from_method), - ('--choices_method', choices_from_method), + assert first_match is not None and ac_app.completion_matches == sorted(completions, key=ac_app.matches_sort_key) + +@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_choices_completion(ac_app, flag, completions): - text = '' - line = 'choices {} {}'.format(flag, text) +def test_autocomp_flag_completers(ac_app, flag, text, completions): + line = 'completer {} {}'.format(flag, text) endidx = len(line) begidx = endidx - len(text) first_match = complete_tester(text, line, begidx, endidx, ac_app) - assert first_match is not None and \ - ac_app.completion_matches == sorted(completions, key=ac_app.matches_sort_key) - + assert first_match is not None and ac_app.completion_matches == sorted(completions, key=ac_app.matches_sort_key) -@pytest.mark.parametrize('pos, completions', [ - (2, static_choices_list), # choices_list_pos - (3, choices_from_function), # choices_function_pos - (4, choices_from_method), # choices_method_pos +@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_choices_completion(ac_app, pos, completions): - # Test completions of positional arguments by generating a line were preceding positionals are already filled - text = '' - line = 'choices {} {}'.format('foo ' * (pos - 1), text) +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) endidx = len(line) begidx = endidx - len(text) first_match = complete_tester(text, line, begidx, endidx, ac_app) - assert first_match is not None and \ - ac_app.completion_matches == sorted(completions, key=ac_app.matches_sort_key) - + assert first_match is not None and ac_app.completion_matches == sorted(completions, key=ac_app.matches_sort_key) # def test_autcomp_hint_in_narg_range(cmd2_app, capsys): # text = '' @@ -315,4 +347,4 @@ def test_is_potential_flag(): # Valid flags assert is_potential_flag('-flag', parser) - assert is_potential_flag('--flag', parser) \ No newline at end of file + assert is_potential_flag('--flag', parser) -- cgit v1.2.1 From f8f06bff169dca1f0c6ee1dbb2d61c347490b3bb Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Sat, 6 Jul 2019 18:03:12 -0400 Subject: More unit tests --- tests/test_argparse_completer.py | 65 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 60 insertions(+), 5 deletions(-) (limited to 'tests') diff --git a/tests/test_argparse_completer.py b/tests/test_argparse_completer.py index 839cdf7a..26bea794 100644 --- a/tests/test_argparse_completer.py +++ b/tests/test_argparse_completer.py @@ -79,9 +79,9 @@ class AutoCompleteTester(cmd2.Cmd): completer_parser = Cmd2ArgParser() # Flags args for completer command - completer_parser.add_argument("-f", "--function", help="a flag populated with a choices function", + 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 populated with a choices method", + completer_parser.add_argument("-m", "--method", help="a flag using a completer method", completer_method=completer_method) # Positional args for completer command @@ -102,12 +102,67 @@ def ac_app(): return app -def test_help_basic(ac_app): - out1, err1 = run_cmd(ac_app, 'choices -h') - out2, err2 = run_cmd(ac_app, 'help choices') +def test_help(ac_app): + out1, err1 = run_cmd(ac_app, 'alias -h') + out2, err2 = run_cmd(ac_app, 'help alias') assert out1 == out2 +def test_help_subcommand(ac_app): + out1, err1 = run_cmd(ac_app, 'alias create -h') + out2, err2 = run_cmd(ac_app, 'help alias create') + assert out1 == out2 + + +def test_complete_help(ac_app): + text = 'al' + line = 'help {}'.format(text) + endidx = len(line) + begidx = endidx - len(text) + + first_match = complete_tester(text, line, begidx, endidx, ac_app) + assert first_match is not None and ac_app.completion_matches == ['alias '] + + +def test_complete_help_subcommand(ac_app): + text = 'cre' + line = 'help alias {}'.format(text) + endidx = len(line) + begidx = endidx - len(text) + + first_match = complete_tester(text, line, begidx, endidx, ac_app) + assert first_match is not None and ac_app.completion_matches == ['create '] + + +@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): + run_cmd(ac_app, 'alias create fake{} help'.format(i)) + + assert len(ac_app.aliases) == num_aliases + + text = 'fake' + line = 'alias list {}'.format(text) + endidx = len(line) + begidx = endidx - len(text) + + first_match = complete_tester(text, line, begidx, endidx, ac_app) + assert first_match is not None + assert len(ac_app.completion_matches) == num_aliases + assert len(ac_app.display_matches) == num_aliases + + # If show_description is True, the alias's value will be in the display text + assert ('help' in ac_app.display_matches[0]) == show_description + + @pytest.mark.parametrize('text, completions', [ ('-', ['--function', '--help', '--list', '--method', '-f', '-h', '-l', '-m']), ('--', ['--function', '--help', '--list', '--method']), -- cgit v1.2.1 From 901aa9ed176507071198e84978891c69cc6e9d3b Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Sat, 6 Jul 2019 20:30:39 -0400 Subject: Fixed issue where default descriptive header wasn't set correctly --- tests/test_argparse_completer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/tests/test_argparse_completer.py b/tests/test_argparse_completer.py index 26bea794..3c4fca77 100644 --- a/tests/test_argparse_completer.py +++ b/tests/test_argparse_completer.py @@ -49,7 +49,7 @@ class AutoCompleteTester(cmd2.Cmd): choices_parser = Cmd2ArgParser() - # Flags args for choices command + # Flag args for choices command 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", @@ -78,7 +78,7 @@ class AutoCompleteTester(cmd2.Cmd): completer_parser = Cmd2ArgParser() - # Flags args for completer command + # 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", -- cgit v1.2.1 From 47287f42c4b36dcce9f99ed09bbb8ec579439273 Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Sat, 6 Jul 2019 22:31:14 -0400 Subject: More unit tests --- tests/test_argparse_completer.py | 86 +++++++++++++++++++++++++--------------- 1 file changed, 54 insertions(+), 32 deletions(-) (limited to 'tests') diff --git a/tests/test_argparse_completer.py b/tests/test_argparse_completer.py index 3c4fca77..c73290e0 100644 --- a/tests/test_argparse_completer.py +++ b/tests/test_argparse_completer.py @@ -10,7 +10,7 @@ import pytest import cmd2 from cmd2 import with_argparser -from cmd2.argparse_completer import is_potential_flag +from cmd2.argparse_completer import CompletionItem, is_potential_flag, DEFAULT_DESCRIPTIVE_HEADER from cmd2.argparse_custom import Cmd2ArgParser from cmd2.utils import StdSim, basic_complete from .conftest import run_cmd, complete_tester @@ -47,6 +47,14 @@ class AutoCompleteTester(cmd2.Cmd): """Method that provides choices""" return choices_from_method + def completion_item_method(self) -> List[CompletionItem]: + """Choices method that returns CompletionItems""" + items = [] + for i in range(0, 10): + main_str = 'main_str{}'.format(i) + items.append(CompletionItem(main_str, desc='blah blah')) + return items + choices_parser = Cmd2ArgParser() # Flag args for choices command @@ -56,6 +64,8 @@ class AutoCompleteTester(cmd2.Cmd): 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('-n', "--no_header", help='this arg has a no descriptive header', + choices_method=completion_item_method) # Positional args for choices command choices_parser.add_argument("list_pos", help="a positional populated with a choices list", @@ -134,38 +144,9 @@ def test_complete_help_subcommand(ac_app): assert first_match is not None and ac_app.completion_matches == ['create '] -@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): - run_cmd(ac_app, 'alias create fake{} help'.format(i)) - - assert len(ac_app.aliases) == num_aliases - - text = 'fake' - line = 'alias list {}'.format(text) - endidx = len(line) - begidx = endidx - len(text) - - first_match = complete_tester(text, line, begidx, endidx, ac_app) - assert first_match is not None - assert len(ac_app.completion_matches) == num_aliases - assert len(ac_app.display_matches) == num_aliases - - # If show_description is True, the alias's value will be in the display text - assert ('help' in ac_app.display_matches[0]) == show_description - - @pytest.mark.parametrize('text, completions', [ - ('-', ['--function', '--help', '--list', '--method', '-f', '-h', '-l', '-m']), - ('--', ['--function', '--help', '--list', '--method']), + ('-', ['--function', '--help', '--list', '--method', '--no_header', '-f', '-h', '-l', '-m', '-n']), + ('--', ['--function', '--help', '--list', '--method', '--no_header']), ('-f', ['-f ']), ('--f', ['--function ']), ]) @@ -241,6 +222,47 @@ def test_autocomp_positional_completers(ac_app, pos, text, completions): first_match = complete_tester(text, line, begidx, endidx, ac_app) assert first_match is not None and ac_app.completion_matches == sorted(completions, key=ac_app.matches_sort_key) + +@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): + run_cmd(ac_app, 'alias create fake{} help'.format(i)) + + assert len(ac_app.aliases) == num_aliases + + text = 'fake' + line = 'alias list {}'.format(text) + endidx = len(line) + begidx = endidx - len(text) + + first_match = complete_tester(text, line, begidx, endidx, ac_app) + assert first_match is not None + assert len(ac_app.completion_matches) == num_aliases + assert len(ac_app.display_matches) == num_aliases + + # If show_description is True, the alias's value will be in the display text + assert ('help' in ac_app.display_matches[0]) == show_description + + +def test_completion_items_default_header(ac_app): + text = '' + line = 'choices -n {}'.format(text) + endidx = len(line) + begidx = endidx - len(text) + + # This positional argument did not provide a descriptive header, so it should be DEFAULT_DESCRIPTIVE_HEADER + complete_tester(text, line, begidx, endidx, ac_app) + assert DEFAULT_DESCRIPTIVE_HEADER in ac_app.completion_header + + # def test_autcomp_hint_in_narg_range(cmd2_app, capsys): # text = '' # line = 'suggest -d 2 {}'.format(text) -- cgit v1.2.1 From f8db8b766540920de6c85a26a6740170455b9354 Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Sun, 7 Jul 2019 00:18:07 -0400 Subject: Hiding flags that have help value of SUPRESSED in tab completion results Added more unit tests for argparse completer --- tests/test_argparse_completer.py | 91 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) (limited to 'tests') diff --git a/tests/test_argparse_completer.py b/tests/test_argparse_completer.py index c73290e0..a3fa6a59 100644 --- a/tests/test_argparse_completer.py +++ b/tests/test_argparse_completer.py @@ -104,6 +104,21 @@ class AutoCompleteTester(cmd2.Cmd): def do_completer(self, args: argparse.Namespace) -> None: pass + ############################################################################################################ + # Begin code related to testing tab hints + ############################################################################################################ + hint_parser = Cmd2ArgParser() + hint_parser.add_argument('-f', '--flag', help='a flag arg') + hint_parser.add_argument('-s', '--suppressed_help', help=argparse.SUPPRESS) + hint_parser.add_argument('-t', '--suppressed_hint', help='a flag arg', suppress_tab_hint=True) + + hint_parser.add_argument('hint_pos', help='here is a hint\nwith new lines') + hint_parser.add_argument('no_help_pos') + + @with_argparser(hint_parser) + def do_hint(self, args: argparse.Namespace) -> None: + pass + @pytest.fixture def ac_app(): @@ -263,6 +278,82 @@ def test_completion_items_default_header(ac_app): assert DEFAULT_DESCRIPTIVE_HEADER in ac_app.completion_header +def test_autocomp_hint_flag(ac_app, capsys): + text = '' + line = 'hint --flag {}'.format(text) + endidx = len(line) + begidx = endidx - len(text) + + first_match = complete_tester(text, line, begidx, endidx, ac_app) + out, err = capsys.readouterr() + + assert first_match is None + assert out == ''' +Hint: + -f, --flag FLAG a flag arg + +''' + + +def test_autocomp_hint_suppressed_help(ac_app, capsys): + text = '' + line = 'hint --suppressed_help {}'.format(text) + endidx = len(line) + begidx = endidx - len(text) + + first_match = complete_tester(text, line, begidx, endidx, ac_app) + out, err = capsys.readouterr() + + assert first_match is None + assert not out + + +def test_autocomp_hint_suppressed_hint(ac_app, capsys): + text = '' + line = 'hint --suppressed_hint {}'.format(text) + endidx = len(line) + begidx = endidx - len(text) + + first_match = complete_tester(text, line, begidx, endidx, ac_app) + out, err = capsys.readouterr() + + assert first_match is None + assert not out + + +def test_autocomp_hint_pos(ac_app, capsys): + text = '' + line = 'hint {}'.format(text) + endidx = len(line) + begidx = endidx - len(text) + + first_match = complete_tester(text, line, begidx, endidx, ac_app) + out, err = capsys.readouterr() + + assert first_match is None + assert out == ''' +Hint: + HINT_POS here is a hint + with new lines + +''' + +def test_autocomp_hint_no_help(ac_app, capsys): + text = '' + line = 'hint foo {}'.format(text) + endidx = len(line) + begidx = endidx - len(text) + + first_match = complete_tester(text, line, begidx, endidx, ac_app) + out, err = capsys.readouterr() + + assert first_match is None + assert not out == ''' +Hint: + NO_HELP_POS + +''' + # def test_autcomp_hint_in_narg_range(cmd2_app, capsys): # text = '' # line = 'suggest -d 2 {}'.format(text) -- cgit v1.2.1 From bb2dd69bd04f5dccff9474c018eb6b6eea74c6af Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Sun, 7 Jul 2019 21:45:52 -0400 Subject: Moved all custom argparse classes intended for normal development to argparse_custom.py. Lazy loading AutoCompleter in cmd2 instance methods to allow argparse_completer.py to import cmd2.Cmd class. This Architecture makes more sense because AutoCompleter depends on cmd2.Cmd. --- tests/test_argparse_completer.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'tests') diff --git a/tests/test_argparse_completer.py b/tests/test_argparse_completer.py index a3fa6a59..6e092619 100644 --- a/tests/test_argparse_completer.py +++ b/tests/test_argparse_completer.py @@ -9,9 +9,8 @@ from typing import List import pytest import cmd2 -from cmd2 import with_argparser -from cmd2.argparse_completer import CompletionItem, is_potential_flag, DEFAULT_DESCRIPTIVE_HEADER -from cmd2.argparse_custom import Cmd2ArgParser +from cmd2 import with_argparser, Cmd2ArgParser, CompletionItem +from cmd2.argparse_completer import is_potential_flag, DEFAULT_DESCRIPTIVE_HEADER from cmd2.utils import StdSim, basic_complete from .conftest import run_cmd, complete_tester @@ -208,6 +207,7 @@ def test_autocomp_positional_choices_completion(ac_app, pos, text, completions): first_match = complete_tester(text, line, begidx, endidx, ac_app) assert first_match is not None and ac_app.completion_matches == sorted(completions, key=ac_app.matches_sort_key) + @pytest.mark.parametrize('flag, text, completions', [ ('-f', '', completions_from_function), ('--function', 'f', ['function', 'fairly']), @@ -222,6 +222,7 @@ def test_autocomp_flag_completers(ac_app, flag, text, completions): first_match = complete_tester(text, line, begidx, endidx, ac_app) assert first_match is not None and ac_app.completion_matches == sorted(completions, key=ac_app.matches_sort_key) + @pytest.mark.parametrize('pos, text, completions', [ (1, '', completions_from_function), (1, 'c', ['completions', 'complete']), @@ -338,6 +339,7 @@ Hint: ''' + def test_autocomp_hint_no_help(ac_app, capsys): text = '' line = 'hint foo {}'.format(text) @@ -503,6 +505,7 @@ Hint: # # Since -- appeared before the -- being completed, nothing should be completed # assert complete_tester(text, line, begidx, endidx, cmd2_app) is None + def test_is_potential_flag(): parser = Cmd2ArgParser() -- cgit v1.2.1 From 8d3d59801ca690f718ed9814c9e124e27040c141 Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Sun, 7 Jul 2019 22:44:33 -0400 Subject: More argparse completer unit tests --- tests/test_argparse_completer.py | 89 +++++++++++++++++++++++++++++----------- 1 file changed, 65 insertions(+), 24 deletions(-) (limited to 'tests') diff --git a/tests/test_argparse_completer.py b/tests/test_argparse_completer.py index 6e092619..fd43c91c 100644 --- a/tests/test_argparse_completer.py +++ b/tests/test_argparse_completer.py @@ -33,12 +33,59 @@ def completer_function(text: str, line: str, begidx: int, endidx: int) -> List[s return basic_complete(text, line, begidx, endidx, completions_from_function) -# noinspection PyMethodMayBeStatic +# noinspection PyMethodMayBeStatic,PyUnusedLocal class AutoCompleteTester(cmd2.Cmd): """Cmd2 app that exercises AutoCompleter class""" def __init__(self): super().__init__() + ############################################################################################################ + # Begin code related to help and command name completion + ############################################################################################################ + def _music_create(self, args: argparse.Namespace) -> None: + """Implements the 'music create' command""" + self.poutput('music create') + + def _music_create_jazz(self, args: argparse.Namespace) -> None: + """Implements the 'music create jazz' command""" + self.poutput('music create jazz') + + def _music_create_rock(self, args: argparse.Namespace) -> None: + """Implements the 'music create rock' command""" + self.poutput('music create rock') + + # Top level parser for music command + music_parser = Cmd2ArgParser(description='Manage music', prog='music') + + # Add sub-commands to music + music_subparsers = music_parser.add_subparsers() + + # music -> create + music_create_parser = music_subparsers.add_parser('create', help='Create music') + music_create_parser.set_defaults(func=_music_create) + + # Add sub-commands to music -> create + music_create_subparsers = music_create_parser.add_subparsers() + + # music -> create -> jazz + music_create_jazz_parser = music_create_subparsers.add_parser('jazz', help='Create jazz') + music_create_jazz_parser.set_defaults(func=_music_create_jazz) + + # music -> create -> rock + music_create_rock_parser = music_create_subparsers.add_parser('rock', help='Create rocks') + music_create_rock_parser.set_defaults(func=_music_create_rock) + + @with_argparser(music_parser) + def do_music(self, args: argparse.Namespace) -> None: + """Music command""" + func = getattr(args, 'func', None) + if func is not None: + # Call whatever sub-command function was selected + func(self, args) + else: + # No sub-command was provided, so call help + self.do_help('music') + ############################################################################################################ # Begin code related to testing choices, choices_function, and choices_method parameters ############################################################################################################ @@ -126,36 +173,30 @@ def ac_app(): return app -def test_help(ac_app): - out1, err1 = run_cmd(ac_app, 'alias -h') - out2, err2 = run_cmd(ac_app, 'help alias') - assert out1 == out2 - - -def test_help_subcommand(ac_app): - out1, err1 = run_cmd(ac_app, 'alias create -h') - out2, err2 = run_cmd(ac_app, 'help alias create') +@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)) assert out1 == out2 -def test_complete_help(ac_app): - text = 'al' - line = 'help {}'.format(text) - endidx = len(line) - begidx = endidx - len(text) - - first_match = complete_tester(text, line, begidx, endidx, ac_app) - assert first_match is not None and ac_app.completion_matches == ['alias '] - - -def test_complete_help_subcommand(ac_app): - text = 'cre' - line = 'help alias {}'.format(text) +@pytest.mark.parametrize('command, text, completions', [ + ('', 'mu', ['music ']), + ('music', 'cre', ['create ']), + ('music create', '', ['jazz', 'rock']) +]) +def test_complete_help(ac_app, command, text, completions): + line = 'help {} {}'.format(command, text) endidx = len(line) begidx = endidx - len(text) first_match = complete_tester(text, line, begidx, endidx, ac_app) - assert first_match is not None and ac_app.completion_matches == ['create '] + assert first_match is not None and ac_app.completion_matches == completions @pytest.mark.parametrize('text, completions', [ -- cgit v1.2.1 From d9a48462c90807a4a8e8c5d646a62d1bd883529f Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Sun, 7 Jul 2019 23:02:00 -0400 Subject: More unit tests --- tests/test_argparse_completer.py | 39 +++++++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 8 deletions(-) (limited to 'tests') diff --git a/tests/test_argparse_completer.py b/tests/test_argparse_completer.py index fd43c91c..1b6d1f4b 100644 --- a/tests/test_argparse_completer.py +++ b/tests/test_argparse_completer.py @@ -86,6 +86,18 @@ class AutoCompleteTester(cmd2.Cmd): # No sub-command was provided, so call help self.do_help('music') + ############################################################################################################ + # Begin code related to flag completion + ############################################################################################################ + flag_parser = Cmd2ArgParser() + flag_parser.add_argument('-n', '--normal_flag', help='A normal flag', action='store_true') + flag_parser.add_argument('-o', '--other_normal_flag', help='The other normal flag', action='store_true') + flag_parser.add_argument('-s', '--suppressed_flag', help=argparse.SUPPRESS, action='store_true') + + @with_argparser(flag_parser) + def do_flag(self, args: argparse.Namespace) -> None: + pass + ############################################################################################################ # Begin code related to testing choices, choices_function, and choices_method parameters ############################################################################################################ @@ -199,19 +211,30 @@ def test_complete_help(ac_app, command, text, completions): assert first_match is not None and ac_app.completion_matches == completions -@pytest.mark.parametrize('text, completions', [ - ('-', ['--function', '--help', '--list', '--method', '--no_header', '-f', '-h', '-l', '-m', '-n']), - ('--', ['--function', '--help', '--list', '--method', '--no_header']), - ('-f', ['-f ']), - ('--f', ['--function ']), +@pytest.mark.parametrize('used_flags, text, completions', [ + ('', '-', ['--help', '--normal_flag', '--other_normal_flag', '-h', '-n', '-o']), + ('', '--', ['--help', '--normal_flag', '--other_normal_flag']), + ('', '-n', ['-n ']), + ('', '--n', ['--normal_flag ']), + ('', '-s', []), + ('', '--s', []), + ('-h', '-', ['--normal_flag', '--other_normal_flag', '-n', '-o']), + ('-h --normal_flag', '-', ['--other_normal_flag', '-o']), + ('-h --normal_flag', '--', ['--other_normal_flag ']), + ('-h --normal_flag -o', '-', []), ]) -def test_autcomp_flag_completion(ac_app, text, completions): - line = 'choices {}'.format(text) +def test_autcomp_flag_completion(ac_app, used_flags, text, completions): + line = 'flag {} {}'.format(used_flags, text) endidx = len(line) begidx = endidx - len(text) first_match = complete_tester(text, line, begidx, endidx, ac_app) - assert first_match is not None and ac_app.completion_matches == completions + if completions: + assert first_match is not None + else: + assert first_match is None + + assert ac_app.completion_matches == completions @pytest.mark.parametrize('flag, text, completions', [ -- cgit v1.2.1 From 3140cd47d997d3e23789774f37ec601be408177b Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Sun, 7 Jul 2019 23:12:09 -0400 Subject: More unit tests --- tests/test_argparse_completer.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'tests') diff --git a/tests/test_argparse_completer.py b/tests/test_argparse_completer.py index 1b6d1f4b..08b829f7 100644 --- a/tests/test_argparse_completer.py +++ b/tests/test_argparse_completer.py @@ -15,6 +15,7 @@ from cmd2.utils import StdSim, basic_complete from .conftest import run_cmd, complete_tester # Lists used in our tests +static_int_choices_list = [1, 2, 3, 4, 5] static_choices_list = ['static', 'choices', 'stop', 'here'] choices_from_function = ['choices', 'function', 'chatty', 'smith'] choices_from_method = ['choices', 'method', 'most', 'improved'] @@ -115,7 +116,7 @@ class AutoCompleteTester(cmd2.Cmd): choices_parser = Cmd2ArgParser() - # Flag args for choices command + # 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", @@ -124,6 +125,8 @@ class AutoCompleteTester(cmd2.Cmd): choices_method=choices_method) choices_parser.add_argument('-n', "--no_header", help='this arg has a no descriptive header', choices_method=completion_item_method) + choices_parser.add_argument('-i', '--int', type=int, help='a flag with an int type', + choices=static_int_choices_list) # Positional args for choices command choices_parser.add_argument("list_pos", help="a positional populated with a choices list", @@ -221,7 +224,7 @@ def test_complete_help(ac_app, command, text, completions): ('-h', '-', ['--normal_flag', '--other_normal_flag', '-n', '-o']), ('-h --normal_flag', '-', ['--other_normal_flag', '-o']), ('-h --normal_flag', '--', ['--other_normal_flag ']), - ('-h --normal_flag -o', '-', []), + ('-h --normal_flag -o', '-', []) ]) def test_autcomp_flag_completion(ac_app, used_flags, text, completions): line = 'flag {} {}'.format(used_flags, text) @@ -244,6 +247,8 @@ def test_autcomp_flag_completion(ac_app, used_flags, text, completions): ('--function', 'ch', ['choices', 'chatty']), ('-m', '', choices_from_method), ('--method', 'm', ['method', 'most']), + ('-i', '', [str(i) for i in static_int_choices_list]), + ('--int', '1', ['1 ']) ]) def test_autocomp_flag_choices_completion(ac_app, flag, text, completions): line = 'choices {} {}'.format(flag, text) @@ -260,7 +265,7 @@ def test_autocomp_flag_choices_completion(ac_app, flag, text, completions): (2, '', choices_from_function), (2, 'ch', ['choices', 'chatty']), (3, '', choices_from_method), - (3, 'm', ['method', 'most']), + (3, 'm', ['method', 'most']) ]) def test_autocomp_positional_choices_completion(ac_app, pos, text, completions): # Generate line were preceding positionals are already filled @@ -276,7 +281,7 @@ def test_autocomp_positional_choices_completion(ac_app, pos, text, completions): ('-f', '', completions_from_function), ('--function', 'f', ['function', 'fairly']), ('-m', '', completions_from_method), - ('--method', 'm', ['method', 'missed']), + ('--method', 'm', ['method', 'missed']) ]) def test_autocomp_flag_completers(ac_app, flag, text, completions): line = 'completer {} {}'.format(flag, text) @@ -291,7 +296,7 @@ def test_autocomp_flag_completers(ac_app, flag, text, completions): (1, '', completions_from_function), (1, 'c', ['completions', 'complete']), (2, '', completions_from_method), - (2, 'm', ['method', 'missed']), + (2, 'm', ['method', 'missed']) ]) def test_autocomp_positional_completers(ac_app, pos, text, completions): # Generate line were preceding positionals are already filled @@ -309,7 +314,7 @@ def test_autocomp_positional_completers(ac_app, pos, text, completions): # which defaults to 50. (1, False), (5, True), - (100, False), + (100, False) ]) def test_completion_items(ac_app, num_aliases, show_description): # Create aliases -- cgit v1.2.1 From 4a5b23284be04258735ba421f62825520651e14e Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Mon, 8 Jul 2019 11:50:17 -0400 Subject: More unit tests --- tests/test_argparse_completer.py | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/test_argparse_completer.py b/tests/test_argparse_completer.py index 08b829f7..82a825cd 100644 --- a/tests/test_argparse_completer.py +++ b/tests/test_argparse_completer.py @@ -10,7 +10,6 @@ import pytest import cmd2 from cmd2 import with_argparser, Cmd2ArgParser, CompletionItem -from cmd2.argparse_completer import is_potential_flag, DEFAULT_DESCRIPTIVE_HEADER from cmd2.utils import StdSim, basic_complete from .conftest import run_cmd, complete_tester @@ -85,6 +84,7 @@ class AutoCompleteTester(cmd2.Cmd): func(self, args) else: # No sub-command was provided, so call help + # noinspection PyTypeChecker self.do_help('music') ############################################################################################################ @@ -338,6 +338,8 @@ def test_completion_items(ac_app, num_aliases, show_description): def test_completion_items_default_header(ac_app): + from cmd2.argparse_completer import DEFAULT_DESCRIPTIVE_HEADER + text = '' line = 'choices -n {}'.format(text) endidx = len(line) @@ -576,6 +578,7 @@ Hint: def test_is_potential_flag(): + from cmd2.argparse_completer import is_potential_flag parser = Cmd2ArgParser() # Not valid flags @@ -588,3 +591,23 @@ def test_is_potential_flag(): # Valid flags assert is_potential_flag('-flag', parser) assert is_potential_flag('--flag', parser) + + +def test_complete_command_no_tokens(ac_app): + from cmd2.argparse_completer import AutoCompleter + + parser = Cmd2ArgParser() + ac = AutoCompleter(parser, ac_app) + + completions = ac.complete_command(tokens=[], text='', line='', begidx=0, endidx=0) + assert not completions + + +def test_complete_command_help_no_tokens(ac_app): + from cmd2.argparse_completer import AutoCompleter + + parser = Cmd2ArgParser() + ac = AutoCompleter(parser, ac_app) + + completions = ac.complete_command_help(tokens=[], text='', line='', begidx=0, endidx=0) + assert not completions -- cgit v1.2.1 From bdcc179661a16f9bf648f230e5713a81e391f083 Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Mon, 8 Jul 2019 13:23:01 -0400 Subject: Fixed issue where -- was not handled properly in AutoCompleter when the parser's prefix characters did not include - --- tests/test_argparse_completer.py | 82 +++++++++++++++++++++------------------- 1 file changed, 44 insertions(+), 38 deletions(-) (limited to 'tests') diff --git a/tests/test_argparse_completer.py b/tests/test_argparse_completer.py index 82a825cd..ad37629f 100644 --- a/tests/test_argparse_completer.py +++ b/tests/test_argparse_completer.py @@ -90,6 +90,8 @@ class AutoCompleteTester(cmd2.Cmd): ############################################################################################################ # Begin code related to flag completion ############################################################################################################ + + # Uses default flag prefix value (-) flag_parser = Cmd2ArgParser() flag_parser.add_argument('-n', '--normal_flag', help='A normal flag', action='store_true') flag_parser.add_argument('-o', '--other_normal_flag', help='The other normal flag', action='store_true') @@ -99,6 +101,16 @@ class AutoCompleteTester(cmd2.Cmd): def do_flag(self, args: argparse.Namespace) -> None: pass + # Uses non-default flag prefix value (+) + plus_flag_parser = Cmd2ArgParser(prefix_chars='+') + plus_flag_parser.add_argument('+n', '++normal_flag', help='A normal flag', action='store_true') + plus_flag_parser.add_argument('+o', '++other_normal_flag', help='The other normal flag', action='store_true') + plus_flag_parser.add_argument('+s', '++suppressed_flag', help=argparse.SUPPRESS, action='store_true') + + @with_argparser(plus_flag_parser) + def do_plus_flag(self, args: argparse.Namespace) -> None: + pass + ############################################################################################################ # Begin code related to testing choices, choices_function, and choices_method parameters ############################################################################################################ @@ -214,20 +226,39 @@ def test_complete_help(ac_app, command, text, completions): assert first_match is not None and ac_app.completion_matches == completions -@pytest.mark.parametrize('used_flags, text, completions', [ - ('', '-', ['--help', '--normal_flag', '--other_normal_flag', '-h', '-n', '-o']), - ('', '--', ['--help', '--normal_flag', '--other_normal_flag']), - ('', '-n', ['-n ']), - ('', '--n', ['--normal_flag ']), - ('', '-s', []), - ('', '--s', []), - ('-h', '-', ['--normal_flag', '--other_normal_flag', '-n', '-o']), - ('-h --normal_flag', '-', ['--other_normal_flag', '-o']), - ('-h --normal_flag', '--', ['--other_normal_flag ']), - ('-h --normal_flag -o', '-', []) +@pytest.mark.parametrize('command_and_args, text, completions', [ + # Default flag prefix character (-) + ('flag', '-', ['--help', '--normal_flag', '--other_normal_flag', '-h', '-n', '-o']), + ('flag', '--', ['--help', '--normal_flag', '--other_normal_flag']), + ('flag', '-n', ['-n ']), + ('flag', '--n', ['--normal_flag ']), + ('flag', '-s', []), + ('flag', '--s', []), + ('flag -h', '-', ['--normal_flag', '--other_normal_flag', '-n', '-o']), + ('flag -h --normal_flag', '-', ['--other_normal_flag', '-o']), + ('flag -h --normal_flag', '--', ['--other_normal_flag ']), + ('flag -h --normal_flag -o', '-', []), + + # Non-default flag prefix character (+) + ('plus_flag', '+', ['++help', '++normal_flag', '++other_normal_flag', '+h', '+n', '+o']), + ('plus_flag', '++', ['++help', '++normal_flag', '++other_normal_flag']), + ('plus_flag', '+n', ['+n ']), + ('plus_flag', '++n', ['++normal_flag ']), + ('plus_flag', '+s', []), + ('plus_flag', '++s', []), + ('plus_flag +h', '+', ['++normal_flag', '++other_normal_flag', '+n', '+o']), + ('plus_flag +h ++normal_flag', '+', ['++other_normal_flag', '+o']), + ('plus_flag +h ++normal_flag', '++', ['++other_normal_flag ']), + ('plus_flag +h ++normal_flag +o', '+', []), + + # Flag completion should not occur after '--' since that tells argparse the all remaining arguments of non-flags + ('flag --', '--', []), + ('flag --help --', '--', []), + ('plus_flag --', '++', []), + ('plus_flag ++help --', '++', []) ]) -def test_autcomp_flag_completion(ac_app, used_flags, text, completions): - line = 'flag {} {}'.format(used_flags, text) +def test_autcomp_flag_completion(ac_app, command_and_args, text, completions): + line = '{} {}'.format(command_and_args, text) endidx = len(line) begidx = endidx - len(text) @@ -550,31 +581,6 @@ Hint: # assert complete_tester(text, line, begidx, endidx, cmd2_app) is None # # -# def test_completion_after_double_dash(cmd2_app): -# """ -# Test completion after --, which argparse says (all args after -- are non-options) -# All of these tests occur outside of an argparse.REMAINDER section since those tests -# are handled in test_argparse_remainder_flag_completion -# """ -# -# # Test -- as the last token -# text = '--' -# line = 'help {}'.format(text) -# endidx = len(line) -# begidx = endidx - len(text) -# -# # Since -- is the last token, then it should show flag choices -# first_match = complete_tester(text, line, begidx, endidx, cmd2_app) -# assert first_match is not None and '--help' in cmd2_app.completion_matches -# -# # Test -- to end all flag completion -# text = '--' -# line = 'help -- {}'.format(text) -# endidx = len(line) -# begidx = endidx - len(text) -# -# # Since -- appeared before the -- being completed, nothing should be completed -# assert complete_tester(text, line, begidx, endidx, cmd2_app) is None def test_is_potential_flag(): -- cgit v1.2.1 From 36209ae6d9563054d57c630729be3c9002453729 Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Mon, 8 Jul 2019 14:49:53 -0400 Subject: Added code to handle flags with action set to append, append_const, and count in AutoCompleter --- tests/test_argparse_completer.py | 44 ++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 22 deletions(-) (limited to 'tests') diff --git a/tests/test_argparse_completer.py b/tests/test_argparse_completer.py index ad37629f..b70ab29d 100644 --- a/tests/test_argparse_completer.py +++ b/tests/test_argparse_completer.py @@ -94,7 +94,9 @@ class AutoCompleteTester(cmd2.Cmd): # Uses default flag prefix value (-) flag_parser = Cmd2ArgParser() flag_parser.add_argument('-n', '--normal_flag', help='A normal flag', action='store_true') - flag_parser.add_argument('-o', '--other_normal_flag', help='The other normal flag', action='store_true') + flag_parser.add_argument('-a', '--append_flag', help='Append flag', action='append') + flag_parser.add_argument('-o', '--append_const_flag', help='Append const flag', action='append_const', const=True) + flag_parser.add_argument('-c', '--count_flag', help='Count flag', action='count') flag_parser.add_argument('-s', '--suppressed_flag', help=argparse.SUPPRESS, action='store_true') @with_argparser(flag_parser) @@ -104,8 +106,6 @@ class AutoCompleteTester(cmd2.Cmd): # Uses non-default flag prefix value (+) plus_flag_parser = Cmd2ArgParser(prefix_chars='+') plus_flag_parser.add_argument('+n', '++normal_flag', help='A normal flag', action='store_true') - plus_flag_parser.add_argument('+o', '++other_normal_flag', help='The other normal flag', action='store_true') - plus_flag_parser.add_argument('+s', '++suppressed_flag', help=argparse.SUPPRESS, action='store_true') @with_argparser(plus_flag_parser) def do_plus_flag(self, args: argparse.Namespace) -> None: @@ -227,31 +227,31 @@ def test_complete_help(ac_app, command, text, completions): @pytest.mark.parametrize('command_and_args, text, completions', [ - # Default flag prefix character (-) - ('flag', '-', ['--help', '--normal_flag', '--other_normal_flag', '-h', '-n', '-o']), - ('flag', '--', ['--help', '--normal_flag', '--other_normal_flag']), + # Complete all flags (suppressed will not show) + ('flag', '-', ['--append_const_flag', '--append_flag', '--count_flag', '--help', + '--normal_flag', '-a', '-c', '-h', '-n', '-o']), + ('flag', '--', ['--append_const_flag', '--append_flag', '--count_flag', '--help', '--normal_flag']), + + # Complete individual flag ('flag', '-n', ['-n ']), ('flag', '--n', ['--normal_flag ']), + + # Suppressed flag should not complete ('flag', '-s', []), ('flag', '--s', []), - ('flag -h', '-', ['--normal_flag', '--other_normal_flag', '-n', '-o']), - ('flag -h --normal_flag', '-', ['--other_normal_flag', '-o']), - ('flag -h --normal_flag', '--', ['--other_normal_flag ']), - ('flag -h --normal_flag -o', '-', []), + + # A used flag should not show in completions + ('flag -n', '--', ['--append_const_flag', '--append_flag', '--count_flag', '--help']), + + # 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']), # Non-default flag prefix character (+) - ('plus_flag', '+', ['++help', '++normal_flag', '++other_normal_flag', '+h', '+n', '+o']), - ('plus_flag', '++', ['++help', '++normal_flag', '++other_normal_flag']), - ('plus_flag', '+n', ['+n ']), - ('plus_flag', '++n', ['++normal_flag ']), - ('plus_flag', '+s', []), - ('plus_flag', '++s', []), - ('plus_flag +h', '+', ['++normal_flag', '++other_normal_flag', '+n', '+o']), - ('plus_flag +h ++normal_flag', '+', ['++other_normal_flag', '+o']), - ('plus_flag +h ++normal_flag', '++', ['++other_normal_flag ']), - ('plus_flag +h ++normal_flag +o', '+', []), - - # Flag completion should not occur after '--' since that tells argparse the all remaining arguments of non-flags + ('plus_flag', '+', ['++help', '++normal_flag', '+h', '+n']), + ('plus_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 --', '++', []), -- cgit v1.2.1 From 9e49b7eb70b715f8adc06748a73caa5a0fafd065 Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Mon, 8 Jul 2019 16:16:32 -0400 Subject: Resetting consumed arguments list each time a flag is used Started adding AutoCompleter unit tests for nargs. --- tests/test_argparse_completer.py | 95 +++++++++++++++++++++++++++++++++------- 1 file changed, 79 insertions(+), 16 deletions(-) (limited to 'tests') diff --git a/tests/test_argparse_completer.py b/tests/test_argparse_completer.py index b70ab29d..8d939e05 100644 --- a/tests/test_argparse_completer.py +++ b/tests/test_argparse_completer.py @@ -177,6 +177,21 @@ class AutoCompleteTester(cmd2.Cmd): def do_completer(self, args: argparse.Namespace) -> None: pass + ############################################################################################################ + # Begin code related to nargs + ############################################################################################################ + nargs_parser = Cmd2ArgParser() + + # Flag args for nargs command + nargs_parser.add_argument("--set_value", help="a flag with a set value for nargs", nargs=2, + choices=static_choices_list) + nargs_parser.add_argument("--one_or_more", help="a flag with nargs", nargs=argparse.ONE_OR_MORE, + choices=static_choices_list) + + @with_argparser(nargs_parser) + def do_nargs(self, args: argparse.Namespace) -> None: + pass + ############################################################################################################ # Begin code related to testing tab hints ############################################################################################################ @@ -200,6 +215,39 @@ def ac_app(): return app +@pytest.mark.parametrize('args, completions', [ + # nargs = 2 + ('--set_value', static_choices_list), + ('--set_value static', ['choices', 'stop', 'here']), + ('--set_value static choices', []), + + # Using the flag again will reset the choices available + ('--set_value static choices --set_value', static_choices_list), + + # nargs = ONE_OR_MORE + ('--one_or_more', static_choices_list), + ('--one_or_more static', ['choices', 'stop', 'here']), + ('--one_or_more static choices', ['stop', 'here']), + + # No more flags after a double dash + ('-- --one_or_more static choices', []), + +]) +def test_autcomp_nargs(ac_app, args, completions): + text = '' + line = 'nargs {} {}'.format(args, text) + endidx = len(line) + begidx = endidx - len(text) + + first_match = complete_tester(text, line, begidx, endidx, ac_app) + if completions: + assert first_match is not None + else: + assert first_match is None + + assert ac_app.completion_matches == sorted(completions, key=ac_app.matches_sort_key) + + @pytest.mark.parametrize('command', [ 'music', 'music create', @@ -223,7 +271,12 @@ def test_complete_help(ac_app, command, text, completions): begidx = endidx - len(text) first_match = complete_tester(text, line, begidx, endidx, ac_app) - assert first_match is not None and ac_app.completion_matches == completions + if completions: + assert first_match is not None + else: + assert first_match is None + + assert ac_app.completion_matches == sorted(completions, key=ac_app.matches_sort_key) @pytest.mark.parametrize('command_and_args, text, completions', [ @@ -268,7 +321,7 @@ def test_autcomp_flag_completion(ac_app, command_and_args, text, completions): else: assert first_match is None - assert ac_app.completion_matches == completions + assert ac_app.completion_matches == sorted(completions, key=ac_app.matches_sort_key) @pytest.mark.parametrize('flag, text, completions', [ @@ -287,7 +340,12 @@ def test_autocomp_flag_choices_completion(ac_app, flag, text, completions): begidx = endidx - len(text) first_match = complete_tester(text, line, begidx, endidx, ac_app) - assert first_match is not None and ac_app.completion_matches == sorted(completions, key=ac_app.matches_sort_key) + if completions: + assert first_match is not None + else: + assert first_match is None + + assert ac_app.completion_matches == sorted(completions, key=ac_app.matches_sort_key) @pytest.mark.parametrize('pos, text, completions', [ @@ -305,7 +363,12 @@ def test_autocomp_positional_choices_completion(ac_app, pos, text, completions): begidx = endidx - len(text) first_match = complete_tester(text, line, begidx, endidx, ac_app) - assert first_match is not None and ac_app.completion_matches == sorted(completions, key=ac_app.matches_sort_key) + if completions: + assert first_match is not None + else: + assert first_match is None + + assert ac_app.completion_matches == sorted(completions, key=ac_app.matches_sort_key) @pytest.mark.parametrize('flag, text, completions', [ @@ -320,7 +383,12 @@ def test_autocomp_flag_completers(ac_app, flag, text, completions): begidx = endidx - len(text) first_match = complete_tester(text, line, begidx, endidx, ac_app) - assert first_match is not None and ac_app.completion_matches == sorted(completions, key=ac_app.matches_sort_key) + if completions: + assert first_match is not None + else: + assert first_match is None + + assert ac_app.completion_matches == sorted(completions, key=ac_app.matches_sort_key) @pytest.mark.parametrize('pos, text, completions', [ @@ -336,7 +404,12 @@ def test_autocomp_positional_completers(ac_app, pos, text, completions): begidx = endidx - len(text) first_match = complete_tester(text, line, begidx, endidx, ac_app) - assert first_match is not None and ac_app.completion_matches == sorted(completions, key=ac_app.matches_sort_key) + if completions: + assert first_match is not None + else: + assert first_match is None + + assert ac_app.completion_matches == sorted(completions, key=ac_app.matches_sort_key) @pytest.mark.parametrize('num_aliases, show_description', [ @@ -532,16 +605,6 @@ Hint: # cmd2_app.completion_matches == ['John Boyega" '] # # -# def test_autocomp_custom_func_dict_arg(cmd2_app): -# text = '/home/user/' -# line = 'video movies load {}'.format(text) -# endidx = len(line) -# begidx = endidx - len(text) -# -# first_match = complete_tester(text, line, begidx, endidx, cmd2_app) -# assert first_match is not None and \ -# cmd2_app.completion_matches == ['/home/user/another.db', '/home/user/file space.db', '/home/user/file.db'] -# # # def test_argparse_remainder_flag_completion(cmd2_app): # import cmd2 -- cgit v1.2.1 From b188531a0c7277b7e3d70d6c096e00f5653415c3 Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Wed, 10 Jul 2019 09:05:40 -0400 Subject: Added some tests for REMAINDER flags --- tests/test_argparse_completer.py | 37 +++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) (limited to 'tests') diff --git a/tests/test_argparse_completer.py b/tests/test_argparse_completer.py index 8d939e05..6297dfb3 100644 --- a/tests/test_argparse_completer.py +++ b/tests/test_argparse_completer.py @@ -98,6 +98,7 @@ class AutoCompleteTester(cmd2.Cmd): flag_parser.add_argument('-o', '--append_const_flag', help='Append const flag', action='append_const', const=True) flag_parser.add_argument('-c', '--count_flag', help='Count flag', action='count') flag_parser.add_argument('-s', '--suppressed_flag', help=argparse.SUPPRESS, action='store_true') + flag_parser.add_argument('-r', '--remainder_flag', nargs=argparse.REMAINDER, help='a remainder flag') @with_argparser(flag_parser) def do_flag(self, args: argparse.Namespace) -> None: @@ -185,7 +186,9 @@ class AutoCompleteTester(cmd2.Cmd): # Flag args for nargs command nargs_parser.add_argument("--set_value", help="a flag with a set value for nargs", nargs=2, choices=static_choices_list) - nargs_parser.add_argument("--one_or_more", help="a flag with nargs", nargs=argparse.ONE_OR_MORE, + nargs_parser.add_argument("--one_or_more", help="a flag wanting one or more args", nargs=argparse.ONE_OR_MORE, + choices=static_choices_list) + nargs_parser.add_argument("--remainder", help="a flag wanting remaining", nargs=argparse.REMAINDER, choices=static_choices_list) @with_argparser(nargs_parser) @@ -216,7 +219,7 @@ def ac_app(): @pytest.mark.parametrize('args, completions', [ - # nargs = 2 + # Flag with nargs = 2 ('--set_value', static_choices_list), ('--set_value static', ['choices', 'stop', 'here']), ('--set_value static choices', []), @@ -224,7 +227,7 @@ def ac_app(): # Using the flag again will reset the choices available ('--set_value static choices --set_value', static_choices_list), - # nargs = ONE_OR_MORE + # Flag with nargs = ONE_OR_MORE ('--one_or_more', static_choices_list), ('--one_or_more static', ['choices', 'stop', 'here']), ('--one_or_more static choices', ['stop', 'here']), @@ -232,6 +235,12 @@ def ac_app(): # No more flags after a double dash ('-- --one_or_more static choices', []), + # Flag with nargs = REMAINDER + ('--remainder', static_choices_list), + ('--remainder static ', ['choices', 'stop', 'here']), + + # No more flags can appear after a REMAINDER flag) + ('--remainder static --set_value', ['choices', 'stop', 'here']) ]) def test_autcomp_nargs(ac_app, args, completions): text = '' @@ -281,24 +290,36 @@ def test_complete_help(ac_app, command, text, completions): @pytest.mark.parametrize('command_and_args, text, completions', [ # Complete all flags (suppressed will not show) - ('flag', '-', ['--append_const_flag', '--append_flag', '--count_flag', '--help', - '--normal_flag', '-a', '-c', '-h', '-n', '-o']), - ('flag', '--', ['--append_const_flag', '--append_flag', '--count_flag', '--help', '--normal_flag']), + ('flag', '-', ['--append_const_flag', '--append_flag', '--count_flag', '--help', '--normal_flag', + '--remainder_flag', '-a', '-c', '-h', '-n', '-o', '-r']), + ('flag', '--', ['--append_const_flag', '--append_flag', '--count_flag', '--help', + '--normal_flag', '--remainder_flag']), # Complete individual flag ('flag', '-n', ['-n ']), ('flag', '--n', ['--normal_flag ']), + # No flags should complete until current flag has its args + ('flag --append_flag', '-', []), + + # Complete REMAINDER flag name + ('flag', '-r', ['-r ']), + ('flag', '--r', ['--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']), + ('flag -n', '--', ['--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']), + '--help', '--normal_flag', '--remainder_flag']), # Non-default flag prefix character (+) ('plus_flag', '+', ['++help', '++normal_flag', '+h', '+n']), -- cgit v1.2.1 From 2ebf63617990d1ee0ebe08f7c3938234c7c5fd24 Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Wed, 10 Jul 2019 14:49:04 -0400 Subject: Fixed bug where -- wasn't stopping a REMAINDER flag and did a lot of refactoring --- tests/test_argparse_completer.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'tests') diff --git a/tests/test_argparse_completer.py b/tests/test_argparse_completer.py index 6297dfb3..404ba10b 100644 --- a/tests/test_argparse_completer.py +++ b/tests/test_argparse_completer.py @@ -240,7 +240,10 @@ def ac_app(): ('--remainder static ', ['choices', 'stop', 'here']), # No more flags can appear after a REMAINDER flag) - ('--remainder static --set_value', ['choices', 'stop', 'here']) + ('--remainder static --set_value', ['choices', 'stop', 'here']), + + # Double dash ends a remainder flag + ('--remainder static --', []) ]) def test_autcomp_nargs(ac_app, args, completions): text = '' @@ -671,14 +674,15 @@ def test_is_potential_flag(): from cmd2.argparse_completer import is_potential_flag parser = Cmd2ArgParser() - # Not valid flags + # Not potential flags assert not is_potential_flag('', parser) assert not is_potential_flag('non-flag', parser) - assert not is_potential_flag('-', parser) assert not is_potential_flag('--has space', parser) assert not is_potential_flag('-2', parser) - # Valid flags + # Potential flags + assert is_potential_flag('-', parser) + assert is_potential_flag('--', parser) assert is_potential_flag('-flag', parser) assert is_potential_flag('--flag', parser) -- cgit v1.2.1 From 782bab855b0ec1d1b9728a322b932f99e6fb3849 Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Wed, 10 Jul 2019 16:22:47 -0400 Subject: Fixed some double-dash handling logic added unit tests --- tests/test_argparse_completer.py | 102 ++++++++++++++++++++++----------------- 1 file changed, 57 insertions(+), 45 deletions(-) (limited to 'tests') diff --git a/tests/test_argparse_completer.py b/tests/test_argparse_completer.py index 404ba10b..cf4ac7b3 100644 --- a/tests/test_argparse_completer.py +++ b/tests/test_argparse_completer.py @@ -19,6 +19,11 @@ static_choices_list = ['static', 'choices', 'stop', 'here'] choices_from_function = ['choices', 'function', 'chatty', 'smith'] choices_from_method = ['choices', 'method', 'most', 'improved'] +set_value_choices = ['set', 'value', 'choices'] +one_or_more_choices = ['one', 'or', 'more', 'choices'] +optional_choices = ['optional', 'choices'] +remainder_choices = ['remainder', 'choices'] + completions_from_function = ['completions', 'function', 'fairly', 'complete'] completions_from_method = ['completions', 'method', 'missed', 'spot'] @@ -185,11 +190,13 @@ class AutoCompleteTester(cmd2.Cmd): # Flag args for nargs command nargs_parser.add_argument("--set_value", help="a flag with a set value for nargs", nargs=2, - choices=static_choices_list) + 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=static_choices_list) + 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("--remainder", help="a flag wanting remaining", nargs=argparse.REMAINDER, - choices=static_choices_list) + choices=remainder_choices) @with_argparser(nargs_parser) def do_nargs(self, args: argparse.Namespace) -> None: @@ -218,48 +225,6 @@ def ac_app(): return app -@pytest.mark.parametrize('args, completions', [ - # Flag with nargs = 2 - ('--set_value', static_choices_list), - ('--set_value static', ['choices', 'stop', 'here']), - ('--set_value static choices', []), - - # Using the flag again will reset the choices available - ('--set_value static choices --set_value', static_choices_list), - - # Flag with nargs = ONE_OR_MORE - ('--one_or_more', static_choices_list), - ('--one_or_more static', ['choices', 'stop', 'here']), - ('--one_or_more static choices', ['stop', 'here']), - - # No more flags after a double dash - ('-- --one_or_more static choices', []), - - # Flag with nargs = REMAINDER - ('--remainder', static_choices_list), - ('--remainder static ', ['choices', 'stop', 'here']), - - # No more flags can appear after a REMAINDER flag) - ('--remainder static --set_value', ['choices', 'stop', 'here']), - - # Double dash ends a remainder flag - ('--remainder static --', []) -]) -def test_autcomp_nargs(ac_app, args, completions): - text = '' - line = 'nargs {} {}'.format(args, text) - endidx = len(line) - begidx = endidx - len(text) - - first_match = complete_tester(text, line, begidx, endidx, ac_app) - if completions: - assert first_match is not None - else: - assert first_match is None - - assert ac_app.completion_matches == sorted(completions, key=ac_app.matches_sort_key) - - @pytest.mark.parametrize('command', [ 'music', 'music create', @@ -465,6 +430,53 @@ 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']), + ('--set_value set value choices', []), + + # Another flag can't start until all expected args are filled out + ('--set_value --one_or_more', set_value_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 = 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 (even if all expected args aren't entered) + ('--set_value --', []), + + # Double dash ends a REMAINDER flag + ('--remainder remainder --', []), + + # No more flags after a double dash + ('-- --one_or_more ', []), +]) +def test_autcomp_nargs(ac_app, args, completions): + text = '' + line = 'nargs {} {}'.format(args, text) + endidx = len(line) + begidx = endidx - len(text) + + first_match = complete_tester(text, line, begidx, endidx, ac_app) + if completions: + assert first_match is not None + else: + assert first_match is None + + assert ac_app.completion_matches == sorted(completions, key=ac_app.matches_sort_key) + + def test_completion_items_default_header(ac_app): from cmd2.argparse_completer import DEFAULT_DESCRIPTIVE_HEADER -- cgit v1.2.1 From 2ef1f27bdd599a47f34f186fddb3b5550352e04b Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Wed, 10 Jul 2019 16:46:36 -0400 Subject: Added unit tests --- tests/test_argparse_completer.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/test_argparse_completer.py b/tests/test_argparse_completer.py index cf4ac7b3..5c6b750f 100644 --- a/tests/test_argparse_completer.py +++ b/tests/test_argparse_completer.py @@ -21,7 +21,8 @@ choices_from_method = ['choices', 'method', 'most', 'improved'] set_value_choices = ['set', 'value', 'choices'] one_or_more_choices = ['one', 'or', 'more', 'choices'] -optional_choices = ['optional', 'choices'] +optional_choices = ['a', 'few', 'optional', 'choices'] +range_choices = ['some', 'range', 'choices'] remainder_choices = ['remainder', 'choices'] completions_from_function = ['completions', 'function', 'fairly', 'complete'] @@ -195,6 +196,8 @@ class AutoCompleteTester(cmd2.Cmd): 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("--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) @@ -446,6 +449,19 @@ def test_completion_items(ac_app, num_aliases, show_description): ('--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 to completions are now empty + ('--optional optional', []), + + # Flag with nargs range (1, 2) + ('--range', range_choices), + ('--range some', ['range', 'choices']), + + # Already used 2 args so no more completions + ('--range some range', []), + # Flag with nargs = REMAINDER ('--remainder', remainder_choices), ('--remainder remainder ', ['choices ']), -- cgit v1.2.1 From 50e143c34147ada3f693cdf1b11e4e88fedb3104 Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Wed, 10 Jul 2019 18:57:14 -0400 Subject: Added unit tests for argparse_custom.py --- tests/test_argparse_custom.py | 108 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 101 insertions(+), 7 deletions(-) (limited to 'tests') diff --git a/tests/test_argparse_custom.py b/tests/test_argparse_custom.py index 85587d49..35d97974 100644 --- a/tests/test_argparse_custom.py +++ b/tests/test_argparse_custom.py @@ -2,50 +2,144 @@ """ Unit/functional testing for argparse customizations in cmd2 """ +import argparse + import pytest + +import cmd2 from cmd2.argparse_custom import Cmd2ArgParser +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) + + range_parser = Cmd2ArgParser() + range_parser.add_argument('--arg1', nargs=(2, 3)) + range_parser.add_argument('--arg2', nargs=argparse.ZERO_OR_MORE) + range_parser.add_argument('--arg3', nargs=argparse.ONE_OR_MORE) + + @cmd2.with_argparser(range_parser) + def do_range(self, _): + pass + + +@pytest.fixture +def cust_app(): + return ApCustomTestApp() + + +def fake_func(): + pass + + +@pytest.mark.parametrize('args, is_valid', [ + ({'choices': []}, True), + ({'choices_function': fake_func}, True), + ({'choices_method': fake_func}, True), + ({'completer_function': fake_func}, True), + ({'completer_method': fake_func}, True), + ({'choices': [], 'choices_function': fake_func}, False), + ({'choices': [], 'choices_method': fake_func}, False), + ({'choices_method': fake_func, 'completer_function': fake_func}, False), + ({'choices_method': fake_func, 'completer_method': fake_func}, False), +]) +def test_apcustom_invalid_args(args, is_valid): + parser = Cmd2ArgParser(prog='test') + try: + parser.add_argument('name', **args) + assert is_valid + except ValueError as ex: + assert not is_valid + assert 'Only one of the following may be used' in str(ex) + + +def test_apcustom_usage(): + usage = "A custom usage statement" + parser = Cmd2ArgParser(usage=usage) + help = parser.format_help() + assert usage in help -def test_acarg_narg_empty_tuple(): +def test_apcustom_nargs_help_format(cust_app): + out, err = run_cmd(cust_app, 'help range') + assert 'Usage: range [-h] [--arg1 ARG1{2..3}] [--arg2 [ARG2 [...]]]' in out[0] + assert ' [--arg3 ARG3 [...]]' in out[1] + + +def test_apcustom_nargs_not_enough(cust_app): + out, err = run_cmd(cust_app, 'range --arg1 one') + assert 'Error: argument --arg1: Expected between 2 and 3 arguments' in err[2] + + +def test_apcustom_narg_empty_tuple(): with pytest.raises(ValueError) as excinfo: parser = Cmd2ArgParser(prog='test') parser.add_argument('invalid_tuple', nargs=()) assert 'Ranged values for nargs must be a tuple of 2 integers' in str(excinfo.value) -def test_acarg_narg_single_tuple(): +def test_apcustom_narg_single_tuple(): with pytest.raises(ValueError) as excinfo: parser = Cmd2ArgParser(prog='test') parser.add_argument('invalid_tuple', nargs=(1,)) assert 'Ranged values for nargs must be a tuple of 2 integers' in str(excinfo.value) -def test_acarg_narg_tuple_triple(): +def test_apcustom_narg_tuple_triple(): with pytest.raises(ValueError) as excinfo: parser = Cmd2ArgParser(prog='test') parser.add_argument('invalid_tuple', nargs=(1, 2, 3)) assert 'Ranged values for nargs must be a tuple of 2 integers' in str(excinfo.value) -def test_acarg_narg_tuple_order(): +def test_apcustom_narg_tuple_order(): with pytest.raises(ValueError) as excinfo: parser = Cmd2ArgParser(prog='test') parser.add_argument('invalid_tuple', nargs=(2, 1)) assert 'Invalid nargs range. The first value must be less than the second' in str(excinfo.value) -def test_acarg_narg_tuple_negative(): +def test_apcustom_narg_tuple_negative(): with pytest.raises(ValueError) as excinfo: parser = Cmd2ArgParser(prog='test') parser.add_argument('invalid_tuple', nargs=(-1, 1)) assert 'Negative numbers are invalid for nargs range' in str(excinfo.value) -def test_acarg_narg_tuple_zero_base(): +def test_apcustom_narg_tuple_zero_base(): parser = Cmd2ArgParser(prog='test') parser.add_argument('tuple', nargs=(0, 3)) -def test_acarg_narg_tuple_zero_to_one(): +def test_apcustom_narg_tuple_zero_to_one(): parser = Cmd2ArgParser(prog='test') parser.add_argument('tuple', nargs=(0, 1)) + + +def test_apcustom_print_message(capsys): + import sys + test_message = 'The test message' + + # Specify the file + parser = Cmd2ArgParser(prog='test') + parser._print_message(test_message, file=sys.stdout) + out, err = capsys.readouterr() + assert test_message in out + + # Make sure file defaults to sys.stderr + parser = Cmd2ArgParser(prog='test') + parser._print_message(test_message) + out, err = capsys.readouterr() + assert test_message in err + + +def test_apcustom_required_options(): + # Make sure a 'required arguments' section shows when a flag is marked required + parser = Cmd2ArgParser(prog='test') + parser.add_argument('--required_flag', required=True) + help = parser.format_help() + + assert 'required arguments' in help -- cgit v1.2.1 From bc80c994abece4ac1a0540beecd93624d96514b7 Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Wed, 10 Jul 2019 18:58:28 -0400 Subject: Small refactoring --- tests/test_argparse_completer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/tests/test_argparse_completer.py b/tests/test_argparse_completer.py index 5c6b750f..7a7559f4 100644 --- a/tests/test_argparse_completer.py +++ b/tests/test_argparse_completer.py @@ -42,8 +42,8 @@ def completer_function(text: str, line: str, begidx: int, endidx: int) -> List[s # noinspection PyMethodMayBeStatic,PyUnusedLocal class AutoCompleteTester(cmd2.Cmd): """Cmd2 app that exercises AutoCompleter class""" - def __init__(self): - super().__init__() + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) ############################################################################################################ # Begin code related to help and command name completion -- cgit v1.2.1 From ca89266546d93b993cc3e48935b62de08332c3a0 Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Wed, 10 Jul 2019 19:29:45 -0400 Subject: More unit tests --- tests/test_argparse_completer.py | 159 +++++++++------------------------------ 1 file changed, 36 insertions(+), 123 deletions(-) (limited to 'tests') diff --git a/tests/test_argparse_completer.py b/tests/test_argparse_completer.py index 7a7559f4..f1faa66a 100644 --- a/tests/test_argparse_completer.py +++ b/tests/test_argparse_completer.py @@ -25,6 +25,8 @@ optional_choices = ['a', 'few', 'optional', 'choices'] range_choices = ['some', 'range', 'choices'] remainder_choices = ['remainder', 'choices'] +positional_choices = ['the', 'positional', 'choices'] + completions_from_function = ['completions', 'function', 'fairly', 'complete'] completions_from_method = ['completions', 'method', 'missed', 'spot'] @@ -201,6 +203,11 @@ class AutoCompleteTester(cmd2.Cmd): 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) + @with_argparser(nargs_parser) def do_nargs(self, args: argparse.Namespace) -> None: pass @@ -437,7 +444,9 @@ def test_completion_items(ac_app, num_aliases, show_description): # Flag with nargs = 2 ('--set_value', set_value_choices), ('--set_value set', ['value', 'choices']), - ('--set_value set value choices', []), + + # Both args are filled. At positional arg now. + ('--set_value set value', positional_choices), # Another flag can't start until all expected args are filled out ('--set_value --one_or_more', set_value_choices), @@ -452,15 +461,15 @@ def test_completion_items(ac_app, num_aliases, show_description): # Flag with nargs = OPTIONAL ('--optional', optional_choices), - # Only one arg allowed for an OPTIONAL to completions are now empty - ('--optional optional', []), + # 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 no more completions - ('--range some range', []), + # Already used 2 args so at positional + ('--range some range', positional_choices), # Flag with nargs = REMAINDER ('--remainder', remainder_choices), @@ -470,13 +479,32 @@ def test_completion_items(ac_app, num_aliases, show_description): ('--remainder choices --set_value', ['remainder ']), # Double dash ends the current flag (even if all expected args aren't entered) - ('--set_value --', []), + ('--set_value --', positional_choices), # Double dash ends a REMAINDER flag - ('--remainder remainder --', []), + ('--remainder remainder --', positional_choices), # No more flags after a double dash - ('-- --one_or_more ', []), + ('-- --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', ['value', 'choices']), + + # Intermixed flag and positional with flag finishing + ('positional --set_value set value', ['the', 'choices']), + ('positional --set_value set --', ['the', 'choices']), + + # REMAINDER positional + ('the positional', remainder_choices), + ('the positional remainder', ['choices ']), + + # REMAINDER positional. Flags don't work in REMAINDER + ('the positional --set_value', remainder_choices), ]) def test_autcomp_nargs(ac_app, args, completions): text = '' @@ -583,121 +611,6 @@ Hint: ''' -# def test_autcomp_hint_in_narg_range(cmd2_app, capsys): -# text = '' -# line = 'suggest -d 2 {}'.format(text) -# endidx = len(line) -# begidx = endidx - len(text) -# -# first_match = complete_tester(text, line, begidx, endidx, cmd2_app) -# out, err = capsys.readouterr() -# -# assert out == ''' -# Hint: -# -d, --duration DURATION Duration constraint in minutes. -# single value - maximum duration -# [a, b] - duration range -# -# ''' -# -# def test_autocomp_flags_narg_max(cmd2_app): -# text = '' -# line = 'suggest d 2 3 {}'.format(text) -# endidx = len(line) -# begidx = endidx - len(text) -# -# first_match = complete_tester(text, line, begidx, endidx, cmd2_app) -# assert first_match is None -# -# -# def test_autcomp_narg_beyond_max(cmd2_app): -# out, err = run_cmd(cmd2_app, 'suggest -t movie -d 3 4 5') -# assert 'Error: unrecognized arguments: 5' in err[1] -# -# -# def test_autocomp_subcmd_flag_comp_func_attr(cmd2_app): -# text = 'A' -# line = 'video movies list -a "{}'.format(text) -# endidx = len(line) -# begidx = endidx - len(text) -# -# first_match = complete_tester(text, line, begidx, endidx, cmd2_app) -# assert first_match is not None and \ -# cmd2_app.completion_matches == ['Adam Driver', 'Alec Guinness', 'Andy Serkis', 'Anthony Daniels'] -# -# -# def test_autocomp_subcmd_flag_comp_list_attr(cmd2_app): -# text = 'G' -# line = 'video movies list -d {}'.format(text) -# endidx = len(line) -# begidx = endidx - len(text) -# -# first_match = complete_tester(text, line, begidx, endidx, cmd2_app) -# assert first_match is not None and first_match == '"Gareth Edwards' -# -# -# def test_autocomp_pos_consumed(cmd2_app): -# text = '' -# line = 'library movie add SW_EP01 {}'.format(text) -# endidx = len(line) -# begidx = endidx - len(text) -# -# first_match = complete_tester(text, line, begidx, endidx, cmd2_app) -# assert first_match is None -# -# -# def test_autocomp_pos_after_flag(cmd2_app): -# text = 'Joh' -# line = 'video movies add -d "George Lucas" -- "Han Solo" PG "Emilia Clarke" "{}'.format(text) -# endidx = len(line) -# begidx = endidx - len(text) -# -# first_match = complete_tester(text, line, begidx, endidx, cmd2_app) -# assert first_match is not None and \ -# cmd2_app.completion_matches == ['John Boyega" '] -# -# -# -# def test_argparse_remainder_flag_completion(cmd2_app): -# import cmd2 -# import argparse -# -# # Test flag completion as first arg of positional with nargs=argparse.REMAINDER -# text = '--h' -# line = 'help command {}'.format(text) -# endidx = len(line) -# begidx = endidx - len(text) -# -# # --h should not complete into --help because we are in the argparse.REMAINDER section -# assert complete_tester(text, line, begidx, endidx, cmd2_app) is None -# -# # Test flag completion within an already started positional with nargs=argparse.REMAINDER -# text = '--h' -# line = 'help command subcommand {}'.format(text) -# endidx = len(line) -# begidx = endidx - len(text) -# -# # --h should not complete into --help because we are in the argparse.REMAINDER section -# assert complete_tester(text, line, begidx, endidx, cmd2_app) is None -# -# # Test a flag with nargs=argparse.REMAINDER -# parser = argparse.ArgumentParser() -# parser.add_argument('-f', nargs=argparse.REMAINDER) -# -# # Overwrite eof's parser for this test -# cmd2.Cmd.do_eof.argparser = parser -# -# text = '--h' -# line = 'eof -f {}'.format(text) -# endidx = len(line) -# begidx = endidx - len(text) -# -# # --h should not complete into --help because we are in the argparse.REMAINDER section -# assert complete_tester(text, line, begidx, endidx, cmd2_app) is None -# -# - - def test_is_potential_flag(): from cmd2.argparse_completer import is_potential_flag parser = Cmd2ArgParser() -- cgit v1.2.1 From a28896f40177d66a717250cb890c5ac82eba179d Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Thu, 11 Jul 2019 16:14:47 -0400 Subject: Removed unused code and add unit test --- tests/test_argparse_completer.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'tests') diff --git a/tests/test_argparse_completer.py b/tests/test_argparse_completer.py index f1faa66a..fa987503 100644 --- a/tests/test_argparse_completer.py +++ b/tests/test_argparse_completer.py @@ -411,6 +411,35 @@ def test_autocomp_positional_completers(ac_app, pos, text, completions): assert ac_app.completion_matches == sorted(completions, key=ac_app.matches_sort_key) +def test_autocomp_blank_token(ac_app): + """Force a blank token to make sure AutoCompleter consumes them like argparse does""" + from cmd2.argparse_completer import AutoCompleter + + blank = '' + + # Blank flag arg + text = '' + line = 'completer -m {} {}'.format(blank, text) + endidx = len(line) + begidx = endidx - len(text) + + completer = AutoCompleter(ac_app.completer_parser, ac_app) + tokens = ['completer', '-f', blank, text] + completions = completer.complete_command(tokens, text, line, begidx, endidx) + assert completions == completions_from_function + + # Blank positional arg + text = '' + line = 'completer {} {}'.format(blank, text) + endidx = len(line) + begidx = endidx - len(text) + + completer = AutoCompleter(ac_app.completer_parser, ac_app) + tokens = ['completer', blank, text] + completions = completer.complete_command(tokens, text, line, begidx, endidx) + 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, @@ -611,6 +640,7 @@ Hint: ''' + def test_is_potential_flag(): from cmd2.argparse_completer import is_potential_flag parser = Cmd2ArgParser() -- cgit v1.2.1 From 73fad36d62671aa257d80affd5099ac9b46b3e03 Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Thu, 11 Jul 2019 23:26:47 -0400 Subject: Refactored much of AutoCompleter.complete_command. Fixed issue where negative numbers did not tab complete. AutoCompleter now prints an error if flags are left unfinished before moving to next argument. --- tests/test_argparse_completer.py | 53 +++++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 22 deletions(-) (limited to 'tests') diff --git a/tests/test_argparse_completer.py b/tests/test_argparse_completer.py index fa987503..2a30fc3b 100644 --- a/tests/test_argparse_completer.py +++ b/tests/test_argparse_completer.py @@ -14,7 +14,7 @@ from cmd2.utils import StdSim, basic_complete from .conftest import run_cmd, complete_tester # Lists used in our tests -static_int_choices_list = [1, 2, 3, 4, 5] +static_int_choices_list = [-12, -1, -2, 0, 1, 2] static_choices_list = ['static', 'choices', 'stop', 'here'] choices_from_function = ['choices', 'function', 'chatty', 'smith'] choices_from_method = ['choices', 'method', 'most', 'improved'] @@ -331,7 +331,9 @@ def test_autcomp_flag_completion(ac_app, command_and_args, text, completions): ('-m', '', choices_from_method), ('--method', 'm', ['method', 'most']), ('-i', '', [str(i) for i in static_int_choices_list]), - ('--int', '1', ['1 ']) + ('--int', '1', ['1 ']), + ('--int', '-', ['-12', '-1', '-2']), + ('--int', '-1', ['-12', '-1']) ]) def test_autocomp_flag_choices_completion(ac_app, flag, text, completions): line = 'choices {} {}'.format(flag, text) @@ -477,9 +479,6 @@ def test_completion_items(ac_app, num_aliases, show_description): # Both args are filled. At positional arg now. ('--set_value set value', positional_choices), - # Another flag can't start until all expected args are filled out - ('--set_value --one_or_more', set_value_choices), - # Using the flag again will reset the choices available ('--set_value set value --set_value', set_value_choices), @@ -507,8 +506,8 @@ def test_completion_items(ac_app, num_aliases, show_description): # No more flags can appear after a REMAINDER flag) ('--remainder choices --set_value', ['remainder ']), - # Double dash ends the current flag (even if all expected args aren't entered) - ('--set_value --', positional_choices), + # Double dash ends the current flag + ('--range choice --', positional_choices), # Double dash ends a REMAINDER flag ('--remainder remainder --', positional_choices), @@ -522,11 +521,11 @@ def test_completion_items(ac_app, num_aliases, show_description): # Intermixed flag and positional ('positional --set_value', set_value_choices), - ('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 --set_value set --', ['the', 'choices']), + ('positional --range choice --', ['the', 'choices']), # REMAINDER positional ('the positional', remainder_choices), @@ -550,6 +549,16 @@ def test_autcomp_nargs(ac_app, args, completions): assert ac_app.completion_matches == sorted(completions, key=ac_app.matches_sort_key) +""" +TODO: Add unit tests for unfinished flag errors + # Double dash ends the current flag (even if all expected args aren't entered) + ('--set_value --', positional_choices), + + # Another flag can't start until all expected args are filled out + ('--set_value --one_or_more', set_value_choices), +""" + + def test_completion_items_default_header(ac_app): from cmd2.argparse_completer import DEFAULT_DESCRIPTIVE_HEADER @@ -641,21 +650,21 @@ Hint: ''' -def test_is_potential_flag(): - from cmd2.argparse_completer import is_potential_flag +def test_starts_like_flag(): + from cmd2.argparse_completer import starts_like_flag parser = Cmd2ArgParser() - # Not potential flags - assert not is_potential_flag('', parser) - assert not is_potential_flag('non-flag', parser) - assert not is_potential_flag('--has space', parser) - assert not is_potential_flag('-2', parser) - - # Potential flags - assert is_potential_flag('-', parser) - assert is_potential_flag('--', parser) - assert is_potential_flag('-flag', parser) - assert is_potential_flag('--flag', parser) + # Does not start like a flag + assert not starts_like_flag('', parser) + assert not starts_like_flag('non-flag', parser) + assert not starts_like_flag('-', parser) + assert not starts_like_flag('--has space', parser) + assert not starts_like_flag('-2', parser) + + # Does start like a flag + assert starts_like_flag('--', parser) + assert starts_like_flag('-flag', parser) + assert starts_like_flag('--flag', parser) def test_complete_command_no_tokens(ac_app): -- cgit v1.2.1 From 9bb6b84608b6262d228c021c7115e1389eed33e3 Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Fri, 12 Jul 2019 12:42:04 -0400 Subject: Renamed Cmd2ArgParser to ArgParser --- tests/test_argparse_completer.py | 22 +++++++++++----------- tests/test_argparse_custom.py | 27 +++++++++++++-------------- 2 files changed, 24 insertions(+), 25 deletions(-) (limited to 'tests') diff --git a/tests/test_argparse_completer.py b/tests/test_argparse_completer.py index 2a30fc3b..3274ad0c 100644 --- a/tests/test_argparse_completer.py +++ b/tests/test_argparse_completer.py @@ -9,7 +9,7 @@ from typing import List import pytest import cmd2 -from cmd2 import with_argparser, Cmd2ArgParser, CompletionItem +from cmd2 import with_argparser, CompletionItem from cmd2.utils import StdSim, basic_complete from .conftest import run_cmd, complete_tester @@ -63,7 +63,7 @@ class AutoCompleteTester(cmd2.Cmd): self.poutput('music create rock') # Top level parser for music command - music_parser = Cmd2ArgParser(description='Manage music', prog='music') + music_parser = cmd2.ArgParser(description='Manage music', prog='music') # Add sub-commands to music music_subparsers = music_parser.add_subparsers() @@ -100,7 +100,7 @@ class AutoCompleteTester(cmd2.Cmd): ############################################################################################################ # Uses default flag prefix value (-) - flag_parser = Cmd2ArgParser() + flag_parser = cmd2.ArgParser() flag_parser.add_argument('-n', '--normal_flag', help='A normal flag', action='store_true') flag_parser.add_argument('-a', '--append_flag', help='Append flag', action='append') flag_parser.add_argument('-o', '--append_const_flag', help='Append const flag', action='append_const', const=True) @@ -113,7 +113,7 @@ class AutoCompleteTester(cmd2.Cmd): pass # Uses non-default flag prefix value (+) - plus_flag_parser = Cmd2ArgParser(prefix_chars='+') + plus_flag_parser = cmd2.ArgParser(prefix_chars='+') plus_flag_parser.add_argument('+n', '++normal_flag', help='A normal flag', action='store_true') @with_argparser(plus_flag_parser) @@ -135,7 +135,7 @@ class AutoCompleteTester(cmd2.Cmd): items.append(CompletionItem(main_str, desc='blah blah')) return items - choices_parser = Cmd2ArgParser() + choices_parser = cmd2.ArgParser() # 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", @@ -168,7 +168,7 @@ class AutoCompleteTester(cmd2.Cmd): """Tab completion method""" return basic_complete(text, line, begidx, endidx, completions_from_method) - completer_parser = Cmd2ArgParser() + completer_parser = cmd2.ArgParser() # Flag args for completer command completer_parser.add_argument("-f", "--function", help="a flag using a completer function", @@ -189,7 +189,7 @@ class AutoCompleteTester(cmd2.Cmd): ############################################################################################################ # Begin code related to nargs ############################################################################################################ - nargs_parser = Cmd2ArgParser() + nargs_parser = cmd2.ArgParser() # Flag args for nargs command nargs_parser.add_argument("--set_value", help="a flag with a set value for nargs", nargs=2, @@ -215,7 +215,7 @@ class AutoCompleteTester(cmd2.Cmd): ############################################################################################################ # Begin code related to testing tab hints ############################################################################################################ - hint_parser = Cmd2ArgParser() + hint_parser = cmd2.ArgParser() hint_parser.add_argument('-f', '--flag', help='a flag arg') hint_parser.add_argument('-s', '--suppressed_help', help=argparse.SUPPRESS) hint_parser.add_argument('-t', '--suppressed_hint', help='a flag arg', suppress_tab_hint=True) @@ -652,7 +652,7 @@ Hint: def test_starts_like_flag(): from cmd2.argparse_completer import starts_like_flag - parser = Cmd2ArgParser() + parser = cmd2.ArgParser() # Does not start like a flag assert not starts_like_flag('', parser) @@ -670,7 +670,7 @@ def test_starts_like_flag(): def test_complete_command_no_tokens(ac_app): from cmd2.argparse_completer import AutoCompleter - parser = Cmd2ArgParser() + parser = cmd2.ArgParser() ac = AutoCompleter(parser, ac_app) completions = ac.complete_command(tokens=[], text='', line='', begidx=0, endidx=0) @@ -680,7 +680,7 @@ def test_complete_command_no_tokens(ac_app): def test_complete_command_help_no_tokens(ac_app): from cmd2.argparse_completer import AutoCompleter - parser = Cmd2ArgParser() + parser = cmd2.ArgParser() ac = AutoCompleter(parser, ac_app) completions = ac.complete_command_help(tokens=[], text='', line='', begidx=0, endidx=0) diff --git a/tests/test_argparse_custom.py b/tests/test_argparse_custom.py index 35d97974..b738efa3 100644 --- a/tests/test_argparse_custom.py +++ b/tests/test_argparse_custom.py @@ -7,7 +7,6 @@ import argparse import pytest import cmd2 -from cmd2.argparse_custom import Cmd2ArgParser from .conftest import run_cmd @@ -16,7 +15,7 @@ class ApCustomTestApp(cmd2.Cmd): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - range_parser = Cmd2ArgParser() + range_parser = cmd2.ArgParser() range_parser.add_argument('--arg1', nargs=(2, 3)) range_parser.add_argument('--arg2', nargs=argparse.ZERO_OR_MORE) range_parser.add_argument('--arg3', nargs=argparse.ONE_OR_MORE) @@ -47,7 +46,7 @@ def fake_func(): ({'choices_method': fake_func, 'completer_method': fake_func}, False), ]) def test_apcustom_invalid_args(args, is_valid): - parser = Cmd2ArgParser(prog='test') + parser = cmd2.ArgParser(prog='test') try: parser.add_argument('name', **args) assert is_valid @@ -58,7 +57,7 @@ def test_apcustom_invalid_args(args, is_valid): def test_apcustom_usage(): usage = "A custom usage statement" - parser = Cmd2ArgParser(usage=usage) + parser = cmd2.ArgParser(usage=usage) help = parser.format_help() assert usage in help @@ -76,46 +75,46 @@ def test_apcustom_nargs_not_enough(cust_app): def test_apcustom_narg_empty_tuple(): with pytest.raises(ValueError) as excinfo: - parser = Cmd2ArgParser(prog='test') + parser = cmd2.ArgParser(prog='test') parser.add_argument('invalid_tuple', nargs=()) assert 'Ranged values for nargs must be a tuple of 2 integers' in str(excinfo.value) def test_apcustom_narg_single_tuple(): with pytest.raises(ValueError) as excinfo: - parser = Cmd2ArgParser(prog='test') + parser = cmd2.ArgParser(prog='test') parser.add_argument('invalid_tuple', nargs=(1,)) assert 'Ranged values for nargs must be a tuple of 2 integers' in str(excinfo.value) def test_apcustom_narg_tuple_triple(): with pytest.raises(ValueError) as excinfo: - parser = Cmd2ArgParser(prog='test') + parser = cmd2.ArgParser(prog='test') parser.add_argument('invalid_tuple', nargs=(1, 2, 3)) assert 'Ranged values for nargs must be a tuple of 2 integers' in str(excinfo.value) def test_apcustom_narg_tuple_order(): with pytest.raises(ValueError) as excinfo: - parser = Cmd2ArgParser(prog='test') + parser = cmd2.ArgParser(prog='test') parser.add_argument('invalid_tuple', nargs=(2, 1)) assert 'Invalid nargs range. The first value must be less than the second' in str(excinfo.value) def test_apcustom_narg_tuple_negative(): with pytest.raises(ValueError) as excinfo: - parser = Cmd2ArgParser(prog='test') + parser = cmd2.ArgParser(prog='test') parser.add_argument('invalid_tuple', nargs=(-1, 1)) assert 'Negative numbers are invalid for nargs range' in str(excinfo.value) def test_apcustom_narg_tuple_zero_base(): - parser = Cmd2ArgParser(prog='test') + parser = cmd2.ArgParser(prog='test') parser.add_argument('tuple', nargs=(0, 3)) def test_apcustom_narg_tuple_zero_to_one(): - parser = Cmd2ArgParser(prog='test') + parser = cmd2.ArgParser(prog='test') parser.add_argument('tuple', nargs=(0, 1)) @@ -124,13 +123,13 @@ def test_apcustom_print_message(capsys): test_message = 'The test message' # Specify the file - parser = Cmd2ArgParser(prog='test') + parser = cmd2.ArgParser(prog='test') parser._print_message(test_message, file=sys.stdout) out, err = capsys.readouterr() assert test_message in out # Make sure file defaults to sys.stderr - parser = Cmd2ArgParser(prog='test') + parser = cmd2.ArgParser(prog='test') parser._print_message(test_message) out, err = capsys.readouterr() assert test_message in err @@ -138,7 +137,7 @@ def test_apcustom_print_message(capsys): def test_apcustom_required_options(): # Make sure a 'required arguments' section shows when a flag is marked required - parser = Cmd2ArgParser(prog='test') + parser = cmd2.ArgParser(prog='test') parser.add_argument('--required_flag', required=True) help = parser.format_help() -- cgit v1.2.1 From dc747c6fa05d9abb6b7ab45f3714e4ed01b50a0f Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Fri, 12 Jul 2019 16:23:03 -0400 Subject: Added unit tests for unfinished flag errors --- tests/test_argparse_completer.py | 53 ++++++++++++++++++++++++++++++++++------ 1 file changed, 46 insertions(+), 7 deletions(-) (limited to 'tests') diff --git a/tests/test_argparse_completer.py b/tests/test_argparse_completer.py index 3274ad0c..3be9561d 100644 --- a/tests/test_argparse_completer.py +++ b/tests/test_argparse_completer.py @@ -549,14 +549,53 @@ def test_autcomp_nargs(ac_app, args, completions): assert ac_app.completion_matches == sorted(completions, key=ac_app.matches_sort_key) -""" -TODO: Add unit tests for unfinished flag errors - # Double dash ends the current flag (even if all expected args aren't entered) - ('--set_value --', positional_choices), +@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 --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 --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) + begidx = endidx - len(text) - # Another flag can't start until all expected args are filled out - ('--set_value --one_or_more', set_value_choices), -""" + complete_tester(text, line, begidx, endidx, ac_app) + + out, err = capsys.readouterr() + if is_error: + assert "Flag requires" in out + else: + assert not out def test_completion_items_default_header(ac_app): -- cgit v1.2.1 From 191b4c25293ab1d68b93862243c295d248fd959c Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Fri, 12 Jul 2019 16:36:59 -0400 Subject: More unit tests --- tests/test_argparse_completer.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/test_argparse_completer.py b/tests/test_argparse_completer.py index 3be9561d..ec3c940b 100644 --- a/tests/test_argparse_completer.py +++ b/tests/test_argparse_completer.py @@ -595,7 +595,7 @@ def test_unfinished_flag_error(ac_app, command_and_args, text, is_error, capsys) if is_error: assert "Flag requires" in out else: - assert not out + assert "Flag requires" not in out def test_completion_items_default_header(ac_app): @@ -689,6 +689,22 @@ Hint: ''' +def test_single_prefix_char(): + from cmd2.argparse_completer import single_prefix_char + parser = cmd2.ArgParser(prefix_chars='-+') + + # Invalid + assert not single_prefix_char('', parser) + assert not single_prefix_char('--', parser) + assert not single_prefix_char('-+', parser) + assert not single_prefix_char('++has space', parser) + assert not single_prefix_char('foo', parser) + + # Valid + assert single_prefix_char('-', parser) + assert single_prefix_char('+', parser) + + def test_starts_like_flag(): from cmd2.argparse_completer import starts_like_flag parser = cmd2.ArgParser() -- cgit v1.2.1 From 96e16c90965952182d9e60c8ddb249cbe2236e08 Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Fri, 12 Jul 2019 16:43:05 -0400 Subject: More unit tests --- tests/test_argparse_completer.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'tests') diff --git a/tests/test_argparse_completer.py b/tests/test_argparse_completer.py index ec3c940b..e8ff1aaf 100644 --- a/tests/test_argparse_completer.py +++ b/tests/test_argparse_completer.py @@ -530,6 +530,7 @@ def test_completion_items(ac_app, num_aliases, show_description): # 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), @@ -562,6 +563,7 @@ def test_autcomp_nargs(ac_app, args, completions): ('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), @@ -579,6 +581,7 @@ def test_autcomp_nargs(ac_app, args, completions): ('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), -- cgit v1.2.1 From 5d1fcdba6f1674b46629e92bb3075c12d706af5f Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Fri, 12 Jul 2019 16:46:07 -0400 Subject: Simplified unit tests --- tests/test_argparse_completer.py | 22 +++------------------- 1 file changed, 3 insertions(+), 19 deletions(-) (limited to 'tests') diff --git a/tests/test_argparse_completer.py b/tests/test_argparse_completer.py index e8ff1aaf..72efdc20 100644 --- a/tests/test_argparse_completer.py +++ b/tests/test_argparse_completer.py @@ -623,12 +623,7 @@ def test_autocomp_hint_flag(ac_app, capsys): first_match = complete_tester(text, line, begidx, endidx, ac_app) out, err = capsys.readouterr() - assert first_match is None - assert out == ''' -Hint: - -f, --flag FLAG a flag arg - -''' + assert first_match is None and "Hint" in out def test_autocomp_hint_suppressed_help(ac_app, capsys): @@ -666,13 +661,7 @@ def test_autocomp_hint_pos(ac_app, capsys): first_match = complete_tester(text, line, begidx, endidx, ac_app) out, err = capsys.readouterr() - assert first_match is None - assert out == ''' -Hint: - HINT_POS here is a hint - with new lines - -''' + assert first_match is None and "Hint" in out def test_autocomp_hint_no_help(ac_app, capsys): @@ -684,12 +673,7 @@ def test_autocomp_hint_no_help(ac_app, capsys): first_match = complete_tester(text, line, begidx, endidx, ac_app) out, err = capsys.readouterr() - assert first_match is None - assert not out == ''' -Hint: - NO_HELP_POS - -''' + assert first_match is None and "Hint" in out def test_single_prefix_char(): -- cgit v1.2.1 From 0b9794ab065c7f981fe8841e53c8cb7805026dcc Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Fri, 12 Jul 2019 17:47:30 -0400 Subject: Added more hint unit tests --- tests/test_argparse_completer.py | 75 ++++++++++++++++++++++++---------------- 1 file changed, 46 insertions(+), 29 deletions(-) (limited to 'tests') diff --git a/tests/test_argparse_completer.py b/tests/test_argparse_completer.py index 72efdc20..c477f92f 100644 --- a/tests/test_argparse_completer.py +++ b/tests/test_argparse_completer.py @@ -534,6 +534,7 @@ def test_completion_items(ac_app, num_aliases, show_description): # 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 = '' @@ -614,45 +615,50 @@ def test_completion_items_default_header(ac_app): assert DEFAULT_DESCRIPTIVE_HEADER in ac_app.completion_header -def test_autocomp_hint_flag(ac_app, capsys): - text = '' - line = 'hint --flag {}'.format(text) - endidx = len(line) - begidx = endidx - len(text) - - first_match = complete_tester(text, line, begidx, endidx, ac_app) - out, err = capsys.readouterr() +@pytest.mark.parametrize('command_and_args, text, has_hint', [ + # Normal cases + ('hint', '', True), + ('hint --flag', '', True), + ('hint --suppressed_help', '', False), + ('hint --suppressed_hint', '--', False), - assert first_match is None and "Hint" in out + # 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), -def test_autocomp_hint_suppressed_help(ac_app, capsys): - text = '' - line = 'hint --suppressed_help {}'.format(text) - endidx = len(line) - begidx = endidx - len(text) - - first_match = complete_tester(text, line, begidx, endidx, ac_app) - out, err = capsys.readouterr() + # 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), - assert first_match is None - assert not out + # 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), -def test_autocomp_hint_suppressed_hint(ac_app, capsys): - text = '' - line = 'hint --suppressed_hint {}'.format(text) + # 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) begidx = endidx - len(text) - first_match = complete_tester(text, line, begidx, endidx, ac_app) + complete_tester(text, line, begidx, endidx, ac_app) out, err = capsys.readouterr() - assert first_match is None - assert not out + if has_hint: + assert "Hint" in out + else: + assert "Hint" not in out -def test_autocomp_hint_pos(ac_app, capsys): +def test_autocomp_hint_multiple_lines(ac_app, capsys): text = '' line = 'hint {}'.format(text) endidx = len(line) @@ -661,10 +667,16 @@ def test_autocomp_hint_pos(ac_app, capsys): first_match = complete_tester(text, line, begidx, endidx, ac_app) out, err = capsys.readouterr() - assert first_match is None and "Hint" in out + assert first_match is None + assert out == ''' +Hint: + HINT_POS here is a hint + with new lines + +''' -def test_autocomp_hint_no_help(ac_app, capsys): +def test_autocomp_hint_no_help_text(ac_app, capsys): text = '' line = 'hint foo {}'.format(text) endidx = len(line) @@ -673,7 +685,12 @@ def test_autocomp_hint_no_help(ac_app, capsys): first_match = complete_tester(text, line, begidx, endidx, ac_app) out, err = capsys.readouterr() - assert first_match is None and "Hint" in out + assert first_match is None + assert not out == ''' +Hint: + NO_HELP_POS + +''' def test_single_prefix_char(): -- cgit v1.2.1 From 87702b56c5b146ef9ce867343ceaa675ae9c1c21 Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Mon, 15 Jul 2019 11:30:24 -0400 Subject: Made a few module functions protected --- tests/test_argparse_completer.py | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) (limited to 'tests') diff --git a/tests/test_argparse_completer.py b/tests/test_argparse_completer.py index c477f92f..1262b9e1 100644 --- a/tests/test_argparse_completer.py +++ b/tests/test_argparse_completer.py @@ -694,36 +694,36 @@ Hint: def test_single_prefix_char(): - from cmd2.argparse_completer import single_prefix_char + from cmd2.argparse_completer import _single_prefix_char parser = cmd2.ArgParser(prefix_chars='-+') # Invalid - assert not single_prefix_char('', parser) - assert not single_prefix_char('--', parser) - assert not single_prefix_char('-+', parser) - assert not single_prefix_char('++has space', parser) - assert not single_prefix_char('foo', parser) + assert not _single_prefix_char('', parser) + assert not _single_prefix_char('--', parser) + assert not _single_prefix_char('-+', parser) + assert not _single_prefix_char('++has space', parser) + assert not _single_prefix_char('foo', parser) # Valid - assert single_prefix_char('-', parser) - assert single_prefix_char('+', parser) + assert _single_prefix_char('-', parser) + assert _single_prefix_char('+', parser) -def test_starts_like_flag(): - from cmd2.argparse_completer import starts_like_flag +def test_looks_like_flag(): + from cmd2.argparse_completer import _looks_like_flag parser = cmd2.ArgParser() # Does not start like a flag - assert not starts_like_flag('', parser) - assert not starts_like_flag('non-flag', parser) - assert not starts_like_flag('-', parser) - assert not starts_like_flag('--has space', parser) - assert not starts_like_flag('-2', parser) + assert not _looks_like_flag('', parser) + assert not _looks_like_flag('non-flag', parser) + assert not _looks_like_flag('-', parser) + assert not _looks_like_flag('--has space', parser) + assert not _looks_like_flag('-2', parser) # Does start like a flag - assert starts_like_flag('--', parser) - assert starts_like_flag('-flag', parser) - assert starts_like_flag('--flag', parser) + assert _looks_like_flag('--', parser) + assert _looks_like_flag('-flag', parser) + assert _looks_like_flag('--flag', parser) def test_complete_command_no_tokens(ac_app): -- cgit v1.2.1 From bff6e04607ea9bede7bc981755cdf41740c15462 Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Mon, 15 Jul 2019 11:39:16 -0400 Subject: Make max_completion_items settable --- tests/conftest.py | 2 ++ tests/transcripts/regex_set.txt | 1 + 2 files changed, 3 insertions(+) (limited to 'tests') diff --git a/tests/conftest.py b/tests/conftest.py index 8040c21d..c0aea4a6 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -95,6 +95,7 @@ echo: False editor: vim feedback_to_output: False locals_in_py: False +max_completion_items: 50 prompt: (Cmd) quiet: False timing: False @@ -108,6 +109,7 @@ echo: False # Echo command issued into output editor: vim # Program used by ``edit`` feedback_to_output: False # Include nonessentials in `|`, `>` results locals_in_py: False # Allow access to your application in py via self +max_completion_items: 50 # Maximum number of CompletionItems to display during tab completion prompt: (Cmd) # The prompt issued to solicit input quiet: False # Don't print nonessential feedback timing: False # Report execution times diff --git a/tests/transcripts/regex_set.txt b/tests/transcripts/regex_set.txt index 02bc9875..fdcca3a8 100644 --- a/tests/transcripts/regex_set.txt +++ b/tests/transcripts/regex_set.txt @@ -11,6 +11,7 @@ echo: False editor: /.*/ feedback_to_output: False locals_in_py: False +max_completion_items: 50 maxrepeats: 3 prompt: (Cmd)/ / quiet: False -- cgit v1.2.1 From 218091f1ae3fd9ee0435fb126ea8e032ed3de76f Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Mon, 15 Jul 2019 16:29:06 -0400 Subject: Added ability to specify nargs ranges with no upper bound --- tests/test_argparse_completer.py | 11 ++--------- tests/test_argparse_custom.py | 33 +++++++++++++++------------------ 2 files changed, 17 insertions(+), 27 deletions(-) (limited to 'tests') diff --git a/tests/test_argparse_completer.py b/tests/test_argparse_completer.py index 1262b9e1..4ad4c560 100644 --- a/tests/test_argparse_completer.py +++ b/tests/test_argparse_completer.py @@ -596,10 +596,7 @@ def test_unfinished_flag_error(ac_app, command_and_args, text, is_error, capsys) complete_tester(text, line, begidx, endidx, ac_app) out, err = capsys.readouterr() - if is_error: - assert "Flag requires" in out - else: - assert "Flag requires" not in out + assert is_error == all(x in out for x in ["Error:\n", "expected"]) def test_completion_items_default_header(ac_app): @@ -651,11 +648,7 @@ def test_autocomp_hint(ac_app, command_and_args, text, has_hint, capsys): complete_tester(text, line, begidx, endidx, ac_app) out, err = capsys.readouterr() - - if has_hint: - assert "Hint" in out - else: - assert "Hint" not in out + assert has_hint == ("Hint:\n" in out) def test_autocomp_hint_multiple_lines(ac_app, capsys): diff --git a/tests/test_argparse_custom.py b/tests/test_argparse_custom.py index b738efa3..17fd8334 100644 --- a/tests/test_argparse_custom.py +++ b/tests/test_argparse_custom.py @@ -70,28 +70,20 @@ def test_apcustom_nargs_help_format(cust_app): def test_apcustom_nargs_not_enough(cust_app): out, err = run_cmd(cust_app, 'range --arg1 one') - assert 'Error: argument --arg1: Expected between 2 and 3 arguments' in err[2] + assert 'Error: argument --arg1: expected 2 to 3 arguments' in err[2] -def test_apcustom_narg_empty_tuple(): - with pytest.raises(ValueError) as excinfo: - parser = cmd2.ArgParser(prog='test') - parser.add_argument('invalid_tuple', nargs=()) - assert 'Ranged values for nargs must be a tuple of 2 integers' in str(excinfo.value) - - -def test_apcustom_narg_single_tuple(): - with pytest.raises(ValueError) as excinfo: - parser = cmd2.ArgParser(prog='test') - parser.add_argument('invalid_tuple', nargs=(1,)) - assert 'Ranged values for nargs must be a tuple of 2 integers' in str(excinfo.value) - - -def test_apcustom_narg_tuple_triple(): +@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 = cmd2.ArgParser(prog='test') - parser.add_argument('invalid_tuple', nargs=(1, 2, 3)) - assert 'Ranged values for nargs must be a tuple of 2 integers' in str(excinfo.value) + parser.add_argument('invalid_tuple', nargs=nargs_tuple) + assert 'Ranged values for nargs must be a tuple of 1 or 2 integers' in str(excinfo.value) def test_apcustom_narg_tuple_order(): @@ -113,6 +105,11 @@ def test_apcustom_narg_tuple_zero_base(): parser.add_argument('tuple', nargs=(0, 3)) +def test_apcustom_narg_single_tuple(): + parser = cmd2.ArgParser(prog='test') + parser.add_argument('tuple', nargs=(5,)) + + def test_apcustom_narg_tuple_zero_to_one(): parser = cmd2.ArgParser(prog='test') parser.add_argument('tuple', nargs=(0, 1)) -- cgit v1.2.1 From 22a1d42ad997a69a6870fb3b4a33a4ee0b56a54b Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Mon, 15 Jul 2019 16:52:59 -0400 Subject: Added unit tests --- tests/test_argparse_custom.py | 65 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 55 insertions(+), 10 deletions(-) (limited to 'tests') diff --git a/tests/test_argparse_custom.py b/tests/test_argparse_custom.py index 17fd8334..6d418432 100644 --- a/tests/test_argparse_custom.py +++ b/tests/test_argparse_custom.py @@ -7,6 +7,7 @@ import argparse import pytest import cmd2 +from cmd2.argparse_custom import generate_range_error, INFINITY from .conftest import run_cmd @@ -58,8 +59,7 @@ def test_apcustom_invalid_args(args, is_valid): def test_apcustom_usage(): usage = "A custom usage statement" parser = cmd2.ArgParser(usage=usage) - help = parser.format_help() - assert usage in help + assert usage in parser.format_help() def test_apcustom_nargs_help_format(cust_app): @@ -100,19 +100,43 @@ def test_apcustom_narg_tuple_negative(): assert 'Negative numbers are invalid for nargs range' in str(excinfo.value) +# noinspection PyUnresolvedReferences def test_apcustom_narg_tuple_zero_base(): parser = cmd2.ArgParser(prog='test') - parser.add_argument('tuple', nargs=(0, 3)) + arg = parser.add_argument('tuple', nargs=(0,)) + assert arg.nargs == argparse.ZERO_OR_MORE + assert arg.nargs_range is None + parser = cmd2.ArgParser(prog='test') + arg = parser.add_argument('tuple', nargs=(0, 1)) + assert arg.nargs == argparse.OPTIONAL + assert arg.nargs_range is None -def test_apcustom_narg_single_tuple(): parser = cmd2.ArgParser(prog='test') - parser.add_argument('tuple', nargs=(5,)) + arg = parser.add_argument('tuple', nargs=(0, 3)) + assert arg.nargs == argparse.ZERO_OR_MORE + assert arg.nargs_range == (0, 3) + +# noinspection PyUnresolvedReferences +def test_apcustom_narg_tuple_one_base(): + parser = cmd2.ArgParser(prog='test') + arg = parser.add_argument('tuple', nargs=(1,)) + assert arg.nargs == argparse.ONE_OR_MORE + assert arg.nargs_range is None -def test_apcustom_narg_tuple_zero_to_one(): parser = cmd2.ArgParser(prog='test') - parser.add_argument('tuple', nargs=(0, 1)) + arg = parser.add_argument('tuple', nargs=(1, 5)) + assert arg.nargs == argparse.ONE_OR_MORE + assert arg.nargs_range is (1, 5) + + +# noinspection PyUnresolvedReferences +def test_apcustom_narg_tuple_other(): + parser = cmd2.ArgParser(prog='test') + arg = parser.add_argument('tuple', nargs=(2, 5)) + assert arg.nargs == argparse.ONE_OR_MORE + assert arg.nargs_range is (2, 5) def test_apcustom_print_message(capsys): @@ -132,10 +156,31 @@ def test_apcustom_print_message(capsys): assert test_message in err +def test_generate_range_error(): + # max is INFINITY + err_str = generate_range_error(1, INFINITY) + assert err_str == "expected at least 1 argument" + + err_str = generate_range_error(2, INFINITY) + assert err_str == "expected at least 2 arguments" + + # min and max are equal + err_str = generate_range_error(1, 1) + assert err_str == "expected 1 argument" + + err_str = generate_range_error(2, 2) + assert err_str == "expected 2 arguments" + + # min and max are not equal + err_str = generate_range_error(0, 1) + assert err_str == "expected 0 to 1 argument" + + err_str = generate_range_error(0, 2) + assert err_str == "expected 0 to 2 arguments" + + def test_apcustom_required_options(): # Make sure a 'required arguments' section shows when a flag is marked required parser = cmd2.ArgParser(prog='test') parser.add_argument('--required_flag', required=True) - help = parser.format_help() - - assert 'required arguments' in help + assert 'required arguments' in parser.format_help() -- cgit v1.2.1 From 719641ca5d99386a9446ef0908c964122e8cfc86 Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Mon, 15 Jul 2019 17:21:41 -0400 Subject: More unit tests --- tests/test_argparse_custom.py | 56 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 47 insertions(+), 9 deletions(-) (limited to 'tests') diff --git a/tests/test_argparse_custom.py b/tests/test_argparse_custom.py index 6d418432..dd9c9757 100644 --- a/tests/test_argparse_custom.py +++ b/tests/test_argparse_custom.py @@ -103,40 +103,78 @@ def test_apcustom_narg_tuple_negative(): # noinspection PyUnresolvedReferences def test_apcustom_narg_tuple_zero_base(): parser = cmd2.ArgParser(prog='test') - arg = parser.add_argument('tuple', nargs=(0,)) + arg = parser.add_argument('arg', nargs=(0,)) assert arg.nargs == argparse.ZERO_OR_MORE assert arg.nargs_range is None + assert "[arg [...]]" in parser.format_help() parser = cmd2.ArgParser(prog='test') - arg = parser.add_argument('tuple', nargs=(0, 1)) + arg = parser.add_argument('arg', nargs=(0, 1)) assert arg.nargs == argparse.OPTIONAL assert arg.nargs_range is None + assert "[arg]" in parser.format_help() parser = cmd2.ArgParser(prog='test') - arg = parser.add_argument('tuple', nargs=(0, 3)) + arg = parser.add_argument('arg', nargs=(0, 3)) assert arg.nargs == argparse.ZERO_OR_MORE assert arg.nargs_range == (0, 3) + assert "arg{0..3}" in parser.format_help() # noinspection PyUnresolvedReferences def test_apcustom_narg_tuple_one_base(): parser = cmd2.ArgParser(prog='test') - arg = parser.add_argument('tuple', nargs=(1,)) + arg = parser.add_argument('arg', nargs=(1,)) assert arg.nargs == argparse.ONE_OR_MORE assert arg.nargs_range is None + assert "arg [...]" in parser.format_help() parser = cmd2.ArgParser(prog='test') - arg = parser.add_argument('tuple', nargs=(1, 5)) + arg = parser.add_argument('arg', nargs=(1, 5)) assert arg.nargs == argparse.ONE_OR_MORE - assert arg.nargs_range is (1, 5) + assert arg.nargs_range == (1, 5) + assert "arg{1..5}" in parser.format_help() # noinspection PyUnresolvedReferences -def test_apcustom_narg_tuple_other(): +def test_apcustom_narg_tuple_other_ranges(): + + # Test range with no upper bound on max + parser = cmd2.ArgParser(prog='test') + arg = parser.add_argument('arg', nargs=(2,)) + assert arg.nargs == argparse.ONE_OR_MORE + assert arg.nargs_range == (2, INFINITY) + assert "arg{2+}" in parser.format_help() + + # Valid number of args + parser.parse_args('one two'.split()) + parser.parse_args('one two three'.split()) + + # Not enough args + with pytest.raises(SystemExit): + parser.parse_args('one'.split()) + + # Test finite range parser = cmd2.ArgParser(prog='test') - arg = parser.add_argument('tuple', nargs=(2, 5)) + arg = parser.add_argument('arg', nargs=(2, 5)) assert arg.nargs == argparse.ONE_OR_MORE - assert arg.nargs_range is (2, 5) + assert arg.nargs_range == (2, 5) + assert "arg{2..5}" in parser.format_help() + + # Valid number of args + parser.parse_args('one two'.split()) + parser.parse_args('one two'.split()) + parser.parse_args('one two three'.split()) + parser.parse_args('one two three four'.split()) + parser.parse_args('one two three four five'.split()) + + # Not enough args + with pytest.raises(SystemExit): + parser.parse_args('one'.split()) + + # Too many args + with pytest.raises(SystemExit): + parser.parse_args('one two three four five six'.split()) def test_apcustom_print_message(capsys): -- cgit v1.2.1 From aa394cd88077b37b8ee5796a4d0fe8f7ae5837aa Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Mon, 15 Jul 2019 19:16:26 -0400 Subject: Changed format of help where nargs is a number greater than 1 --- tests/test_argparse_custom.py | 61 ++++++++++++++++++++----------------------- 1 file changed, 28 insertions(+), 33 deletions(-) (limited to 'tests') diff --git a/tests/test_argparse_custom.py b/tests/test_argparse_custom.py index dd9c9757..caf30080 100644 --- a/tests/test_argparse_custom.py +++ b/tests/test_argparse_custom.py @@ -17,9 +17,12 @@ class ApCustomTestApp(cmd2.Cmd): super().__init__(*args, **kwargs) range_parser = cmd2.ArgParser() - range_parser.add_argument('--arg1', nargs=(2, 3)) - range_parser.add_argument('--arg2', nargs=argparse.ZERO_OR_MORE) - range_parser.add_argument('--arg3', nargs=argparse.ONE_OR_MORE) + range_parser.add_argument('--arg0', nargs=1) + range_parser.add_argument('--arg1', nargs=2) + range_parser.add_argument('--arg2', nargs=(3,)) + range_parser.add_argument('--arg3', nargs=(2, 3)) + range_parser.add_argument('--arg4', nargs=argparse.ZERO_OR_MORE) + range_parser.add_argument('--arg5', nargs=argparse.ONE_OR_MORE) @cmd2.with_argparser(range_parser) def do_range(self, _): @@ -64,13 +67,30 @@ def test_apcustom_usage(): def test_apcustom_nargs_help_format(cust_app): out, err = run_cmd(cust_app, 'help range') - assert 'Usage: range [-h] [--arg1 ARG1{2..3}] [--arg2 [ARG2 [...]]]' in out[0] - assert ' [--arg3 ARG3 [...]]' in out[1] + assert 'Usage: range [-h] [--arg0 ARG0] [--arg1 ARG1{2}] [--arg2 ARG2{3+}]' in out[0] + assert ' [--arg3 ARG3{2..3}] [--arg4 [ARG4 [...]]] [--arg5 ARG5 [...]]' in out[1] -def test_apcustom_nargs_not_enough(cust_app): - out, err = run_cmd(cust_app, 'range --arg1 one') - assert 'Error: argument --arg1: expected 2 to 3 arguments' in err[2] +def test_apcustom_nargs_range_validation(cust_app): + # nargs = (3,) + out, err = run_cmd(cust_app, 'range --arg2 one two') + assert 'Error: argument --arg2: expected at least 3 arguments' in err[2] + + out, err = run_cmd(cust_app, 'range --arg2 one two three') + assert not err + + out, err = run_cmd(cust_app, 'range --arg2 one two three four') + assert not err + + # nargs = (2,3) + out, err = run_cmd(cust_app, 'range --arg3 one') + assert 'Error: argument --arg3: expected 2 to 3 arguments' in err[2] + + out, err = run_cmd(cust_app, 'range --arg3 one two') + assert not err + + out, err = run_cmd(cust_app, 'range --arg2 one two three') + assert not err @pytest.mark.parametrize('nargs_tuple', [ @@ -144,37 +164,12 @@ def test_apcustom_narg_tuple_other_ranges(): arg = parser.add_argument('arg', nargs=(2,)) assert arg.nargs == argparse.ONE_OR_MORE assert arg.nargs_range == (2, INFINITY) - assert "arg{2+}" in parser.format_help() - - # Valid number of args - parser.parse_args('one two'.split()) - parser.parse_args('one two three'.split()) - - # Not enough args - with pytest.raises(SystemExit): - parser.parse_args('one'.split()) # Test finite range parser = cmd2.ArgParser(prog='test') arg = parser.add_argument('arg', nargs=(2, 5)) assert arg.nargs == argparse.ONE_OR_MORE assert arg.nargs_range == (2, 5) - assert "arg{2..5}" in parser.format_help() - - # Valid number of args - parser.parse_args('one two'.split()) - parser.parse_args('one two'.split()) - parser.parse_args('one two three'.split()) - parser.parse_args('one two three four'.split()) - parser.parse_args('one two three four five'.split()) - - # Not enough args - with pytest.raises(SystemExit): - parser.parse_args('one'.split()) - - # Too many args - with pytest.raises(SystemExit): - parser.parse_args('one two three four five six'.split()) def test_apcustom_print_message(capsys): -- cgit v1.2.1