Skip to content

Commit 2fed954

Browse files
authored
Respect [tool.coverage.report] omit in coverage view (#24466)
fixes: #24366 also adds a missing test dependency to the requirements file, and moves how to import the NoSource exception
1 parent 8ac716d commit 2fed954

File tree

6 files changed

+78
-9
lines changed

6 files changed

+78
-9
lines changed

build/test-requirements.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ django-stubs
3131
# for coverage
3232
coverage
3333
pytest-cov
34+
pytest-json
35+
3436

3537
# for pytest-describe related tests
3638
pytest-describe
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
4+
[tool.coverage.report]
5+
omit = ["test_ignore.py"]
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
4+
def test_to_ignore():
5+
assert True
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
4+
def test_simple():
5+
assert True
6+
7+
8+
def untouched_function():
9+
return 1

python_files/tests/pytestadapter/test_coverage.py

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Copyright (c) Microsoft Corporation. All rights reserved.
22
# Licensed under the MIT License.
3+
import json
34
import os
45
import pathlib
56
import sys
@@ -43,21 +44,21 @@ def test_simple_pytest_coverage():
4344
assert focal_function_coverage.get("lines_covered") is not None
4445
assert focal_function_coverage.get("lines_missed") is not None
4546
assert set(focal_function_coverage.get("lines_covered")) == {4, 5, 7, 9, 10, 11, 12, 13, 14, 17}
46-
assert set(focal_function_coverage.get("lines_missed")) == {18, 19, 6}
47+
assert len(set(focal_function_coverage.get("lines_missed"))) >= 3
4748

4849

49-
coverage_file_path = TEST_DATA_PATH / "coverage_gen" / "coverage.json"
50+
coverage_gen_file_path = TEST_DATA_PATH / "coverage_gen" / "coverage.json"
5051

5152

5253
@pytest.fixture
53-
def cleanup_coverage_file():
54+
def cleanup_coverage_gen_file():
5455
# delete the coverage file if it exists as part of test cleanup
5556
yield
56-
if os.path.exists(coverage_file_path): # noqa: PTH110
57-
os.remove(coverage_file_path) # noqa: PTH107
57+
if os.path.exists(coverage_gen_file_path): # noqa: PTH110
58+
os.remove(coverage_gen_file_path) # noqa: PTH107
5859

5960

60-
def test_coverage_gen_report(cleanup_coverage_file): # noqa: ARG001
61+
def test_coverage_gen_report(cleanup_coverage_gen_file): # noqa: ARG001
6162
"""
6263
Test coverage payload is correct for simple pytest example. Output of coverage run is below.
6364
@@ -73,6 +74,7 @@ def test_coverage_gen_report(cleanup_coverage_file): # noqa: ARG001
7374
args = ["--cov-report=json"]
7475
env_add = {"COVERAGE_ENABLED": "True"}
7576
cov_folder_path = TEST_DATA_PATH / "coverage_gen"
77+
print("cov_folder_path", cov_folder_path)
7678
actual = runner_with_cwd_env(args, cov_folder_path, env_add)
7779
assert actual
7880
coverage = actual[-1]
@@ -87,4 +89,40 @@ def test_coverage_gen_report(cleanup_coverage_file): # noqa: ARG001
8789
assert set(focal_function_coverage.get("lines_covered")) == {4, 5, 7, 9, 10, 11, 12, 13, 14, 17}
8890
assert set(focal_function_coverage.get("lines_missed")) == {18, 19, 6}
8991
# assert that the coverage file was created at the right path
90-
assert os.path.exists(coverage_file_path) # noqa: PTH110
92+
assert os.path.exists(coverage_gen_file_path) # noqa: PTH110
93+
94+
95+
def test_coverage_w_omit_config():
96+
"""
97+
Test the coverage report generation with omit configuration.
98+
99+
folder structure of coverage_w_config
100+
├── coverage_w_config
101+
│ ├── test_ignore.py
102+
│ ├── test_ran.py
103+
│ └── pyproject.toml
104+
105+
pyproject.toml file with the following content:
106+
[tool.coverage.report]
107+
omit = [
108+
"test_ignore.py",
109+
]
110+
111+
112+
Assertions:
113+
- The coverage report is generated.
114+
- The coverage report contains results.
115+
- Only one file is reported in the coverage results.
116+
"""
117+
env_add = {"COVERAGE_ENABLED": "True"}
118+
cov_folder_path = TEST_DATA_PATH / "coverage_w_config"
119+
print("cov_folder_path", cov_folder_path)
120+
actual = runner_with_cwd_env([], cov_folder_path, env_add)
121+
assert actual
122+
print("actual", json.dumps(actual, indent=2))
123+
coverage = actual[-1]
124+
assert coverage
125+
results = coverage["result"]
126+
assert results
127+
# assert one file is reported and one file (as specified in pyproject.toml) is omitted
128+
assert len(results) == 1

python_files/vscode_pytest/__init__.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -442,17 +442,27 @@ def pytest_sessionfinish(session, exitstatus):
442442
if is_coverage_run == "True":
443443
# load the report and build the json result to return
444444
import coverage
445-
from coverage.exceptions import NoSource
445+
from coverage import exceptions
446446

447447
cov = coverage.Coverage()
448448
cov.load()
449449

450450
file_set: set[str] = cov.get_data().measured_files()
451451
file_coverage_map: dict[str, FileCoverageInfo] = {}
452+
453+
# remove files omitted per coverage report config if any
454+
omit_files = cov.config.report_omit
455+
if omit_files:
456+
omit_files = set(omit_files)
457+
# convert to absolute paths, check against file set
458+
omit_files = {os.fspath(pathlib.Path(file).absolute()) for file in omit_files}
459+
print("Files to omit from reporting", omit_files)
460+
file_set = file_set - omit_files
461+
452462
for file in file_set:
453463
try:
454464
analysis = cov.analysis2(file)
455-
except NoSource:
465+
except exceptions.NoSource:
456466
# as per issue 24308 this best way to handle this edge case
457467
continue
458468
lines_executable = {int(line_no) for line_no in analysis[1]}

0 commit comments

Comments
 (0)