Skip to content

Commit

Permalink
test: assert the messages of raised errors
Browse files Browse the repository at this point in the history
I'm planning to make the error messages more user-friendly, (especially
"Warning: found unmatched (duplicate?) arguments ..."), so I need to verify
what the messages currently are so I can ensure that any changes are expected.
  • Loading branch information
h4l committed Aug 28, 2022
1 parent 1374305 commit 742cc44
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 36 deletions.
114 changes: 81 additions & 33 deletions tests/test_docopt.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import re
from docopt import (
ParsedOptions,
docopt,
Expand Down Expand Up @@ -85,7 +84,10 @@ def test_commands():
assert docopt("Usage: prog (add|rm)", "add") == {"add": True, "rm": False}
assert docopt("Usage: prog (add|rm)", "rm") == {"add": False, "rm": True}
assert docopt("Usage: prog a b", "a b") == {"a": True, "b": True}
with raises(DocoptExit):
with raises(
DocoptExit,
match=r"Warning: found unmatched \(duplicate\?\) arguments" ".*'b'.*'a'",
):
docopt("Usage: prog a b", "b a")


Expand Down Expand Up @@ -487,40 +489,53 @@ def test_long_options_error_handling():
# docopt('Usage: prog --non-existent', '--non-existent')
# with raises(DocoptLanguageError):
# docopt('Usage: prog --non-existent')
with raises(DocoptExit):
with raises(
DocoptExit,
match=r"Warning: found unmatched \(duplicate\?\) arguments.*--non-existent",
):
docopt("Usage: prog", "--non-existent")
with raises(DocoptExit):
with raises(
DocoptExit, match=r"Warning: found unmatched \(duplicate\?\) arguments.*--ver\b"
):
docopt(
"Usage: prog [--version --verbose]\n" "Options: --version\n --verbose",
"--ver",
)
with raises(DocoptLanguageError):
# --long is missing ARG in usage
with raises(DocoptLanguageError, match=r"unmatched '\('"):
docopt("Usage: prog --long\nOptions: --long ARG")
with raises(DocoptExit):
with raises(DocoptExit, match=r"--long requires argument"):
docopt("Usage: prog --long ARG\nOptions: --long ARG", "--long")
with raises(DocoptLanguageError):
with raises(DocoptLanguageError, match=r"--long must not have an argument"):
docopt("Usage: prog --long=ARG\nOptions: --long")
with raises(DocoptExit):
with raises(DocoptExit, match=r"--long must not have an argument"):
docopt("Usage: prog --long\nOptions: --long", "--long=ARG")


def test_short_options_error_handling():
with raises(DocoptLanguageError):
with raises(DocoptLanguageError, match=r"-x is specified ambiguously 2 times"):
docopt("Usage: prog -x\nOptions: -x this\n -x that")

with raises(DocoptExit):
with raises(
DocoptExit, match=r"Warning: found unmatched \(duplicate\?\) arguments.*-x"
):
docopt("Usage: prog", "-x")

with raises(DocoptLanguageError):
# -o needs an argument in usage. The "unmatched" error is because `-o` is
# expanded to `( -o )` during parsing, and ')' is consumed as the argument
# of -o. Clearly this isn't the right error, but the current parser isn't
# able to distinguish between a literal ')' value and a ) closing a required
# group.
with raises(DocoptLanguageError, match=r"unmatched '\('"):
docopt("Usage: prog -o\nOptions: -o ARG")
with raises(DocoptExit):
with raises(DocoptExit, match=r"-o requires argument"):
docopt("Usage: prog -o ARG\nOptions: -o ARG", "-o")


def test_matching_paren():
with raises(DocoptLanguageError):
with raises(DocoptLanguageError, match=r"unmatched '\['"):
docopt("Usage: prog [a [b]")
with raises(DocoptLanguageError):
with raises(DocoptLanguageError, match=r"unexpected ending: '\)'"):
docopt("Usage: prog [a [b] ] c )")


Expand All @@ -535,11 +550,13 @@ def test_allow_double_dash():
"<arg>": "1",
"--": False,
}
with raises(DocoptExit): # "--" is not allowed; FIXME?
with raises(
DocoptExit, match=r"Warning: found unmatched \(duplicate\?\) arguments.*-o\b"
): # "--" is not allowed; FIXME?
docopt("usage: prog [-o] <arg>\noptions:-o", "-- -o")


def test_docopt():
def test_docopt(capsys: pytest.CaptureFixture):
doc = """Usage: prog [-v] A
Options: -v Be verbose."""
Expand Down Expand Up @@ -579,14 +596,23 @@ def test_docopt():
"OUTPUT": None,
}

with raises(DocoptExit): # does not match
with raises(
DocoptExit,
match=r"Warning: found unmatched \(duplicate\?\) arguments.*output\.py",
):
docopt(doc, "-v input.py output.py")

with raises(DocoptExit):
with raises(
DocoptExit,
match=r"Warning: found unmatched \(duplicate\?\) arguments.*--fake",
):
docopt(doc, "--fake")

capsys.readouterr() # clear any captured output

with raises(SystemExit):
docopt(doc, "--hel")
assert capsys.readouterr().out.startswith("Usage: prog")


