diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/test_goldtest.py | 26 | ||||
-rw-r--r-- | tests/test_json.py | 26 | ||||
-rw-r--r-- | tests/test_lcov.py | 21 | ||||
-rw-r--r-- | tests/test_mixins.py | 16 | ||||
-rw-r--r-- | tests/test_numbits.py | 28 | ||||
-rw-r--r-- | tests/test_oddball.py | 41 |
6 files changed, 90 insertions, 68 deletions
diff --git a/tests/test_goldtest.py b/tests/test_goldtest.py index 4462d5ab..def5ee90 100644 --- a/tests/test_goldtest.py +++ b/tests/test_goldtest.py @@ -3,6 +3,8 @@ """Tests of the helpers in goldtest.py""" +from __future__ import annotations + import os.path import re @@ -33,7 +35,7 @@ SCRUBS = [ (r'G\w+', 'Gxxx'), ] -def path_regex(path): +def path_regex(path: str) -> str: """Convert a file path into a regex that will match that path on any OS.""" return re.sub(r"[/\\]", r"[/\\\\]", path.replace(".", "[.]")) @@ -48,16 +50,16 @@ OUT_PATH_RX = path_regex("out/gettysburg.txt") class CompareTest(CoverageTest): """Tests of goldtest.py:compare()""" - def setUp(self): + def setUp(self) -> None: super().setUp() self.addCleanup(remove_tree, ACTUAL_DIR) - def test_good(self): + def test_good(self) -> None: self.make_file("out/gettysburg.txt", GOOD_GETTY) compare(gold_path("testing/getty"), "out", scrubs=SCRUBS) self.assert_doesnt_exist(ACTUAL_GETTY_FILE) - def test_bad(self): + def test_bad(self) -> None: self.make_file("out/gettysburg.txt", BAD_GETTY) # compare() raises an assertion. @@ -78,7 +80,7 @@ class CompareTest(CoverageTest): saved = f.read() assert saved == BAD_GETTY - def test_good_needs_scrubs(self): + def test_good_needs_scrubs(self) -> None: # Comparing the "good" result without scrubbing the variable parts will fail. self.make_file("out/gettysburg.txt", GOOD_GETTY) @@ -91,7 +93,7 @@ class CompareTest(CoverageTest): assert "- 11/19/1863, Gettysburg, Pennsylvania" in stdout assert "+ 11/19/9999, Gettysburg, Pennsylvania" in stdout - def test_actual_extra(self): + def test_actual_extra(self) -> None: self.make_file("out/gettysburg.txt", GOOD_GETTY) self.make_file("out/another.more", "hi") @@ -107,7 +109,7 @@ class CompareTest(CoverageTest): # But only the files matching the file_pattern are considered. compare(gold_path("testing/getty"), "out", file_pattern="*.txt", scrubs=SCRUBS) - def test_xml_good(self): + def test_xml_good(self) -> None: self.make_file("out/output.xml", """\ <?xml version="1.0" ?> <the_root c="three" b="222" a="one"> @@ -118,7 +120,7 @@ class CompareTest(CoverageTest): """) compare(gold_path("testing/xml"), "out", scrubs=SCRUBS) - def test_xml_bad(self): + def test_xml_bad(self) -> None: self.make_file("out/output.xml", """\ <?xml version="1.0" ?> <the_root c="nine" b="2" a="one"> @@ -147,25 +149,25 @@ class ContainsTest(CoverageTest): run_in_temp_dir = False - def test_contains(self): + def test_contains(self) -> None: contains(GOLD_GETTY_FILE, "Four", "fathers", "dedicated") msg = rf"Missing content in {GOLD_GETTY_FILE_RX}: 'xyzzy'" with pytest.raises(AssertionError, match=msg): contains(GOLD_GETTY_FILE, "Four", "fathers", "xyzzy", "dedicated") - def test_contains_rx(self): + def test_contains_rx(self) -> None: contains_rx(GOLD_GETTY_FILE, r"Fo.r", r"f[abc]thers", "dedi[cdef]ated") msg = rf"Missing regex in {GOLD_GETTY_FILE_RX}: r'm\[opq\]thers'" with pytest.raises(AssertionError, match=msg): contains_rx(GOLD_GETTY_FILE, r"Fo.r", r"m[opq]thers") - def test_contains_any(self): + def test_contains_any(self) -> None: contains_any(GOLD_GETTY_FILE, "Five", "Four", "Three") msg = rf"Missing content in {GOLD_GETTY_FILE_RX}: 'One' \[1 of 3\]" with pytest.raises(AssertionError, match=msg): contains_any(GOLD_GETTY_FILE, "One", "Two", "Three") - def test_doesnt_contain(self): + def test_doesnt_contain(self) -> None: doesnt_contain(GOLD_GETTY_FILE, "One", "Two", "Three") msg = rf"Forbidden content in {GOLD_GETTY_FILE_RX}: 'Four'" with pytest.raises(AssertionError, match=msg): diff --git a/tests/test_json.py b/tests/test_json.py index 9bb69bf2..acfdbba7 100644 --- a/tests/test_json.py +++ b/tests/test_json.py @@ -2,17 +2,29 @@ # For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt """Test json-based summary reporting for coverage.py""" -from datetime import datetime + +from __future__ import annotations + import json import os +from datetime import datetime +from typing import Any, Dict + import coverage +from coverage import Coverage + from tests.coveragetest import UsingModulesMixin, CoverageTest class JsonReportTest(UsingModulesMixin, CoverageTest): """Tests of the JSON reports from coverage.py.""" - def _assert_expected_json_report(self, cov, expected_result): + + def _assert_expected_json_report( + self, + cov: Coverage, + expected_result: Dict[str, Any], + ) -> None: """ Helper for tests that handles the common ceremony so the tests can be clearly show the consequences of setting various arguments. @@ -39,7 +51,7 @@ class JsonReportTest(UsingModulesMixin, CoverageTest): del (parsed_result['meta']['timestamp']) assert parsed_result == expected_result - def test_branch_coverage(self): + def test_branch_coverage(self) -> None: cov = coverage.Coverage(branch=True) expected_result = { 'meta': { @@ -91,7 +103,7 @@ class JsonReportTest(UsingModulesMixin, CoverageTest): } self._assert_expected_json_report(cov, expected_result) - def test_simple_line_coverage(self): + def test_simple_line_coverage(self) -> None: cov = coverage.Coverage() expected_result = { 'meta': { @@ -125,7 +137,7 @@ class JsonReportTest(UsingModulesMixin, CoverageTest): } self._assert_expected_json_report(cov, expected_result) - def run_context_test(self, relative_files): + def run_context_test(self, relative_files: bool) -> None: """A helper for two tests below.""" self.make_file("config", """\ [run] @@ -187,8 +199,8 @@ class JsonReportTest(UsingModulesMixin, CoverageTest): } self._assert_expected_json_report(cov, expected_result) - def test_context_non_relative(self): + def test_context_non_relative(self) -> None: self.run_context_test(relative_files=False) - def test_context_relative(self): + def test_context_relative(self) -> None: self.run_context_test(relative_files=True) diff --git a/tests/test_lcov.py b/tests/test_lcov.py index 6c9605ca..95bc5fee 100644 --- a/tests/test_lcov.py +++ b/tests/test_lcov.py @@ -3,6 +3,8 @@ """Test LCOV-based summary reporting for coverage.py.""" +from __future__ import annotations + import math import textwrap @@ -15,7 +17,7 @@ from coverage import env class LcovTest(CoverageTest): """Tests of the LCOV reports from coverage.py.""" - def create_initial_files(self): + def create_initial_files(self) -> None: """ Helper for tests that handles the common ceremony so the tests can show the consequences of changes in the setup. @@ -44,13 +46,12 @@ class LcovTest(CoverageTest): self.assertAlmostEqual(cuboid_volume(5.5),166.375) """) - def get_lcov_report_content(self, filename="coverage.lcov"): + def get_lcov_report_content(self, filename: str="coverage.lcov") -> str: """Return the content of an LCOV report.""" with open(filename, "r") as file: - file_contents = file.read() - return file_contents + return file.read() - def test_lone_file(self): + def test_lone_file(self) -> None: """For a single file with a couple of functions, the lcov should cover the function definitions themselves, but not the returns.""" self.make_file("main_file.py", """\ @@ -81,7 +82,7 @@ class LcovTest(CoverageTest): actual_result = self.get_lcov_report_content() assert expected_result == actual_result - def test_simple_line_coverage_two_files(self): + def test_simple_line_coverage_two_files(self) -> None: """Test that line coverage is created when coverage is run, and matches the output of the file below.""" self.create_initial_files() @@ -119,7 +120,7 @@ class LcovTest(CoverageTest): actual_result = self.get_lcov_report_content(filename="data.lcov") assert expected_result == actual_result - def test_branch_coverage_one_file(self): + def test_branch_coverage_one_file(self) -> None: """Test that the reporter produces valid branch coverage.""" self.make_file("main_file.py", """\ #!/usr/bin/env python3 @@ -154,7 +155,7 @@ class LcovTest(CoverageTest): actual_result = self.get_lcov_report_content() assert expected_result == actual_result - def test_branch_coverage_two_files(self): + def test_branch_coverage_two_files(self) -> None: """Test that valid branch coverage is generated in the case of two files.""" self.make_file("main_file.py", """\ @@ -215,7 +216,7 @@ class LcovTest(CoverageTest): actual_result = self.get_lcov_report_content() assert actual_result == expected_result - def test_half_covered_branch(self): + def test_half_covered_branch(self) -> None: """Test that for a given branch that is only half covered, the block numbers remain the same, and produces valid lcov. """ @@ -251,7 +252,7 @@ class LcovTest(CoverageTest): actual_result = self.get_lcov_report_content() assert actual_result == expected_result - def test_empty_init_files(self): + def test_empty_init_files(self) -> None: """Test that in the case of an empty __init__.py file, the lcov reporter will note that the file is there, and will note the empty line. It will also note the lack of branches, and the checksum for diff --git a/tests/test_mixins.py b/tests/test_mixins.py index 5692c334..cb49ab92 100644 --- a/tests/test_mixins.py +++ b/tests/test_mixins.py @@ -3,6 +3,8 @@ """Tests of code in tests/mixins.py""" +from __future__ import annotations + import pytest from coverage.misc import import_local_file @@ -13,12 +15,12 @@ from tests.mixins import TempDirMixin, RestoreModulesMixin class TempDirMixinTest(TempDirMixin): """Test the methods in TempDirMixin.""" - def file_text(self, fname): + def file_text(self, fname: str) -> str: """Return the text read from a file.""" with open(fname, "rb") as f: return f.read().decode('ascii') - def test_make_file(self): + def test_make_file(self) -> None: # A simple file. self.make_file("fooey.boo", "Hello there") assert self.file_text("fooey.boo") == "Hello there" @@ -38,7 +40,7 @@ class TempDirMixinTest(TempDirMixin): """) assert self.file_text("dedented.txt") == "Hello\nBye\n" - def test_make_file_newline(self): + def test_make_file_newline(self) -> None: self.make_file("unix.txt", "Hello\n") assert self.file_text("unix.txt") == "Hello\n" self.make_file("dos.txt", "Hello\n", newline="\r\n") @@ -46,13 +48,13 @@ class TempDirMixinTest(TempDirMixin): self.make_file("mac.txt", "Hello\n", newline="\r") assert self.file_text("mac.txt") == "Hello\r" - def test_make_file_non_ascii(self): + def test_make_file_non_ascii(self) -> None: self.make_file("unicode.txt", "tablo: «ταБℓσ»") with open("unicode.txt", "rb") as f: text = f.read() assert text == b"tablo: \xc2\xab\xcf\x84\xce\xb1\xd0\x91\xe2\x84\x93\xcf\x83\xc2\xbb" - def test_make_bytes_file(self): + def test_make_bytes_file(self) -> None: self.make_file("binary.dat", bytes=b"\x99\x33\x66hello\0") with open("binary.dat", "rb") as f: data = f.read() @@ -63,12 +65,12 @@ class RestoreModulessMixinTest(TempDirMixin, RestoreModulesMixin): """Tests of SysPathModulesMixin.""" @pytest.mark.parametrize("val", [17, 42]) - def test_module_independence(self, val): + def test_module_independence(self, val: int) -> None: self.make_file("xyzzy.py", f"A = {val}") import xyzzy # pylint: disable=import-error assert xyzzy.A == val - def test_cleanup_and_reimport(self): + def test_cleanup_and_reimport(self) -> None: self.make_file("xyzzy.py", "A = 17") xyzzy = import_local_file("xyzzy") assert xyzzy.A == 17 diff --git a/tests/test_numbits.py b/tests/test_numbits.py index 39cb93f6..ba6d8ec2 100644 --- a/tests/test_numbits.py +++ b/tests/test_numbits.py @@ -3,9 +3,13 @@ """Tests for coverage.numbits""" +from __future__ import annotations + import json import sqlite3 +from typing import Iterable, Set + from hypothesis import example, given, settings from hypothesis.strategies import sets, integers @@ -29,7 +33,7 @@ if env.METACOV: default_settings = settings(default_settings, deadline=None) -def good_numbits(numbits): +def good_numbits(numbits: bytes) -> None: """Assert that numbits is good.""" # It shouldn't end with a zero byte, that should have been trimmed off. assert (not numbits) or (numbits[-1] != 0) @@ -42,7 +46,7 @@ class NumbitsOpTest(CoverageTest): @given(line_number_sets) @settings(default_settings) - def test_conversion(self, nums): + def test_conversion(self, nums: Iterable[int]) -> None: numbits = nums_to_numbits(nums) good_numbits(numbits) nums2 = numbits_to_nums(numbits) @@ -50,7 +54,7 @@ class NumbitsOpTest(CoverageTest): @given(line_number_sets, line_number_sets) @settings(default_settings) - def test_union(self, nums1, nums2): + def test_union(self, nums1: Set[int], nums2: Set[int]) -> None: nb1 = nums_to_numbits(nums1) good_numbits(nb1) nb2 = nums_to_numbits(nums2) @@ -62,7 +66,7 @@ class NumbitsOpTest(CoverageTest): @given(line_number_sets, line_number_sets) @settings(default_settings) - def test_intersection(self, nums1, nums2): + def test_intersection(self, nums1: Set[int], nums2: Set[int]) -> None: nb1 = nums_to_numbits(nums1) good_numbits(nb1) nb2 = nums_to_numbits(nums2) @@ -74,7 +78,7 @@ class NumbitsOpTest(CoverageTest): @given(line_number_sets, line_number_sets) @settings(default_settings) - def test_any_intersection(self, nums1, nums2): + def test_any_intersection(self, nums1: Set[int], nums2: Set[int]) -> None: nb1 = nums_to_numbits(nums1) good_numbits(nb1) nb2 = nums_to_numbits(nums2) @@ -86,7 +90,7 @@ class NumbitsOpTest(CoverageTest): @given(line_numbers, line_number_sets) @settings(default_settings) @example(152, {144}) - def test_num_in_numbits(self, num, nums): + def test_num_in_numbits(self, num: int, nums: Iterable[int]) -> None: numbits = nums_to_numbits(nums) good_numbits(numbits) is_in = num_in_numbits(num, numbits) @@ -98,7 +102,7 @@ class NumbitsSqliteFunctionTest(CoverageTest): run_in_temp_dir = False - def setUp(self): + def setUp(self) -> None: super().setUp() conn = sqlite3.connect(":memory:") register_sqlite_functions(conn) @@ -113,7 +117,7 @@ class NumbitsSqliteFunctionTest(CoverageTest): ) self.addCleanup(self.cursor.close) - def test_numbits_union(self): + def test_numbits_union(self) -> None: res = self.cursor.execute( "select numbits_union(" + "(select numbits from data where id = 7)," + @@ -127,7 +131,7 @@ class NumbitsSqliteFunctionTest(CoverageTest): answer = numbits_to_nums(list(res)[0][0]) assert expected == answer - def test_numbits_intersection(self): + def test_numbits_intersection(self) -> None: res = self.cursor.execute( "select numbits_intersection(" + "(select numbits from data where id = 7)," + @@ -137,7 +141,7 @@ class NumbitsSqliteFunctionTest(CoverageTest): answer = numbits_to_nums(list(res)[0][0]) assert [63] == answer - def test_numbits_any_intersection(self): + def test_numbits_any_intersection(self) -> None: res = self.cursor.execute( "select numbits_any_intersection(?, ?)", (nums_to_numbits([1, 2, 3]), nums_to_numbits([3, 4, 5])) @@ -152,11 +156,11 @@ class NumbitsSqliteFunctionTest(CoverageTest): answer = [any_inter for (any_inter,) in res] assert [0] == answer - def test_num_in_numbits(self): + def test_num_in_numbits(self) -> None: res = self.cursor.execute("select id, num_in_numbits(12, numbits) from data order by id") answer = [is_in for (id, is_in) in res] assert [1, 1, 1, 1, 0, 1, 0, 0, 0, 0] == answer - def test_numbits_to_nums(self): + def test_numbits_to_nums(self) -> None: res = self.cursor.execute("select numbits_to_nums(?)", [nums_to_numbits([1, 2, 3])]) assert [1, 2, 3] == json.loads(res.fetchone()[0]) diff --git a/tests/test_oddball.py b/tests/test_oddball.py index ca139737..a44beae4 100644 --- a/tests/test_oddball.py +++ b/tests/test_oddball.py @@ -24,7 +24,7 @@ from tests import osinfo class ThreadingTest(CoverageTest): """Tests of the threading support.""" - def test_threading(self): + def test_threading(self) -> None: self.check_coverage("""\ import threading @@ -44,7 +44,7 @@ class ThreadingTest(CoverageTest): """, [1, 3, 4, 6, 7, 9, 10, 12, 13, 14, 15], "10") - def test_thread_run(self): + def test_thread_run(self) -> None: self.check_coverage("""\ import threading @@ -67,7 +67,7 @@ class ThreadingTest(CoverageTest): class RecursionTest(CoverageTest): """Check what happens when recursive code gets near limits.""" - def test_short_recursion(self): + def test_short_recursion(self) -> None: # We can definitely get close to 500 stack frames. self.check_coverage("""\ def recur(n): @@ -81,7 +81,7 @@ class RecursionTest(CoverageTest): """, [1, 2, 3, 5, 7, 8], "") - def test_long_recursion(self): + def test_long_recursion(self) -> None: # We can't finish a very deep recursion, but we don't crash. with pytest.raises(RuntimeError): with swallow_warnings("Trace function changed, data is likely wrong: None"): @@ -97,7 +97,7 @@ class RecursionTest(CoverageTest): [1, 2, 3, 5, 7], "" ) - def test_long_recursion_recovery(self): + def test_long_recursion_recovery(self) -> None: # Test the core of bug 93: https://github.com/nedbat/coveragepy/issues/93 # When recovering from a stack overflow, the Python trace function is # disabled, but the C trace function is not. So if we're using a @@ -157,7 +157,7 @@ class MemoryLeakTest(CoverageTest): """ @flaky @pytest.mark.skipif(not env.C_TRACER, reason="Only the C tracer has refcounting issues") - def test_for_leaks(self): + def test_for_leaks(self) -> None: # Our original bad memory leak only happened on line numbers > 255, so # make a code object with more lines than that. Ugly string mumbo # jumbo to get 300 blank lines at the beginning.. @@ -204,7 +204,7 @@ class MemoryFumblingTest(CoverageTest): """Test that we properly manage the None refcount.""" @pytest.mark.skipif(not env.C_TRACER, reason="Only the C tracer has refcounting issues") - def test_dropping_none(self): # pragma: not covered + def test_dropping_none(self) -> None: # pragma: not covered # TODO: Mark this so it will only be run sometimes. pytest.skip("This is too expensive for now (30s)") # Start and stop coverage thousands of times to flush out bad @@ -238,7 +238,7 @@ class MemoryFumblingTest(CoverageTest): class PyexpatTest(CoverageTest): """Pyexpat screws up tracing. Make sure we've counter-defended properly.""" - def test_pyexpat(self): + def test_pyexpat(self) -> None: # pyexpat calls the trace function explicitly (inexplicably), and does # it wrong for exceptions. Parsing a DOCTYPE for some reason throws # an exception internally, and triggers its wrong behavior. This test @@ -290,7 +290,7 @@ class ExceptionTest(CoverageTest): in the trace function. """ - def test_exception(self): + def test_exception(self) -> None: # Python 2.3's trace function doesn't get called with "return" if the # scope is exiting due to an exception. This confounds our trace # function which relies on scope announcements to track which files to @@ -368,8 +368,8 @@ class ExceptionTest(CoverageTest): for callnames, lines_expected in runs: # Make the list of functions we'll call for this test. - callnames = callnames.split() - calls = [getattr(sys.modules[cn], cn) for cn in callnames] + callnames_list = callnames.split() + calls = [getattr(sys.modules[cn], cn) for cn in callnames_list] cov = coverage.Coverage() cov.start() @@ -382,7 +382,7 @@ class ExceptionTest(CoverageTest): # The file names are absolute, so keep just the base. clean_lines = {} data = cov.get_data() - for callname in callnames: + for callname in callnames_list: filename = callname + ".py" clean_lines[filename] = sorted_lines(data, abs_file(filename)) @@ -392,7 +392,7 @@ class ExceptionTest(CoverageTest): class DoctestTest(CoverageTest): """Tests invoked with doctest should measure properly.""" - def test_doctest(self): + def test_doctest(self) -> None: # Doctests used to be traced, with their line numbers credited to the # file they were in. Below, one of the doctests has four lines (1-4), # which would incorrectly claim that lines 1-4 of the file were @@ -433,7 +433,7 @@ class DoctestTest(CoverageTest): class GettraceTest(CoverageTest): """Tests that we work properly with `sys.gettrace()`.""" - def test_round_trip_in_untraced_function(self): + def test_round_trip_in_untraced_function(self) -> None: # https://github.com/nedbat/coveragepy/issues/575 self.make_file("main.py", """import sample""") self.make_file("sample.py", """\ @@ -466,7 +466,7 @@ class GettraceTest(CoverageTest): assert statements == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] assert missing == [] - def test_setting_new_trace_function(self): + def test_setting_new_trace_function(self) -> None: # https://github.com/nedbat/coveragepy/issues/436 self.check_coverage('''\ import os.path @@ -484,7 +484,7 @@ class GettraceTest(CoverageTest): t = sys.gettrace() assert t is tracer, t - def test_unsets_trace(): + def test_unsets_trace() -> None: begin() collect() @@ -498,6 +498,7 @@ class GettraceTest(CoverageTest): missing="5-7, 13-14", ) + assert self.last_module_name is not None out = self.stdout().replace(self.last_module_name, "coverage_test") expected = ( "call: coverage_test.py @ 12\n" + @@ -509,7 +510,7 @@ class GettraceTest(CoverageTest): @pytest.mark.expensive @pytest.mark.skipif(env.METACOV, reason="Can't set trace functions during meta-coverage") - def test_atexit_gettrace(self): + def test_atexit_gettrace(self) -> None: # This is not a test of coverage at all, but of our understanding # of this edge-case behavior in various Pythons. @@ -541,7 +542,7 @@ class GettraceTest(CoverageTest): class ExecTest(CoverageTest): """Tests of exec.""" - def test_correct_filename(self): + def test_correct_filename(self) -> None: # https://github.com/nedbat/coveragepy/issues/380 # Bug was that exec'd files would have their lines attributed to the # calling file. Make two files, both with ~30 lines, but no lines in @@ -570,7 +571,7 @@ class ExecTest(CoverageTest): assert statements == [31] assert missing == [] - def test_unencodable_filename(self): + def test_unencodable_filename(self) -> None: # https://github.com/nedbat/coveragepy/issues/891 self.make_file("bug891.py", r"""exec(compile("pass", "\udcff.py", "exec"))""") cov = coverage.Coverage() @@ -587,7 +588,7 @@ class MockingProtectionTest(CoverageTest): https://github.com/nedbat/coveragepy/issues/416 """ - def test_os_path_exists(self): + def test_os_path_exists(self) -> None: # To see if this test still detects the problem, change isolate_module # in misc.py to simply return its argument. It should fail with a # StopIteration error. |