From 2fed954a350142219000ccd2f1579bcdaa9c6e95 Mon Sep 17 00:00:00 2001 From: Eleanor Boyd Date: Tue, 19 Nov 2024 14:37:05 -0800 Subject: [PATCH] Respect [tool.coverage.report] omit in coverage view (#24466) fixes: https://github.com/microsoft/vscode-python/issues/24366 also adds a missing test dependency to the requirements file, and moves how to import the NoSource exception --- build/test-requirements.txt | 2 + .../.data/coverage_w_config/pyproject.toml | 5 ++ .../.data/coverage_w_config/test_ignore.py | 5 ++ .../.data/coverage_w_config/test_ran.py | 9 ++++ .../tests/pytestadapter/test_coverage.py | 52 ++++++++++++++++--- python_files/vscode_pytest/__init__.py | 14 ++++- 6 files changed, 78 insertions(+), 9 deletions(-) create mode 100644 python_files/tests/pytestadapter/.data/coverage_w_config/pyproject.toml create mode 100644 python_files/tests/pytestadapter/.data/coverage_w_config/test_ignore.py create mode 100644 python_files/tests/pytestadapter/.data/coverage_w_config/test_ran.py diff --git a/build/test-requirements.txt b/build/test-requirements.txt index 49e5fb4f75c3..af19987bc8cb 100644 --- a/build/test-requirements.txt +++ b/build/test-requirements.txt @@ -31,6 +31,8 @@ django-stubs # for coverage coverage pytest-cov +pytest-json + # for pytest-describe related tests pytest-describe diff --git a/python_files/tests/pytestadapter/.data/coverage_w_config/pyproject.toml b/python_files/tests/pytestadapter/.data/coverage_w_config/pyproject.toml new file mode 100644 index 000000000000..334fa05bd25e --- /dev/null +++ b/python_files/tests/pytestadapter/.data/coverage_w_config/pyproject.toml @@ -0,0 +1,5 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +[tool.coverage.report] +omit = ["test_ignore.py"] diff --git a/python_files/tests/pytestadapter/.data/coverage_w_config/test_ignore.py b/python_files/tests/pytestadapter/.data/coverage_w_config/test_ignore.py new file mode 100644 index 000000000000..98640e336ab4 --- /dev/null +++ b/python_files/tests/pytestadapter/.data/coverage_w_config/test_ignore.py @@ -0,0 +1,5 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +def test_to_ignore(): + assert True diff --git a/python_files/tests/pytestadapter/.data/coverage_w_config/test_ran.py b/python_files/tests/pytestadapter/.data/coverage_w_config/test_ran.py new file mode 100644 index 000000000000..864acec79ba2 --- /dev/null +++ b/python_files/tests/pytestadapter/.data/coverage_w_config/test_ran.py @@ -0,0 +1,9 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +def test_simple(): + assert True + + +def untouched_function(): + return 1 diff --git a/python_files/tests/pytestadapter/test_coverage.py b/python_files/tests/pytestadapter/test_coverage.py index d4e3669e2733..bbf5e2f83976 100644 --- a/python_files/tests/pytestadapter/test_coverage.py +++ b/python_files/tests/pytestadapter/test_coverage.py @@ -1,5 +1,6 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. +import json import os import pathlib import sys @@ -43,21 +44,21 @@ def test_simple_pytest_coverage(): assert focal_function_coverage.get("lines_covered") is not None assert focal_function_coverage.get("lines_missed") is not None assert set(focal_function_coverage.get("lines_covered")) == {4, 5, 7, 9, 10, 11, 12, 13, 14, 17} - assert set(focal_function_coverage.get("lines_missed")) == {18, 19, 6} + assert len(set(focal_function_coverage.get("lines_missed"))) >= 3 -coverage_file_path = TEST_DATA_PATH / "coverage_gen" / "coverage.json" +coverage_gen_file_path = TEST_DATA_PATH / "coverage_gen" / "coverage.json" @pytest.fixture -def cleanup_coverage_file(): +def cleanup_coverage_gen_file(): # delete the coverage file if it exists as part of test cleanup yield - if os.path.exists(coverage_file_path): # noqa: PTH110 - os.remove(coverage_file_path) # noqa: PTH107 + if os.path.exists(coverage_gen_file_path): # noqa: PTH110 + os.remove(coverage_gen_file_path) # noqa: PTH107 -def test_coverage_gen_report(cleanup_coverage_file): # noqa: ARG001 +def test_coverage_gen_report(cleanup_coverage_gen_file): # noqa: ARG001 """ Test coverage payload is correct for simple pytest example. Output of coverage run is below. @@ -73,6 +74,7 @@ def test_coverage_gen_report(cleanup_coverage_file): # noqa: ARG001 args = ["--cov-report=json"] env_add = {"COVERAGE_ENABLED": "True"} cov_folder_path = TEST_DATA_PATH / "coverage_gen" + print("cov_folder_path", cov_folder_path) actual = runner_with_cwd_env(args, cov_folder_path, env_add) assert actual coverage = actual[-1] @@ -87,4 +89,40 @@ def test_coverage_gen_report(cleanup_coverage_file): # noqa: ARG001 assert set(focal_function_coverage.get("lines_covered")) == {4, 5, 7, 9, 10, 11, 12, 13, 14, 17} assert set(focal_function_coverage.get("lines_missed")) == {18, 19, 6} # assert that the coverage file was created at the right path - assert os.path.exists(coverage_file_path) # noqa: PTH110 + assert os.path.exists(coverage_gen_file_path) # noqa: PTH110 + + +def test_coverage_w_omit_config(): + """ + Test the coverage report generation with omit configuration. + + folder structure of coverage_w_config + ├── coverage_w_config + │ ├── test_ignore.py + │ ├── test_ran.py + │ └── pyproject.toml + + pyproject.toml file with the following content: + [tool.coverage.report] + omit = [ + "test_ignore.py", + ] + + + Assertions: + - The coverage report is generated. + - The coverage report contains results. + - Only one file is reported in the coverage results. + """ + env_add = {"COVERAGE_ENABLED": "True"} + cov_folder_path = TEST_DATA_PATH / "coverage_w_config" + print("cov_folder_path", cov_folder_path) + actual = runner_with_cwd_env([], cov_folder_path, env_add) + assert actual + print("actual", json.dumps(actual, indent=2)) + coverage = actual[-1] + assert coverage + results = coverage["result"] + assert results + # assert one file is reported and one file (as specified in pyproject.toml) is omitted + assert len(results) == 1 diff --git a/python_files/vscode_pytest/__init__.py b/python_files/vscode_pytest/__init__.py index 9f02481b344a..59a1b75e9688 100644 --- a/python_files/vscode_pytest/__init__.py +++ b/python_files/vscode_pytest/__init__.py @@ -442,17 +442,27 @@ def pytest_sessionfinish(session, exitstatus): if is_coverage_run == "True": # load the report and build the json result to return import coverage - from coverage.exceptions import NoSource + from coverage import exceptions cov = coverage.Coverage() cov.load() file_set: set[str] = cov.get_data().measured_files() file_coverage_map: dict[str, FileCoverageInfo] = {} + + # remove files omitted per coverage report config if any + omit_files = cov.config.report_omit + if omit_files: + omit_files = set(omit_files) + # convert to absolute paths, check against file set + omit_files = {os.fspath(pathlib.Path(file).absolute()) for file in omit_files} + print("Files to omit from reporting", omit_files) + file_set = file_set - omit_files + for file in file_set: try: analysis = cov.analysis2(file) - except NoSource: + except exceptions.NoSource: # as per issue 24308 this best way to handle this edge case continue lines_executable = {int(line_no) for line_no in analysis[1]}