@pytest.mark.parametrize(
Expand All @@ -604,15 +630,23 @@ def test_docopt_result_dict_repr(items: dict[str, object], expected: str):


def test_language_errors():
with raises(DocoptLanguageError):
with raises(
DocoptLanguageError,
match=r'"usage:" section \(case-insensitive\) not found. '
r"Perhaps missing indentation\?",
):
docopt("no usage with colon here")
with raises(DocoptLanguageError):
with raises(
DocoptLanguageError, match=r'More than one "usage:" \(case-insensitive\)'
):
docopt("usage: here \n\n and again usage: here")


def test_issue_40():
def test_issue_40(capsys: pytest.CaptureFixture):
with raises(SystemExit): # i.e. shows help
docopt("usage: prog --help-commands | --help", "--help")
assert capsys.readouterr().out.startswith("usage: prog --help-commands | --help")

assert docopt("usage: prog --aabb | --aa", "--aa") == {
"--aabb": False,
"--aa": True,
Expand All @@ -631,28 +665,46 @@ def test_count_multiple_flags():
assert docopt("usage: prog [-vv]", "") == {"-v": 0}
assert docopt("usage: prog [-vv]", "-v") == {"-v": 1}
assert docopt("usage: prog [-vv]", "-vv") == {"-v": 2}
with raises(DocoptExit):
with raises(
DocoptExit, match=r"Warning: found unmatched \(duplicate\?\) arguments.*'-v'"
):
docopt("usage: prog [-vv]", "-vvv")
assert docopt("usage: prog [-v | -vv | -vvv]", "-vvv") == {"-v": 3}
assert docopt("usage: prog -v...", "-vvvvvv") == {"-v": 6}
assert docopt("usage: prog [--ver --ver]", "--ver --ver") == {"--ver": 2}


def test_any_options_parameter():
with raises(DocoptExit):
with raises(
DocoptExit,
match=r"Warning: found unmatched \(duplicate\?\) arguments"
r".*-f.*-o.*-o.*--bar.*--spam.*eggs",
):
docopt("usage: prog [options]", "-foo --bar --spam=eggs")
# assert docopt('usage: prog [options]', '-foo --bar --spam=eggs',
# any_options=True) == {'-f': True, '-o': 2,
# '--bar': True, '--spam': 'eggs'}
with raises(DocoptExit):
with raises(
DocoptExit,
match=r"Warning: found unmatched \(duplicate\?\) arguments"
r".*--foo.*--bar.*--bar",
):
docopt("usage: prog [options]", "--foo --bar --bar")
# assert docopt('usage: prog [options]', '--foo --bar --bar',
# any_options=True) == {'--foo': True, '--bar': 2}
with raises(DocoptExit):
with raises(
DocoptExit,
match=r"Warning: found unmatched \(duplicate\?\) arguments"
r".*--bar.*--bar.*--bar.*-f.*-f.*-f.*-f",
):
docopt("usage: prog [options]", "--bar --bar --bar -ffff")
# assert docopt('usage: prog [options]', '--bar --bar --bar -ffff',
# any_options=True) == {'--bar': 3, '-f': 4}
with raises(DocoptExit):
with raises(
DocoptExit,
match=r"Warning: found unmatched \(duplicate\?\) arguments"
r".*--long.*arg.*--long.*another",
):
docopt("usage: prog [options]", "--long=arg --long=another")


Expand Down Expand Up @@ -725,14 +777,10 @@ def test_issue_65_evaluate_argv_when_called_not_when_imported():


def test_issue_71_double_dash_is_not_a_valid_option_argument():
with raises(DocoptExit):
with raises(DocoptExit, match=r"--log requires argument"):
docopt("usage: prog [--log=LEVEL] [--] <args>...", "--log -- 1 2")
with raises(DocoptExit):
docopt(
"""usage: prog [-l LEVEL] [--] <args>...
options: -l LEVEL""",
"-l -- 1 2",
)
with raises(DocoptExit, match=r"-l requires argument"):
docopt("usage: prog [-l LEVEL] [--] <args>...\noptions: -l LEVEL", "-l -- 1 2")


usage = """usage: this
Expand Down
13 changes: 10 additions & 3 deletions tests/test_docopt_ng.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,10 +153,16 @@ def test_docopt_ng_more_magic_global_arguments_and_dot_access():
}
assert arguments.FILE is None

with raises(DocoptExit):
with raises(
DocoptExit,
match=r"Warning: found unmatched \(duplicate\?\) arguments.*output\.py",
):
docopt.docopt(doc, "-v input.py output.py")

with raises(DocoptExit):
with raises(
DocoptExit,
match=r"Warning: found unmatched \(duplicate\?\) arguments.*--fake",
):
docopt.docopt(doc, "--fake")
arguments = None

Expand All @@ -180,9 +186,10 @@ def test_docopt_ng_negative_float():
assert args == {"--negative_pi": "-3.14", "NEGTAU": "-6.28"}


def test_docopt_ng_doubledash_version():
def test_docopt_ng_doubledash_version(capsys: pytest.CaptureFixture):
with pytest.raises(SystemExit) as pytest_wrapped_e:
docopt.docopt("usage: prog", version=1, argv="prog --version")
assert capsys.readouterr().out == "1\n"
assert pytest_wrapped_e.type == SystemExit


Expand Down

0 comments on commit 742cc44

Please sign in to comment.