summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNed Batchelder <ned@nedbatchelder.com>2023-01-07 21:25:42 -0500
committerNed Batchelder <ned@nedbatchelder.com>2023-01-07 21:25:42 -0500
commit13218037401dc30f05fd3a16a2cd52ee882fd1c4 (patch)
treec58e4361b3bc81c15196255e3c3a90cb10aa2b04
parent2c527825ac0cf394b32d773fd0ca5375dd8c031b (diff)
downloadpython-coveragepy-git-13218037401dc30f05fd3a16a2cd52ee882fd1c4.tar.gz
mypy: test_parser.py test_phystokens.py test_process.py test_report.py test_results.py test_setup.py
-rw-r--r--tests/test_parser.py56
-rw-r--r--tests/test_phystokens.py34
-rw-r--r--tests/test_plugins.py86
-rw-r--r--tests/test_process.py140
-rw-r--r--tests/test_report.py31
-rw-r--r--tests/test_results.py38
-rw-r--r--tests/test_setup.py14
-rw-r--r--tox.ini6
8 files changed, 221 insertions, 184 deletions
diff --git a/tests/test_parser.py b/tests/test_parser.py
index 057b9244..8009ce51 100644
--- a/tests/test_parser.py
+++ b/tests/test_parser.py
@@ -3,11 +3,15 @@
"""Tests for coverage.py's code parsing."""
+from __future__ import annotations
+
import ast
import os.path
import textwrap
import warnings
+from typing import List
+
import pytest
from coverage import env
@@ -23,14 +27,14 @@ class PythonParserTest(CoverageTest):
run_in_temp_dir = False
- def parse_source(self, text):
+ def parse_source(self, text: str) -> PythonParser:
"""Parse `text` as source, and return the `PythonParser` used."""
text = textwrap.dedent(text)
parser = PythonParser(text=text, exclude="nocover")
parser.parse_source()
return parser
- def test_exit_counts(self):
+ def test_exit_counts(self) -> None:
parser = self.parse_source("""\
# check some basic branch counting
class Foo:
@@ -47,7 +51,7 @@ class PythonParserTest(CoverageTest):
2:1, 3:1, 4:2, 5:1, 7:1, 9:1, 10:1
}
- def test_generator_exit_counts(self):
+ def test_generator_exit_counts(self) -> None:
# https://github.com/nedbat/coveragepy/issues/324
parser = self.parse_source("""\
def gen(input):
@@ -63,7 +67,7 @@ class PythonParserTest(CoverageTest):
5:1, # list -> exit
}
- def test_try_except(self):
+ def test_try_except(self) -> None:
parser = self.parse_source("""\
try:
a = 2
@@ -79,7 +83,7 @@ class PythonParserTest(CoverageTest):
1: 1, 2:1, 3:2, 4:1, 5:2, 6:1, 7:1, 8:1, 9:1
}
- def test_excluded_classes(self):
+ def test_excluded_classes(self) -> None:
parser = self.parse_source("""\
class Foo:
def __init__(self):
@@ -93,7 +97,7 @@ class PythonParserTest(CoverageTest):
1:0, 2:1, 3:1
}
- def test_missing_branch_to_excluded_code(self):
+ def test_missing_branch_to_excluded_code(self) -> None:
parser = self.parse_source("""\
if fooey:
a = 2
@@ -121,7 +125,7 @@ class PythonParserTest(CoverageTest):
""")
assert parser.exit_counts() == { 1:1, 2:1, 3:1, 6:1 }
- def test_indentation_error(self):
+ def test_indentation_error(self) -> None:
msg = (
"Couldn't parse '<code>' as Python source: " +
"'unindent does not match any outer indentation level' at line 3"
@@ -133,7 +137,7 @@ class PythonParserTest(CoverageTest):
1
""")
- def test_token_error(self):
+ def test_token_error(self) -> None:
msg = "Couldn't parse '<code>' as Python source: 'EOF in multi-line string' at line 1"
with pytest.raises(NotPython, match=msg):
_ = self.parse_source("""\
@@ -141,7 +145,7 @@ class PythonParserTest(CoverageTest):
""")
@xfail_pypy38
- def test_decorator_pragmas(self):
+ def test_decorator_pragmas(self) -> None:
parser = self.parse_source("""\
# 1
@@ -177,7 +181,7 @@ class PythonParserTest(CoverageTest):
assert parser.statements == {8}
@xfail_pypy38
- def test_decorator_pragmas_with_colons(self):
+ def test_decorator_pragmas_with_colons(self) -> None:
# A colon in a decorator expression would confuse the parser,
# ending the exclusion of the decorated function.
parser = self.parse_source("""\
@@ -197,7 +201,7 @@ class PythonParserTest(CoverageTest):
assert parser.raw_statements == raw_statements
assert parser.statements == set()
- def test_class_decorator_pragmas(self):
+ def test_class_decorator_pragmas(self) -> None:
parser = self.parse_source("""\
class Foo(object):
def __init__(self):
@@ -211,7 +215,7 @@ class PythonParserTest(CoverageTest):
assert parser.raw_statements == {1, 2, 3, 5, 6, 7, 8}
assert parser.statements == {1, 2, 3}
- def test_empty_decorated_function(self):
+ def test_empty_decorated_function(self) -> None:
parser = self.parse_source("""\
def decorator(func):
return func
@@ -247,7 +251,7 @@ class PythonParserTest(CoverageTest):
assert expected_arcs == parser.arcs()
assert expected_exits == parser.exit_counts()
- def test_fuzzed_double_parse(self):
+ def test_fuzzed_double_parse(self) -> None:
# https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=50381
# The second parse used to raise `TypeError: 'NoneType' object is not iterable`
msg = "EOF in multi-line statement"
@@ -262,13 +266,13 @@ class ParserMissingArcDescriptionTest(CoverageTest):
run_in_temp_dir = False
- def parse_text(self, source):
+ def parse_text(self, source: str) -> PythonParser:
"""Parse Python source, and return the parser object."""
parser = PythonParser(text=textwrap.dedent(source))
parser.parse_source()
return parser
- def test_missing_arc_description(self):
+ def test_missing_arc_description(self) -> None:
# This code is never run, so the actual values don't matter.
parser = self.parse_text("""\
if x:
@@ -304,7 +308,7 @@ class ParserMissingArcDescriptionTest(CoverageTest):
)
assert expected == parser.missing_arc_description(11, 13)
- def test_missing_arc_descriptions_for_small_callables(self):
+ def test_missing_arc_descriptions_for_small_callables(self) -> None:
parser = self.parse_text("""\
callables = [
lambda: 2,
@@ -323,7 +327,7 @@ class ParserMissingArcDescriptionTest(CoverageTest):
expected = "line 5 didn't finish the set comprehension on line 5"
assert expected == parser.missing_arc_description(5, -5)
- def test_missing_arc_descriptions_for_exceptions(self):
+ def test_missing_arc_descriptions_for_exceptions(self) -> None:
parser = self.parse_text("""\
try:
pass
@@ -343,7 +347,7 @@ class ParserMissingArcDescriptionTest(CoverageTest):
)
assert expected == parser.missing_arc_description(5, 6)
- def test_missing_arc_descriptions_for_finally(self):
+ def test_missing_arc_descriptions_for_finally(self) -> None:
parser = self.parse_text("""\
def function():
for i in range(2):
@@ -417,7 +421,7 @@ class ParserMissingArcDescriptionTest(CoverageTest):
)
assert expected == parser.missing_arc_description(18, -1)
- def test_missing_arc_descriptions_bug460(self):
+ def test_missing_arc_descriptions_bug460(self) -> None:
parser = self.parse_text("""\
x = 1
d = {
@@ -429,7 +433,7 @@ class ParserMissingArcDescriptionTest(CoverageTest):
assert parser.missing_arc_description(2, -3) == "line 3 didn't finish the lambda on line 3"
@pytest.mark.skipif(not env.PYBEHAVIOR.match_case, reason="Match-case is new in 3.10")
- def test_match_case_with_default(self):
+ def test_match_case_with_default(self) -> None:
parser = self.parse_text("""\
for command in ["huh", "go home", "go n"]:
match command.split():
@@ -450,7 +454,7 @@ class ParserMissingArcDescriptionTest(CoverageTest):
class ParserFileTest(CoverageTest):
"""Tests for coverage.py's code parsing from files."""
- def parse_file(self, filename):
+ def parse_file(self, filename: str) -> PythonParser:
"""Parse `text` as source, and return the `PythonParser` used."""
parser = PythonParser(filename=filename, exclude="nocover")
parser.parse_source()
@@ -459,7 +463,7 @@ class ParserFileTest(CoverageTest):
@pytest.mark.parametrize("slug, newline", [
("unix", "\n"), ("dos", "\r\n"), ("mac", "\r"),
])
- def test_line_endings(self, slug, newline):
+ def test_line_endings(self, slug: str, newline: str) -> None:
text = """\
# check some basic branch counting
class Foo:
@@ -478,14 +482,14 @@ class ParserFileTest(CoverageTest):
parser = self.parse_file(fname)
assert parser.exit_counts() == counts, f"Wrong for {fname!r}"
- def test_encoding(self):
+ def test_encoding(self) -> None:
self.make_file("encoded.py", """\
coverage = "\xe7\xf6v\xear\xe3g\xe9"
""")
parser = self.parse_file("encoded.py")
assert parser.exit_counts() == {1: 1}
- def test_missing_line_ending(self):
+ def test_missing_line_ending(self) -> None:
# Test that the set of statements is the same even if a final
# multi-line statement has no final newline.
# https://github.com/nedbat/coveragepy/issues/293
@@ -514,7 +518,7 @@ class ParserFileTest(CoverageTest):
assert parser.statements == {1}
-def test_ast_dump():
+def test_ast_dump() -> None:
# Run the AST_DUMP code to make sure it doesn't fail, with some light
# assertions. Use parser.py as the test code since it is the longest file,
# and fitting, since it's the AST_DUMP code.
@@ -531,7 +535,7 @@ def test_ast_dump():
# stress_phystoken.tok has deprecation warnings, suppress them.
warnings.filterwarnings("ignore", message=r".*invalid escape sequence",)
ast_root = ast.parse(source)
- result = []
+ result: List[str] = []
ast_dump(ast_root, print=result.append)
if num_lines < 100:
continue
diff --git a/tests/test_phystokens.py b/tests/test_phystokens.py
index dae1a0ed..5807f00d 100644
--- a/tests/test_phystokens.py
+++ b/tests/test_phystokens.py
@@ -58,7 +58,7 @@ class PhysTokensTest(CoverageTest):
run_in_temp_dir = False
- def check_tokenization(self, source):
+ def check_tokenization(self, source: str) -> None:
"""Tokenize `source`, then put it back together, should be the same."""
tokenized = ""
for line in source_token_lines(source):
@@ -71,26 +71,26 @@ class PhysTokensTest(CoverageTest):
tokenized = re.sub(r"(?m)[ \t]+$", "", tokenized)
assert source == tokenized
- def check_file_tokenization(self, fname):
+ def check_file_tokenization(self, fname: str) -> None:
"""Use the contents of `fname` for `check_tokenization`."""
self.check_tokenization(get_python_source(fname))
- def test_simple(self):
+ def test_simple(self) -> None:
assert list(source_token_lines(SIMPLE)) == SIMPLE_TOKENS
self.check_tokenization(SIMPLE)
- def test_missing_final_newline(self):
+ def test_missing_final_newline(self) -> None:
# We can tokenize source that is missing the final newline.
assert list(source_token_lines(SIMPLE.rstrip())) == SIMPLE_TOKENS
- def test_tab_indentation(self):
+ def test_tab_indentation(self) -> None:
# Mixed tabs and spaces...
assert list(source_token_lines(MIXED_WS)) == MIXED_WS_TOKENS
- def test_bug_822(self):
+ def test_bug_822(self) -> None:
self.check_tokenization(BUG_822)
- def test_tokenize_real_file(self):
+ def test_tokenize_real_file(self) -> None:
# Check the tokenization of a real file (large, btw).
real_file = os.path.join(TESTS_DIR, "test_coverage.py")
self.check_file_tokenization(real_file)
@@ -99,7 +99,7 @@ class PhysTokensTest(CoverageTest):
"stress_phystoken.tok",
"stress_phystoken_dos.tok",
])
- def test_stress(self, fname):
+ def test_stress(self, fname: str) -> None:
# Check the tokenization of the stress-test files.
# And check that those files haven't been incorrectly "fixed".
with warnings.catch_warnings():
@@ -116,7 +116,7 @@ class SoftKeywordTest(CoverageTest):
run_in_temp_dir = False
- def test_soft_keywords(self):
+ def test_soft_keywords(self) -> None:
source = textwrap.dedent("""\
match re.match(something):
case ["what"]:
@@ -168,40 +168,40 @@ class SourceEncodingTest(CoverageTest):
run_in_temp_dir = False
- def test_detect_source_encoding(self):
+ def test_detect_source_encoding(self) -> None:
for _, source, expected in ENCODING_DECLARATION_SOURCES:
assert source_encoding(source) == expected, f"Wrong encoding in {source!r}"
- def test_detect_source_encoding_not_in_comment(self):
+ def test_detect_source_encoding_not_in_comment(self) -> None:
# Should not detect anything here
source = b'def parse(src, encoding=None):\n pass'
assert source_encoding(source) == DEF_ENCODING
- def test_dont_detect_source_encoding_on_third_line(self):
+ def test_dont_detect_source_encoding_on_third_line(self) -> None:
# A coding declaration doesn't count on the third line.
source = b"\n\n# coding=cp850\n\n"
assert source_encoding(source) == DEF_ENCODING
- def test_detect_source_encoding_of_empty_file(self):
+ def test_detect_source_encoding_of_empty_file(self) -> None:
# An important edge case.
assert source_encoding(b"") == DEF_ENCODING
- def test_bom(self):
+ def test_bom(self) -> None:
# A BOM means utf-8.
source = b"\xEF\xBB\xBFtext = 'hello'\n"
assert source_encoding(source) == 'utf-8-sig'
- def test_bom_with_encoding(self):
+ def test_bom_with_encoding(self) -> None:
source = b"\xEF\xBB\xBF# coding: utf-8\ntext = 'hello'\n"
assert source_encoding(source) == 'utf-8-sig'
- def test_bom_is_wrong(self):
+ def test_bom_is_wrong(self) -> None:
# A BOM with an explicit non-utf8 encoding is an error.
source = b"\xEF\xBB\xBF# coding: cp850\n"
with pytest.raises(SyntaxError, match="encoding problem: utf-8"):
source_encoding(source)
- def test_unknown_encoding(self):
+ def test_unknown_encoding(self) -> None:
source = b"# coding: klingon\n"
with pytest.raises(SyntaxError, match="unknown encoding: klingon"):
source_encoding(source)
diff --git a/tests/test_plugins.py b/tests/test_plugins.py
index d407f748..866fab87 100644
--- a/tests/test_plugins.py
+++ b/tests/test_plugins.py
@@ -27,7 +27,7 @@ from tests.helpers import CheckUniqueFilenames, swallow_warnings
class FakeConfig:
"""A fake config for use in tests."""
- def __init__(self, plugin, options):
+ def __init__(self, plugin, options) -> None:
self.plugin = plugin
self.options = options
self.asked_for = []
@@ -44,7 +44,7 @@ class FakeConfig:
class LoadPluginsTest(CoverageTest):
"""Test Plugins.load_plugins directly."""
- def test_implicit_boolean(self):
+ def test_implicit_boolean(self) -> None:
self.make_file("plugin1.py", """\
from coverage import CoveragePlugin
@@ -62,7 +62,7 @@ class LoadPluginsTest(CoverageTest):
plugins = Plugins.load_plugins(["plugin1"], config)
assert plugins
- def test_importing_and_configuring(self):
+ def test_importing_and_configuring(self) -> None:
self.make_file("plugin1.py", """\
from coverage import CoveragePlugin
@@ -83,7 +83,7 @@ class LoadPluginsTest(CoverageTest):
assert plugins[0].options == {'a': 'hello'}
assert config.asked_for == ['plugin1']
- def test_importing_and_configuring_more_than_one(self):
+ def test_importing_and_configuring_more_than_one(self) -> None:
self.make_file("plugin1.py", """\
from coverage import CoveragePlugin
@@ -124,11 +124,11 @@ class LoadPluginsTest(CoverageTest):
assert plugins[1].this_is == "me"
assert plugins[1].options == {'a': 'second'}
- def test_cant_import(self):
+ def test_cant_import(self) -> None:
with pytest.raises(ImportError, match="No module named '?plugin_not_there'?"):
_ = Plugins.load_plugins(["plugin_not_there"], None)
- def test_plugin_must_define_coverage_init(self):
+ def test_plugin_must_define_coverage_init(self) -> None:
self.make_file("no_plugin.py", """\
from coverage import CoveragePlugin
Nothing = 0
@@ -141,7 +141,7 @@ class LoadPluginsTest(CoverageTest):
class PluginTest(CoverageTest):
"""Test plugins through the Coverage class."""
- def test_plugin_imported(self):
+ def test_plugin_imported(self) -> None:
# Prove that a plugin will be imported.
self.make_file("my_plugin.py", """\
from coverage import CoveragePlugin
@@ -162,7 +162,7 @@ class PluginTest(CoverageTest):
with open("evidence.out") as f:
assert f.read() == "we are here!"
- def test_missing_plugin_raises_import_error(self):
+ def test_missing_plugin_raises_import_error(self) -> None:
# Prove that a missing plugin will raise an ImportError.
with pytest.raises(ImportError, match="No module named '?does_not_exist_woijwoicweo'?"):
cov = coverage.Coverage()
@@ -170,7 +170,7 @@ class PluginTest(CoverageTest):
cov.start()
cov.stop()
- def test_bad_plugin_isnt_hidden(self):
+ def test_bad_plugin_isnt_hidden(self) -> None:
# Prove that a plugin with an error in it will raise the error.
self.make_file("plugin_over_zero.py", "1/0")
with pytest.raises(ZeroDivisionError):
@@ -179,7 +179,7 @@ class PluginTest(CoverageTest):
cov.start()
cov.stop()
- def test_plugin_sys_info(self):
+ def test_plugin_sys_info(self) -> None:
self.make_file("plugin_sys_info.py", """\
import coverage
@@ -213,7 +213,7 @@ class PluginTest(CoverageTest):
]
assert expected_end == out_lines[-len(expected_end):]
- def test_plugin_with_no_sys_info(self):
+ def test_plugin_with_no_sys_info(self) -> None:
self.make_file("plugin_no_sys_info.py", """\
import coverage
@@ -239,7 +239,7 @@ class PluginTest(CoverageTest):
]
assert expected_end == out_lines[-len(expected_end):]
- def test_local_files_are_importable(self):
+ def test_local_files_are_importable(self) -> None:
self.make_file("importing_plugin.py", """\
from coverage import CoveragePlugin
import local_module
@@ -264,7 +264,7 @@ class PluginTest(CoverageTest):
@pytest.mark.skipif(env.C_TRACER, reason="This test is only about PyTracer.")
class PluginWarningOnPyTracerTest(CoverageTest):
"""Test that we get a controlled exception with plugins on PyTracer."""
- def test_exception_if_plugins_on_pytracer(self):
+ def test_exception_if_plugins_on_pytracer(self) -> None:
self.make_file("simple.py", "a = 1")
cov = coverage.Coverage()
@@ -285,7 +285,7 @@ class FileTracerTest(CoverageTest):
class GoodFileTracerTest(FileTracerTest):
"""Tests of file tracer plugin happy paths."""
- def test_plugin1(self):
+ def test_plugin1(self) -> None:
self.make_file("simple.py", """\
import try_xyz
a = 1
@@ -354,7 +354,7 @@ class GoodFileTracerTest(FileTracerTest):
self.make_file("bar_4.html", lines(4))
self.make_file("foo_7.html", lines(7))
- def test_plugin2(self):
+ def test_plugin2(self) -> None:
self.make_render_and_caller()
cov = coverage.Coverage(omit=["*quux*"])
@@ -379,7 +379,7 @@ class GoodFileTracerTest(FileTracerTest):
assert "quux_5.html" not in line_counts(cov.get_data())
- def test_plugin2_with_branch(self):
+ def test_plugin2_with_branch(self) -> None:
self.make_render_and_caller()
cov = coverage.Coverage(branch=True, omit=["*quux*"])
@@ -400,7 +400,7 @@ class GoodFileTracerTest(FileTracerTest):
assert analysis.missing == {1, 2, 3, 6, 7}
- def test_plugin2_with_text_report(self):
+ def test_plugin2_with_text_report(self) -> None:
self.make_render_and_caller()
cov = coverage.Coverage(branch=True, omit=["*quux*"])
@@ -422,7 +422,7 @@ class GoodFileTracerTest(FileTracerTest):
assert expected == report
assert math.isclose(total, 4 / 11 * 100)
- def test_plugin2_with_html_report(self):
+ def test_plugin2_with_html_report(self) -> None:
self.make_render_and_caller()
cov = coverage.Coverage(branch=True, omit=["*quux*"])
@@ -437,7 +437,7 @@ class GoodFileTracerTest(FileTracerTest):
self.assert_exists("htmlcov/bar_4_html.html")
self.assert_exists("htmlcov/foo_7_html.html")
- def test_plugin2_with_xml_report(self):
+ def test_plugin2_with_xml_report(self) -> None:
self.make_render_and_caller()
cov = coverage.Coverage(branch=True, omit=["*quux*"])
@@ -468,7 +468,7 @@ class GoodFileTracerTest(FileTracerTest):
'name': 'foo_7.html',
}
- def test_defer_to_python(self):
+ def test_defer_to_python(self) -> None:
# A plugin that measures, but then wants built-in python reporting.
self.make_file("fairly_odd_plugin.py", """\
# A plugin that claims all the odd lines are executed, and none of
@@ -521,7 +521,7 @@ class GoodFileTracerTest(FileTracerTest):
assert expected == report
assert total == 50
- def test_find_unexecuted(self):
+ def test_find_unexecuted(self) -> None:
self.make_file("unexecuted_plugin.py", """\
import os
import coverage.plugin
@@ -653,7 +653,7 @@ class BadFileTracerTest(FileTracerTest):
found_exc = any(em in stderr for em in excmsgs) # pragma: part covered
assert found_exc, f"expected one of {excmsgs} in stderr"
- def test_file_tracer_has_no_file_tracer_method(self):
+ def test_file_tracer_has_no_file_tracer_method(self) -> None:
self.make_file("bad_plugin.py", """\
class Plugin(object):
pass
@@ -663,7 +663,7 @@ class BadFileTracerTest(FileTracerTest):
""")
self.run_bad_plugin("bad_plugin", "Plugin", our_error=False)
- def test_file_tracer_has_inherited_sourcefilename_method(self):
+ def test_file_tracer_has_inherited_sourcefilename_method(self) -> None:
self.make_file("bad_plugin.py", """\
import coverage
class Plugin(coverage.CoveragePlugin):
@@ -682,7 +682,7 @@ class BadFileTracerTest(FileTracerTest):
excmsg="Class 'bad_plugin.FileTracer' needs to implement source_filename()",
)
- def test_plugin_has_inherited_filereporter_method(self):
+ def test_plugin_has_inherited_filereporter_method(self) -> None:
self.make_file("bad_plugin.py", """\
import coverage
class Plugin(coverage.CoveragePlugin):
@@ -702,7 +702,7 @@ class BadFileTracerTest(FileTracerTest):
with pytest.raises(NotImplementedError, match=expected_msg):
cov.report()
- def test_file_tracer_fails(self):
+ def test_file_tracer_fails(self) -> None:
self.make_file("bad_plugin.py", """\
import coverage.plugin
class Plugin(coverage.plugin.CoveragePlugin):
@@ -714,7 +714,7 @@ class BadFileTracerTest(FileTracerTest):
""")
self.run_bad_plugin("bad_plugin", "Plugin")
- def test_file_tracer_fails_eventually(self):
+ def test_file_tracer_fails_eventually(self) -> None:
# Django coverage plugin can report on a few files and then fail.
# https://github.com/nedbat/coveragepy/issues/1011
self.make_file("bad_plugin.py", """\
@@ -745,7 +745,7 @@ class BadFileTracerTest(FileTracerTest):
""")
self.run_bad_plugin("bad_plugin", "Plugin")
- def test_file_tracer_returns_wrong(self):
+ def test_file_tracer_returns_wrong(self) -> None:
self.make_file("bad_plugin.py", """\
import coverage.plugin
class Plugin(coverage.plugin.CoveragePlugin):
@@ -759,7 +759,7 @@ class BadFileTracerTest(FileTracerTest):
"bad_plugin", "Plugin", our_error=False, excmsg="'float' object has no attribute",
)
- def test_has_dynamic_source_filename_fails(self):
+ def test_has_dynamic_source_filename_fails(self) -> None:
self.make_file("bad_plugin.py", """\
import coverage.plugin
class Plugin(coverage.plugin.CoveragePlugin):
@@ -775,7 +775,7 @@ class BadFileTracerTest(FileTracerTest):
""")
self.run_bad_plugin("bad_plugin", "Plugin")
- def test_source_filename_fails(self):
+ def test_source_filename_fails(self) -> None:
self.make_file("bad_plugin.py", """\
import coverage.plugin
class Plugin(coverage.plugin.CoveragePlugin):
@@ -791,7 +791,7 @@ class BadFileTracerTest(FileTracerTest):
""")
self.run_bad_plugin("bad_plugin", "Plugin")
- def test_source_filename_returns_wrong(self):
+ def test_source_filename_returns_wrong(self) -> None:
self.make_file("bad_plugin.py", """\
import coverage.plugin
class Plugin(coverage.plugin.CoveragePlugin):
@@ -815,7 +815,7 @@ class BadFileTracerTest(FileTracerTest):
],
)
- def test_dynamic_source_filename_fails(self):
+ def test_dynamic_source_filename_fails(self) -> None:
self.make_file("bad_plugin.py", """\
import coverage.plugin
class Plugin(coverage.plugin.CoveragePlugin):
@@ -834,7 +834,7 @@ class BadFileTracerTest(FileTracerTest):
""")
self.run_bad_plugin("bad_plugin", "Plugin")
- def test_line_number_range_raises_error(self):
+ def test_line_number_range_raises_error(self) -> None:
self.make_file("bad_plugin.py", """\
import coverage.plugin
class Plugin(coverage.plugin.CoveragePlugin):
@@ -856,7 +856,7 @@ class BadFileTracerTest(FileTracerTest):
"bad_plugin", "Plugin", our_error=False, excmsg="borked!",
)
- def test_line_number_range_returns_non_tuple(self):
+ def test_line_number_range_returns_non_tuple(self) -> None:
self.make_file("bad_plugin.py", """\
import coverage.plugin
class Plugin(coverage.plugin.CoveragePlugin):
@@ -878,7 +878,7 @@ class BadFileTracerTest(FileTracerTest):
"bad_plugin", "Plugin", our_error=False, excmsg="line_number_range must return 2-tuple",
)
- def test_line_number_range_returns_triple(self):
+ def test_line_number_range_returns_triple(self) -> None:
self.make_file("bad_plugin.py", """\
import coverage.plugin
class Plugin(coverage.plugin.CoveragePlugin):
@@ -900,7 +900,7 @@ class BadFileTracerTest(FileTracerTest):
"bad_plugin", "Plugin", our_error=False, excmsg="line_number_range must return 2-tuple",
)
- def test_line_number_range_returns_pair_of_strings(self):
+ def test_line_number_range_returns_pair_of_strings(self) -> None:
self.make_file("bad_plugin.py", """\
import coverage.plugin
class Plugin(coverage.plugin.CoveragePlugin):
@@ -932,7 +932,7 @@ class ConfigurerPluginTest(CoverageTest):
run_in_temp_dir = False
- def test_configurer_plugin(self):
+ def test_configurer_plugin(self) -> None:
cov = coverage.Coverage()
cov.set_option("run:plugins", ["tests.plugin_config"])
cov.start()
@@ -978,7 +978,7 @@ class DynamicContextPluginTest(CoverageTest):
reg.add_dynamic_context(Plugin())
""")
- def make_test_files(self):
+ def make_test_files(self) -> None:
"""Make some files to use while testing dynamic context plugins."""
self.make_file("rendering.py", """\
def html_tag(tag, content):
@@ -997,7 +997,7 @@ class DynamicContextPluginTest(CoverageTest):
self.make_file("testsuite.py", """\
import rendering
- def test_html_tag():
+ def test_html_tag() -> None:
assert rendering.html_tag('b', 'hello') == '<b>hello</b>'
def doctest_html_tag():
@@ -1005,7 +1005,7 @@ class DynamicContextPluginTest(CoverageTest):
rendering.html_tag('i', 'text') == '<i>text</i>'
'''.strip())
- def test_renderers():
+ def test_renderers() -> None:
assert rendering.render_paragraph('hello') == '<p>hello</p>'
assert rendering.render_bold('wide') == '<b>wide</b>'
assert rendering.render_span('world') == '<span>world</span>'
@@ -1030,7 +1030,7 @@ class DynamicContextPluginTest(CoverageTest):
finally:
cov.stop()
- def test_plugin_standalone(self):
+ def test_plugin_standalone(self) -> None:
self.make_plugin_capitalized_testnames('plugin_tests.py')
self.make_test_files()
@@ -1053,7 +1053,7 @@ class DynamicContextPluginTest(CoverageTest):
data.set_query_context("test:RENDERERS")
assert [2, 5, 8, 11] == sorted_lines(data, filenames['rendering.py'])
- def test_static_context(self):
+ def test_static_context(self) -> None:
self.make_plugin_capitalized_testnames('plugin_tests.py')
self.make_test_files()
@@ -1074,7 +1074,7 @@ class DynamicContextPluginTest(CoverageTest):
]
assert expected == sorted(data.measured_contexts())
- def test_plugin_with_test_function(self):
+ def test_plugin_with_test_function(self) -> None:
self.make_plugin_capitalized_testnames('plugin_tests.py')
self.make_test_files()
@@ -1107,7 +1107,7 @@ class DynamicContextPluginTest(CoverageTest):
assert_context_lines("testsuite.test_html_tag", [2])
assert_context_lines("testsuite.test_renderers", [2, 5, 8, 11])
- def test_multiple_plugins(self):
+ def test_multiple_plugins(self) -> None:
self.make_plugin_capitalized_testnames('plugin_tests.py')
self.make_plugin_track_render('plugin_renderers.py')
self.make_test_files()
diff --git a/tests/test_process.py b/tests/test_process.py
index 33d52923..bdfa3316 100644
--- a/tests/test_process.py
+++ b/tests/test_process.py
@@ -3,6 +3,8 @@
"""Tests for process behavior of coverage.py."""
+from __future__ import annotations
+
import glob
import os
import os.path
@@ -11,6 +13,8 @@ import stat
import sys
import textwrap
+from typing import Any
+
import pytest
import coverage
@@ -25,7 +29,7 @@ from tests.helpers import re_lines_text
class ProcessTest(CoverageTest):
"""Tests of the per-process behavior of coverage.py."""
- def test_save_on_exit(self):
+ def test_save_on_exit(self) -> None:
self.make_file("mycode.py", """\
h = "Hello"
w = "world"
@@ -35,7 +39,7 @@ class ProcessTest(CoverageTest):
self.run_command("coverage run mycode.py")
self.assert_exists(".coverage")
- def test_tests_dir_is_importable(self):
+ def test_tests_dir_is_importable(self) -> None:
# Checks that we can import modules from the tests directory at all!
self.make_file("mycode.py", """\
import covmod1
@@ -49,7 +53,7 @@ class ProcessTest(CoverageTest):
self.assert_exists(".coverage")
assert out == 'done\n'
- def test_coverage_run_envvar_is_in_coveragerun(self):
+ def test_coverage_run_envvar_is_in_coveragerun(self) -> None:
# Test that we are setting COVERAGE_RUN when we run.
self.make_file("envornot.py", """\
import os
@@ -64,7 +68,7 @@ class ProcessTest(CoverageTest):
out = self.run_command("coverage run envornot.py")
assert out == "true\n"
- def make_b_or_c_py(self):
+ def make_b_or_c_py(self) -> None:
"""Create b_or_c.py, used in a few of these tests."""
# "b_or_c.py b" will run 6 lines.
# "b_or_c.py c" will run 7 lines.
@@ -81,7 +85,7 @@ class ProcessTest(CoverageTest):
print('done')
""")
- def test_append_data(self):
+ def test_append_data(self) -> None:
self.make_b_or_c_py()
out = self.run_command("coverage run b_or_c.py b")
@@ -100,7 +104,7 @@ class ProcessTest(CoverageTest):
data.read()
assert line_counts(data)['b_or_c.py'] == 8
- def test_append_data_with_different_file(self):
+ def test_append_data_with_different_file(self) -> None:
self.make_b_or_c_py()
self.make_file(".coveragerc", """\
@@ -124,7 +128,7 @@ class ProcessTest(CoverageTest):
data.read()
assert line_counts(data)['b_or_c.py'] == 8
- def test_append_can_create_a_data_file(self):
+ def test_append_can_create_a_data_file(self) -> None:
self.make_b_or_c_py()
out = self.run_command("coverage run --append b_or_c.py b")
@@ -138,7 +142,7 @@ class ProcessTest(CoverageTest):
data.read()
assert line_counts(data)['b_or_c.py'] == 6
- def test_combine_with_rc(self):
+ def test_combine_with_rc(self) -> None:
self.make_b_or_c_py()
self.make_file(".coveragerc", """\
@@ -182,7 +186,7 @@ class ProcessTest(CoverageTest):
TOTAL 8 0 100%
""")
- def test_combine_with_aliases(self):
+ def test_combine_with_aliases(self) -> None:
self.make_file("d1/x.py", """\
a = 1
b = 2
@@ -236,7 +240,7 @@ class ProcessTest(CoverageTest):
assert expected == actual
assert list(summary.values())[0] == 6
- def test_erase_parallel(self):
+ def test_erase_parallel(self) -> None:
self.make_file(".coveragerc", """\
[run]
data_file = data.dat
@@ -253,7 +257,7 @@ class ProcessTest(CoverageTest):
self.assert_doesnt_exist("data.dat.gooey")
self.assert_exists(".coverage")
- def test_missing_source_file(self):
+ def test_missing_source_file(self) -> None:
# Check what happens if the source is missing when reporting happens.
self.make_file("fleeting.py", """\
s = 'goodbye, cruel world!'
@@ -278,14 +282,14 @@ class ProcessTest(CoverageTest):
assert "Traceback" not in out
assert status == 1
- def test_running_missing_file(self):
+ def test_running_missing_file(self) -> None:
status, out = self.run_command_status("coverage run xyzzy.py")
assert re.search("No file to run: .*xyzzy.py", out)
assert "raceback" not in out
assert "rror" not in out
assert status == 1
- def test_code_throws(self):
+ def test_code_throws(self) -> None:
self.make_file("throw.py", """\
class MyException(Exception):
pass
@@ -315,7 +319,7 @@ class ProcessTest(CoverageTest):
assert 'raise MyException("hey!")' in out
assert status == 1
- def test_code_exits(self):
+ def test_code_exits(self) -> None:
self.make_file("exit.py", """\
import sys
def f1():
@@ -337,7 +341,7 @@ class ProcessTest(CoverageTest):
assert status == status2
assert status == 17
- def test_code_exits_no_arg(self):
+ def test_code_exits_no_arg(self) -> None:
self.make_file("exit_none.py", """\
import sys
def f1():
@@ -354,7 +358,7 @@ class ProcessTest(CoverageTest):
assert status == 0
@pytest.mark.skipif(not hasattr(os, "fork"), reason="Can't test os.fork, it doesn't exist.")
- def test_fork(self):
+ def test_fork(self) -> None:
self.make_file("fork.py", """\
import os
@@ -397,7 +401,7 @@ class ProcessTest(CoverageTest):
data.read()
assert line_counts(data)['fork.py'] == 9
- def test_warnings_during_reporting(self):
+ def test_warnings_during_reporting(self) -> None:
# While fixing issue #224, the warnings were being printed far too
# often. Make sure they're not any more.
self.make_file("hello.py", """\
@@ -418,7 +422,7 @@ class ProcessTest(CoverageTest):
out = self.run_command("coverage html")
assert out.count("Module xyzzy was never imported.") == 0
- def test_warns_if_never_run(self):
+ def test_warns_if_never_run(self) -> None:
# Note: the name of the function can't have "warning" in it, or the
# absolute path of the file will have "warning" in it, and an assertion
# will fail.
@@ -437,7 +441,7 @@ class ProcessTest(CoverageTest):
assert "Exception" not in out
@pytest.mark.skipif(env.METACOV, reason="Can't test tracers changing during metacoverage")
- def test_warnings_trace_function_changed_with_threads(self):
+ def test_warnings_trace_function_changed_with_threads(self) -> None:
# https://github.com/nedbat/coveragepy/issues/164
self.make_file("bug164.py", """\
@@ -457,7 +461,7 @@ class ProcessTest(CoverageTest):
assert "Hello\n" in out
assert "warning" not in out
- def test_warning_trace_function_changed(self):
+ def test_warning_trace_function_changed(self) -> None:
self.make_file("settrace.py", """\
import sys
print("Hello")
@@ -473,7 +477,7 @@ class ProcessTest(CoverageTest):
# When meta-coverage testing, this test doesn't work, because it finds
# coverage.py's own trace function.
@pytest.mark.skipif(env.METACOV, reason="Can't test timid during coverage measurement.")
- def test_timid(self):
+ def test_timid(self) -> None:
# Test that the --timid command line argument properly swaps the tracer
# function for a simpler one.
#
@@ -527,7 +531,7 @@ class ProcessTest(CoverageTest):
timid_out = self.run_command("coverage run --timid showtrace.py")
assert timid_out == "PyTracer\n"
- def test_warn_preimported(self):
+ def test_warn_preimported(self) -> None:
self.make_file("hello.py", """\
import goodbye
import coverage
@@ -554,7 +558,7 @@ class ProcessTest(CoverageTest):
@pytest.mark.expensive
@pytest.mark.skipif(not env.C_TRACER, reason="fullcoverage only works with the C tracer.")
@pytest.mark.skipif(env.METACOV, reason="Can't test fullcoverage when measuring ourselves")
- def test_fullcoverage(self):
+ def test_fullcoverage(self) -> None:
# fullcoverage is a trick to get stdlib modules measured from
# the very beginning of the process. Here we import os and
# then check how many lines are measured.
@@ -578,7 +582,7 @@ class ProcessTest(CoverageTest):
# Pypy passes locally, but fails in CI? Perhaps the version of macOS is
# significant? https://foss.heptapod.net/pypy/pypy/-/issues/3074
@pytest.mark.skipif(env.PYPY, reason="PyPy is unreliable with this test")
- def test_lang_c(self):
+ def test_lang_c(self) -> None:
# LANG=C forces getfilesystemencoding on Linux to 'ascii', which causes
# failures with non-ascii file names. We don't want to make a real file
# with strange characters, though, because that gets the test runners
@@ -595,7 +599,7 @@ class ProcessTest(CoverageTest):
out = self.run_command("coverage run weird_file.py")
assert out == "1\n2\n"
- def test_deprecation_warnings(self):
+ def test_deprecation_warnings(self) -> None:
# Test that coverage doesn't trigger deprecation warnings.
# https://github.com/nedbat/coveragepy/issues/305
self.make_file("allok.py", """\
@@ -612,7 +616,7 @@ class ProcessTest(CoverageTest):
out = self.run_command("python allok.py")
assert out == "No warnings!\n"
- def test_run_twice(self):
+ def test_run_twice(self) -> None:
# https://github.com/nedbat/coveragepy/issues/353
self.make_file("foo.py", """\
def foo():
@@ -643,7 +647,7 @@ class ProcessTest(CoverageTest):
)
assert expected == out
- def test_module_name(self):
+ def test_module_name(self) -> None:
# https://github.com/nedbat/coveragepy/issues/478
# Make sure help doesn't show a silly command name when run as a
# module, like it used to:
@@ -658,7 +662,7 @@ TRY_EXECFILE = os.path.join(os.path.dirname(__file__), "modules/process_test/try
class EnvironmentTest(CoverageTest):
"""Tests using try_execfile.py to test the execution environment."""
- def assert_tryexecfile_output(self, expected, actual):
+ def assert_tryexecfile_output(self, expected: str, actual: str) -> None:
"""Assert that the output we got is a successful run of try_execfile.py.
`expected` and `actual` must be the same, modulo a few slight known
@@ -669,27 +673,27 @@ class EnvironmentTest(CoverageTest):
assert '"DATA": "xyzzy"' in actual
assert actual == expected
- def test_coverage_run_is_like_python(self):
+ def test_coverage_run_is_like_python(self) -> None:
with open(TRY_EXECFILE) as f:
self.make_file("run_me.py", f.read())
expected = self.run_command("python run_me.py")
actual = self.run_command("coverage run run_me.py")
self.assert_tryexecfile_output(expected, actual)
- def test_coverage_run_far_away_is_like_python(self):
+ def test_coverage_run_far_away_is_like_python(self) -> None:
with open(TRY_EXECFILE) as f:
self.make_file("sub/overthere/prog.py", f.read())
expected = self.run_command("python sub/overthere/prog.py")
actual = self.run_command("coverage run sub/overthere/prog.py")
self.assert_tryexecfile_output(expected, actual)
- def test_coverage_run_dashm_is_like_python_dashm(self):
+ def test_coverage_run_dashm_is_like_python_dashm(self) -> None:
# These -m commands assume the coverage tree is on the path.
expected = self.run_command("python -m process_test.try_execfile")
actual = self.run_command("coverage run -m process_test.try_execfile")
self.assert_tryexecfile_output(expected, actual)
- def test_coverage_run_dir_is_like_python_dir(self):
+ def test_coverage_run_dir_is_like_python_dir(self) -> None:
with open(TRY_EXECFILE) as f:
self.make_file("with_main/__main__.py", f.read())
@@ -697,7 +701,7 @@ class EnvironmentTest(CoverageTest):
actual = self.run_command("coverage run with_main")
self.assert_tryexecfile_output(expected, actual)
- def test_coverage_run_dashm_dir_no_init_is_like_python(self):
+ def test_coverage_run_dashm_dir_no_init_is_like_python(self) -> None:
with open(TRY_EXECFILE) as f:
self.make_file("with_main/__main__.py", f.read())
@@ -705,7 +709,7 @@ class EnvironmentTest(CoverageTest):
actual = self.run_command("coverage run -m with_main")
self.assert_tryexecfile_output(expected, actual)
- def test_coverage_run_dashm_dir_with_init_is_like_python(self):
+ def test_coverage_run_dashm_dir_with_init_is_like_python(self) -> None:
with open(TRY_EXECFILE) as f:
self.make_file("with_main/__main__.py", f.read())
self.make_file("with_main/__init__.py", "")
@@ -714,7 +718,7 @@ class EnvironmentTest(CoverageTest):
actual = self.run_command("coverage run -m with_main")
self.assert_tryexecfile_output(expected, actual)
- def test_coverage_run_dashm_equal_to_doubledashsource(self):
+ def test_coverage_run_dashm_equal_to_doubledashsource(self) -> None:
"""regression test for #328
When imported by -m, a module's __name__ is __main__, but we need the
@@ -727,7 +731,7 @@ class EnvironmentTest(CoverageTest):
)
self.assert_tryexecfile_output(expected, actual)
- def test_coverage_run_dashm_superset_of_doubledashsource(self):
+ def test_coverage_run_dashm_superset_of_doubledashsource(self) -> None:
"""Edge case: --source foo -m foo.bar"""
# Ugh: without this config file, we'll get a warning about
# CoverageWarning: Module process_test was previously imported,
@@ -751,7 +755,7 @@ class EnvironmentTest(CoverageTest):
assert st == 0
assert self.line_count(out) == 6, out
- def test_coverage_run_script_imports_doubledashsource(self):
+ def test_coverage_run_script_imports_doubledashsource(self) -> None:
# This file imports try_execfile, which compiles it to .pyc, so the
# first run will have __file__ == "try_execfile.py" and the second will
# have __file__ == "try_execfile.pyc", which throws off the comparison.
@@ -770,7 +774,7 @@ class EnvironmentTest(CoverageTest):
assert st == 0
assert self.line_count(out) == 6, out
- def test_coverage_run_dashm_is_like_python_dashm_off_path(self):
+ def test_coverage_run_dashm_is_like_python_dashm_off_path(self) -> None:
# https://github.com/nedbat/coveragepy/issues/242
self.make_file("sub/__init__.py", "")
with open(TRY_EXECFILE) as f:
@@ -780,7 +784,7 @@ class EnvironmentTest(CoverageTest):
actual = self.run_command("coverage run -m sub.run_me")
self.assert_tryexecfile_output(expected, actual)
- def test_coverage_run_dashm_is_like_python_dashm_with__main__207(self):
+ def test_coverage_run_dashm_is_like_python_dashm_with__main__207(self) -> None:
# https://github.com/nedbat/coveragepy/issues/207
self.make_file("package/__init__.py", "print('init')")
self.make_file("package/__main__.py", "print('main')")
@@ -788,7 +792,7 @@ class EnvironmentTest(CoverageTest):
actual = self.run_command("coverage run -m package")
assert expected == actual
- def test_coverage_zip_is_like_python(self):
+ def test_coverage_zip_is_like_python(self) -> None:
# Test running coverage from a zip file itself. Some environments
# (windows?) zip up the coverage main to be used as the coverage
# command.
@@ -799,7 +803,7 @@ class EnvironmentTest(CoverageTest):
actual = self.run_command(f"python {cov_main} run run_me.py")
self.assert_tryexecfile_output(expected, actual)
- def test_coverage_custom_script(self):
+ def test_coverage_custom_script(self) -> None:
# https://github.com/nedbat/coveragepy/issues/678
# If sys.path[0] isn't the Python default, then coverage.py won't
# fiddle with it.
@@ -833,7 +837,7 @@ class EnvironmentTest(CoverageTest):
assert "hello-xyzzy" in out
@pytest.mark.skipif(env.WINDOWS, reason="Windows can't make symlinks")
- def test_bug_862(self):
+ def test_bug_862(self) -> None:
# This simulates how pyenv and pyenv-virtualenv end up creating the
# coverage executable.
self.make_file("elsewhere/bin/fake-coverage", """\
@@ -848,7 +852,7 @@ class EnvironmentTest(CoverageTest):
out = self.run_command("somewhere/bin/fake-coverage run bar.py")
assert "inside foo\n" == out
- def test_bug_909(self):
+ def test_bug_909(self) -> None:
# https://github.com/nedbat/coveragepy/issues/909
# The __init__ files were being imported before measurement started,
# so the line in __init__.py was being marked as missed, and there were
@@ -882,7 +886,7 @@ class ExcepthookTest(CoverageTest):
# TODO: do we need these as process tests if we have test_execfile.py:RunFileTest?
- def test_excepthook(self):
+ def test_excepthook(self) -> None:
self.make_file("excepthook.py", """\
import sys
@@ -912,7 +916,7 @@ class ExcepthookTest(CoverageTest):
@pytest.mark.skipif(not env.CPYTHON,
reason="non-CPython handles excepthook exits differently, punt for now."
)
- def test_excepthook_exit(self):
+ def test_excepthook_exit(self) -> None:
self.make_file("excepthook_exit.py", """\
import sys
@@ -933,7 +937,7 @@ class ExcepthookTest(CoverageTest):
assert cov_out == py_out
@pytest.mark.skipif(env.PYPY, reason="PyPy handles excepthook throws differently.")
- def test_excepthook_throw(self):
+ def test_excepthook_throw(self) -> None:
self.make_file("excepthook_throw.py", """\
import sys
@@ -961,20 +965,20 @@ class AliasedCommandTest(CoverageTest):
run_in_temp_dir = False
- def test_major_version_works(self):
+ def test_major_version_works(self) -> None:
# "coverage3" works on py3
cmd = "coverage%d" % sys.version_info[0]
out = self.run_command(cmd)
assert "Code coverage for Python" in out
- def test_wrong_alias_doesnt_work(self):
+ def test_wrong_alias_doesnt_work(self) -> None:
# "coverage2" doesn't work on py3
assert sys.version_info[0] in [2, 3] # Let us know when Python 4 is out...
badcmd = "coverage%d" % (5 - sys.version_info[0])
out = self.run_command(badcmd)
assert "Code coverage for Python" not in out
- def test_specific_alias_works(self):
+ def test_specific_alias_works(self) -> None:
# "coverage-3.9" works on py3.9
cmd = "coverage-%d.%d" % sys.version_info[:2]
out = self.run_command(cmd)
@@ -985,7 +989,7 @@ class AliasedCommandTest(CoverageTest):
"coverage%d" % sys.version_info[0],
"coverage-%d.%d" % sys.version_info[:2],
])
- def test_aliases_used_in_messages(self, cmd):
+ def test_aliases_used_in_messages(self, cmd: str) -> None:
out = self.run_command(f"{cmd} foobar")
assert "Unknown command: 'foobar'" in out
assert f"Use '{cmd} help' for help" in out
@@ -996,7 +1000,7 @@ class PydocTest(CoverageTest):
run_in_temp_dir = False
- def assert_pydoc_ok(self, name, thing):
+ def assert_pydoc_ok(self, name: str, thing: Any) -> None:
"""Check that pydoc of `name` finds the docstring from `thing`."""
# Run pydoc.
out = self.run_command("python -m pydoc " + name)
@@ -1008,17 +1012,17 @@ class PydocTest(CoverageTest):
for line in thing.__doc__.splitlines():
assert line.strip() in out
- def test_pydoc_coverage(self):
+ def test_pydoc_coverage(self) -> None:
self.assert_pydoc_ok("coverage", coverage)
- def test_pydoc_coverage_coverage(self):
+ def test_pydoc_coverage_coverage(self) -> None:
self.assert_pydoc_ok("coverage.Coverage", coverage.Coverage)
class FailUnderTest(CoverageTest):
"""Tests of the --fail-under switch."""
- def setUp(self):
+ def setUp(self) -> None:
super().setUp()
self.make_file("forty_two_plus.py", """\
# I have 42.857% (3/7) coverage!
@@ -1032,25 +1036,25 @@ class FailUnderTest(CoverageTest):
""")
self.make_data_file(lines={abs_file("forty_two_plus.py"): [2, 3, 4]})
- def test_report_43_is_ok(self):
+ def test_report_43_is_ok(self) -> None:
st, out = self.run_command_status("coverage report --fail-under=43")
assert st == 0
assert self.last_line_squeezed(out) == "TOTAL 7 4 43%"
- def test_report_43_is_not_ok(self):
+ def test_report_43_is_not_ok(self) -> None:
st, out = self.run_command_status("coverage report --fail-under=44")
assert st == 2
expected = "Coverage failure: total of 43 is less than fail-under=44"
assert expected == self.last_line_squeezed(out)
- def test_report_42p86_is_not_ok(self):
+ def test_report_42p86_is_not_ok(self) -> None:
self.make_file(".coveragerc", "[report]\nprecision = 2")
st, out = self.run_command_status("coverage report --fail-under=42.88")
assert st == 2
expected = "Coverage failure: total of 42.86 is less than fail-under=42.88"
assert expected == self.last_line_squeezed(out)
- def test_report_99p9_is_not_ok(self):
+ def test_report_99p9_is_not_ok(self) -> None:
# A file with 99.9% coverage:
self.make_file("ninety_nine_plus.py",
"a = 1\n" +
@@ -1067,7 +1071,7 @@ class FailUnderTest(CoverageTest):
class FailUnderNoFilesTest(CoverageTest):
"""Test that nothing to report results in an error exit status."""
- def test_report(self):
+ def test_report(self) -> None:
self.make_file(".coveragerc", "[report]\nfail_under = 99\n")
st, out = self.run_command_status("coverage report")
assert 'No data to report.' in out
@@ -1076,7 +1080,7 @@ class FailUnderNoFilesTest(CoverageTest):
class FailUnderEmptyFilesTest(CoverageTest):
"""Test that empty files produce the proper fail_under exit status."""
- def test_report(self):
+ def test_report(self) -> None:
self.make_file(".coveragerc", "[report]\nfail_under = 99\n")
self.make_file("empty.py", "")
st, _ = self.run_command_status("coverage run empty.py")
@@ -1101,12 +1105,12 @@ class YankedDirectoryTest(CoverageTest):
print(sys.argv[1])
"""
- def test_removing_directory(self):
+ def test_removing_directory(self) -> None:
self.make_file("bug806.py", self.BUG_806)
out = self.run_command("coverage run bug806.py noerror")
assert out == "noerror\n"
- def test_removing_directory_with_error(self):
+ def test_removing_directory_with_error(self) -> None:
self.make_file("bug806.py", self.BUG_806)
out = self.run_command("coverage run bug806.py")
path = python_reported_file('bug806.py')
@@ -1125,7 +1129,7 @@ class YankedDirectoryTest(CoverageTest):
class ProcessStartupTest(CoverageTest):
"""Test that we can measure coverage in sub-processes."""
- def setUp(self):
+ def setUp(self) -> None:
super().setUp()
# Main will run sub.py
@@ -1141,7 +1145,7 @@ class ProcessStartupTest(CoverageTest):
f.close()
""")
- def test_subprocess_with_pth_files(self):
+ def test_subprocess_with_pth_files(self) -> None:
# An existing data file should not be read when a subprocess gets
# measured automatically. Create the data file here with bogus data in
# it.
@@ -1165,7 +1169,7 @@ class ProcessStartupTest(CoverageTest):
data.read()
assert line_counts(data)['sub.py'] == 3
- def test_subprocess_with_pth_files_and_parallel(self):
+ def test_subprocess_with_pth_files_and_parallel(self) -> None:
# https://github.com/nedbat/coveragepy/issues/492
self.make_file("coverage.ini", """\
[run]
@@ -1212,7 +1216,7 @@ class ProcessStartupWithSourceTest(CoverageTest):
@pytest.mark.parametrize("dashm", ["-m", ""])
@pytest.mark.parametrize("package", ["pkg", ""])
@pytest.mark.parametrize("source", ["main", "sub"])
- def test_pth_and_source_work_together(self, dashm, package, source):
+ def test_pth_and_source_work_together(self, dashm: str, package: str, source: str) -> None:
"""Run the test for a particular combination of factors.
The arguments are all strings:
@@ -1227,14 +1231,14 @@ class ProcessStartupWithSourceTest(CoverageTest):
``--source`` argument.
"""
- def fullname(modname):
+ def fullname(modname: str) -> str:
"""What is the full module name for `modname` for this test?"""
if package and dashm:
return '.'.join((package, modname))
else:
return modname
- def path(basename):
+ def path(basename: str) -> str:
"""Where should `basename` be created for this test?"""
return os.path.join(package, basename)
diff --git a/tests/test_report.py b/tests/test_report.py
index 1e7c0762..3d87b514 100644
--- a/tests/test_report.py
+++ b/tests/test_report.py
@@ -3,10 +3,16 @@
"""Tests for helpers in report.py"""
+from __future__ import annotations
+
+from typing import IO, Iterable, List, Optional
+
import pytest
from coverage.exceptions import CoverageException
from coverage.report import render_report
+from coverage.types import TMorf
+
from tests.coveragetest import CoverageTest
@@ -15,42 +21,45 @@ class FakeReporter:
report_type = "fake report file"
- def __init__(self, output="", error=False):
+ def __init__(self, output: str = "", error: bool = False) -> None:
self.output = output
self.error = error
- self.morfs = None
+ self.morfs: Optional[Iterable[TMorf]] = None
- def report(self, morfs, outfile):
+ def report(self, morfs: Optional[Iterable[TMorf]], outfile: IO[str]) -> float:
"""Fake."""
self.morfs = morfs
outfile.write(self.output)
if self.error:
raise CoverageException("You asked for it!")
+ return 17.25
class RenderReportTest(CoverageTest):
"""Tests of render_report."""
- def test_stdout(self):
+ def test_stdout(self) -> None:
fake = FakeReporter(output="Hello!\n")
- msgs = []
- render_report("-", fake, [pytest, "coverage"], msgs.append)
+ msgs: List[str] = []
+ res = render_report("-", fake, [pytest, "coverage"], msgs.append)
+ assert res == 17.25
assert fake.morfs == [pytest, "coverage"]
assert self.stdout() == "Hello!\n"
assert not msgs
- def test_file(self):
+ def test_file(self) -> None:
fake = FakeReporter(output="Gréètings!\n")
- msgs = []
- render_report("output.txt", fake, [], msgs.append)
+ msgs: List[str] = []
+ res = render_report("output.txt", fake, [], msgs.append)
+ assert res == 17.25
assert self.stdout() == ""
with open("output.txt", "rb") as f:
assert f.read().rstrip() == b"Gr\xc3\xa9\xc3\xa8tings!"
assert msgs == ["Wrote fake report file to output.txt"]
- def test_exception(self):
+ def test_exception(self) -> None:
fake = FakeReporter(error=True)
- msgs = []
+ msgs: List[str] = []
with pytest.raises(CoverageException, match="You asked for it!"):
render_report("output.txt", fake, [], msgs.append)
assert self.stdout() == ""
diff --git a/tests/test_results.py b/tests/test_results.py
index 41f3dc40..f2a5ae83 100644
--- a/tests/test_results.py
+++ b/tests/test_results.py
@@ -3,12 +3,17 @@
"""Tests for coverage.py's results analysis."""
+from __future__ import annotations
+
import math
+from typing import Dict, Iterable, List, Tuple, cast
+
import pytest
from coverage.exceptions import ConfigError
from coverage.results import format_lines, Numbers, should_fail_under
+from coverage.types import TLineNo
from tests.coveragetest import CoverageTest
@@ -18,14 +23,14 @@ class NumbersTest(CoverageTest):
run_in_temp_dir = False
- def test_basic(self):
+ def test_basic(self) -> None:
n1 = Numbers(n_files=1, n_statements=200, n_missing=20)
assert n1.n_statements == 200
assert n1.n_executed == 180
assert n1.n_missing == 20
assert n1.pc_covered == 90
- def test_addition(self):
+ def test_addition(self) -> None:
n1 = Numbers(n_files=1, n_statements=200, n_missing=20)
n2 = Numbers(n_files=1, n_statements=10, n_missing=8)
n3 = n1 + n2
@@ -35,10 +40,10 @@ class NumbersTest(CoverageTest):
assert n3.n_missing == 28
assert math.isclose(n3.pc_covered, 86.666666666)
- def test_sum(self):
+ def test_sum(self) -> None:
n1 = Numbers(n_files=1, n_statements=200, n_missing=20)
n2 = Numbers(n_files=1, n_statements=10, n_missing=8)
- n3 = sum([n1, n2])
+ n3 = cast(Numbers, sum([n1, n2]))
assert n3.n_files == 2
assert n3.n_statements == 210
assert n3.n_executed == 182
@@ -55,7 +60,7 @@ class NumbersTest(CoverageTest):
(dict(precision=1, n_files=1, n_statements=10000, n_missing=9999), "0.1"),
(dict(precision=1, n_files=1, n_statements=10000, n_missing=10000), "0.0"),
])
- def test_pc_covered_str(self, kwargs, res):
+ def test_pc_covered_str(self, kwargs: Dict[str, int], res: str) -> None:
assert Numbers(**kwargs).pc_covered_str == res
@pytest.mark.parametrize("prec, pc, res", [
@@ -64,7 +69,7 @@ class NumbersTest(CoverageTest):
(0, 99.995, "99"),
(2, 99.99995, "99.99"),
])
- def test_display_covered(self, prec, pc, res):
+ def test_display_covered(self, prec: int, pc: float, res: str) -> None:
assert Numbers(precision=prec).display_covered(pc) == res
@pytest.mark.parametrize("prec, width", [
@@ -72,10 +77,10 @@ class NumbersTest(CoverageTest):
(1, 5), # 100.0
(4, 8), # 100.0000
])
- def test_pc_str_width(self, prec, width):
+ def test_pc_str_width(self, prec: int, width: int) -> None:
assert Numbers(precision=prec).pc_str_width() == width
- def test_covered_ratio(self):
+ def test_covered_ratio(self) -> None:
n = Numbers(n_files=1, n_statements=200, n_missing=47)
assert n.ratio_covered == (153, 200)
@@ -111,11 +116,11 @@ class NumbersTest(CoverageTest):
(99.999, 100, 2, True),
(99.999, 100, 3, True),
])
-def test_should_fail_under(total, fail_under, precision, result):
+def test_should_fail_under(total: float, fail_under: float, precision: int, result: bool) -> None:
assert should_fail_under(float(total), float(fail_under), precision) == result
-def test_should_fail_under_invalid_value():
+def test_should_fail_under_invalid_value() -> None:
with pytest.raises(ConfigError, match=r"fail_under=101"):
should_fail_under(100.0, 101, 0)
@@ -129,7 +134,11 @@ def test_should_fail_under_invalid_value():
([1, 2, 3, 4, 5], [], ""),
([1, 2, 3, 4, 5], [4], "4"),
])
-def test_format_lines(statements, lines, result):
+def test_format_lines(
+ statements: Iterable[TLineNo],
+ lines: Iterable[TLineNo],
+ result: str,
+) -> None:
assert format_lines(statements, lines) == result
@@ -153,5 +162,10 @@ def test_format_lines(statements, lines, result):
"1-2, 3->4, 99, 102-104"
),
])
-def test_format_lines_with_arcs(statements, lines, arcs, result):
+def test_format_lines_with_arcs(
+ statements: Iterable[TLineNo],
+ lines: Iterable[TLineNo],
+ arcs: Iterable[Tuple[TLineNo, List[TLineNo]]],
+ result: str,
+) -> None:
assert format_lines(statements, lines, arcs) == result
diff --git a/tests/test_setup.py b/tests/test_setup.py
index 5468e3bf..a7a97d1f 100644
--- a/tests/test_setup.py
+++ b/tests/test_setup.py
@@ -3,8 +3,12 @@
"""Tests of miscellaneous stuff."""
+from __future__ import annotations
+
import sys
+from typing import List, cast
+
import coverage
from tests.coveragetest import CoverageTest
@@ -15,12 +19,12 @@ class SetupPyTest(CoverageTest):
run_in_temp_dir = False
- def setUp(self):
+ def setUp(self) -> None:
super().setUp()
# Force the most restrictive interpretation.
self.set_environ('LC_ALL', 'C')
- def test_metadata(self):
+ def test_metadata(self) -> None:
status, output = self.run_command_status(
"python setup.py --description --version --url --author"
)
@@ -31,19 +35,19 @@ class SetupPyTest(CoverageTest):
assert "github.com/nedbat/coveragepy" in out[2]
assert "Ned Batchelder" in out[3]
- def test_more_metadata(self):
+ def test_more_metadata(self) -> None:
# Let's be sure we pick up our own setup.py
# CoverageTest restores the original sys.path for us.
sys.path.insert(0, '')
from setup import setup_args
- classifiers = setup_args['classifiers']
+ classifiers = cast(List[str], setup_args['classifiers'])
assert len(classifiers) > 7
assert classifiers[-1].startswith("Development Status ::")
assert "Programming Language :: Python :: %d" % sys.version_info[:1] in classifiers
assert "Programming Language :: Python :: %d.%d" % sys.version_info[:2] in classifiers
- long_description = setup_args['long_description'].splitlines()
+ long_description = cast(str, setup_args['long_description']).splitlines()
assert len(long_description) > 7
assert long_description[0].strip() != ""
assert long_description[-1].strip() != ""
diff --git a/tox.ini b/tox.ini
index 8722241c..cf0d09d2 100644
--- a/tox.ini
+++ b/tox.ini
@@ -105,8 +105,10 @@ setenv =
T2=tests/test_annotate.py tests/test_api.py tests/test_arcs.py tests/test_cmdline.py tests/test_collector.py tests/test_concurrency.py
T3=tests/test_config.py tests/test_context.py tests/test_coverage.py tests/test_data.py tests/test_debug.py tests/test_execfile.py
T4=tests/test_filereporter.py tests/test_files.py tests/test_goldtest.py tests/test_html.py tests/test_json.py tests/test_lcov.py
- T5=tests/test_misc.py tests/test_mixins.py tests/test_numbits.py tests/test_oddball.py tests/test_python.py tests/test_summary.py tests/test_xml.py
- TYPEABLE={env:C1} {env:C2} {env:C3} {env:C4} {env:C5} {env:C6} {env:T1} {env:T2} {env:T3} {env:T4} {env:T5}
+ T5=tests/test_misc.py tests/test_mixins.py tests/test_numbits.py tests/test_oddball.py tests/test_parser.py tests/test_phystokens.py
+ T6=tests/test_process.py tests/test_python.py tests/test_report.py tests/test_results.py tests/test_setup.py tests/test_summary.py tests/test_xml.py
+ # not done yet: test_plugins.py
+ TYPEABLE={env:C1} {env:C2} {env:C3} {env:C4} {env:C5} {env:C6} {env:T1} {env:T2} {env:T3} {env:T4} {env:T5} {env:T6}
commands =
# PYVERSIONS