diff options
Diffstat (limited to 'tests/test_argparse_custom.py')
-rw-r--r-- | tests/test_argparse_custom.py | 219 |
1 files changed, 219 insertions, 0 deletions
diff --git a/tests/test_argparse_custom.py b/tests/test_argparse_custom.py new file mode 100644 index 00000000..caf30080 --- /dev/null +++ b/tests/test_argparse_custom.py @@ -0,0 +1,219 @@ +# flake8: noqa E302 +""" +Unit/functional testing for argparse customizations in cmd2 +""" +import argparse + +import pytest + +import cmd2 +from cmd2.argparse_custom import generate_range_error, INFINITY +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 = cmd2.ArgParser() + 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, _): + 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 = cmd2.ArgParser(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 = cmd2.ArgParser(usage=usage) + assert usage in parser.format_help() + + +def test_apcustom_nargs_help_format(cust_app): + out, err = run_cmd(cust_app, 'help range') + 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_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', [ + (), + ('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=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(): + with pytest.raises(ValueError) as excinfo: + 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 = cmd2.ArgParser(prog='test') + parser.add_argument('invalid_tuple', nargs=(-1, 1)) + 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') + 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('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('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('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('arg', nargs=(1, 5)) + assert arg.nargs == argparse.ONE_OR_MORE + assert arg.nargs_range == (1, 5) + assert "arg{1..5}" in parser.format_help() + + +# noinspection PyUnresolvedReferences +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) + + # 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) + + +def test_apcustom_print_message(capsys): + import sys + test_message = 'The test message' + + # Specify the file + 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 = cmd2.ArgParser(prog='test') + parser._print_message(test_message) + out, err = capsys.readouterr() + 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) + assert 'required arguments' in parser.format_help() |