Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 7d293c7

Browse filesBrowse files
Implement django coverage.
1 parent 6a60c92 commit 7d293c7
Copy full SHA for 7d293c7

File tree

Expand file treeCollapse file tree

4 files changed

+71
-24
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

4 files changed

+71
-24
lines changed
Open diff view settings
Collapse file

‎build/test-requirements.txt‎

Copy file name to clipboardExpand all lines: build/test-requirements.txt
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ django-stubs
3232
coverage
3333
pytest-cov
3434
pytest-json
35+
pytest-timeout
3536

3637

3738
# for pytest-describe related tests
Collapse file

‎python_files/tests/pytestadapter/helpers.py‎

Copy file name to clipboardExpand all lines: python_files/tests/pytestadapter/helpers.py
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ def runner_with_cwd_env(
244244
"""
245245
process_args: List[str]
246246
pipe_name: str
247-
if "MANAGE_PY_PATH" in env_add:
247+
if "MANAGE_PY_PATH" in env_add and "COVERAGE_ENABLED" not in env_add:
248248
# If we are running Django, generate a unittest-specific pipe name.
249249
process_args = [sys.executable, *args]
250250
pipe_name = generate_random_pipe_name("unittest-discovery-test")
Collapse file

‎python_files/tests/unittestadapter/test_coverage.py‎

Copy file name to clipboardExpand all lines: python_files/tests/unittestadapter/test_coverage.py
+40Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import pathlib
99
import sys
1010

11+
import pytest
12+
1113
sys.path.append(os.fspath(pathlib.Path(__file__).parent))
1214

1315
python_files_path = pathlib.Path(__file__).parent.parent.parent
@@ -49,3 +51,41 @@ def test_basic_coverage():
4951
assert focal_function_coverage.get("lines_missed") is not None
5052
assert set(focal_function_coverage.get("lines_covered")) == {4, 5, 7, 9, 10, 11, 12, 13, 14}
5153
assert set(focal_function_coverage.get("lines_missed")) == {6}
54+
55+
56+
@pytest.mark.timeout(30)
57+
def test_basic_django_coverage():
58+
"""This test validates that the coverage is correctly calculated for a Django project."""
59+
data_path: pathlib.Path = TEST_DATA_PATH / "simple_django"
60+
manage_py_path: str = os.fsdecode(data_path / "manage.py")
61+
execution_script: pathlib.Path = python_files_path / "unittestadapter" / "execution.py"
62+
63+
test_ids = [
64+
"polls.tests.QuestionModelTests.test_was_published_recently_with_future_question",
65+
"polls.tests.QuestionModelTests.test_was_published_recently_with_future_question_2",
66+
"polls.tests.QuestionModelTests.test_question_creation_and_retrieval",
67+
]
68+
69+
script_str = os.fsdecode(execution_script)
70+
actual = helpers.runner_with_cwd_env(
71+
[script_str, "--udiscovery", "-p", "*test*.py", *test_ids],
72+
data_path,
73+
{
74+
"MANAGE_PY_PATH": manage_py_path,
75+
"_TEST_VAR_UNITTEST": "True",
76+
"COVERAGE_ENABLED": os.fspath(data_path),
77+
},
78+
)
79+
80+
assert actual
81+
coverage = actual[-1]
82+
assert coverage
83+
results = coverage["result"]
84+
assert results
85+
assert len(results) == 15
86+
polls_views_coverage = results.get(str(data_path / "polls" / "views.py"))
87+
assert polls_views_coverage
88+
assert polls_views_coverage.get("lines_covered") is not None
89+
assert polls_views_coverage.get("lines_missed") is not None
90+
assert set(polls_views_coverage.get("lines_covered")) == {3, 4, 6}
91+
assert set(polls_views_coverage.get("lines_missed")) == {7}
Collapse file

‎python_files/unittestadapter/django_handler.py‎

Copy file name to clipboardExpand all lines: python_files/unittestadapter/django_handler.py
+29-23Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
# Copyright (c) Microsoft Corporation. All rights reserved.
22
# Licensed under the MIT License.
33

4+
import importlib.util
45
import os
56
import pathlib
67
import subprocess
78
import sys
8-
from typing import List
9+
from contextlib import contextmanager, suppress
10+
from typing import Generator, List
911

1012
script_dir = pathlib.Path(__file__).parent
1113
sys.path.append(os.fspath(script_dir))
@@ -16,6 +18,17 @@
1618
)
1719

1820

21+
@contextmanager
22+
def override_argv(argv: List[str]) -> Generator:
23+
"""Context manager to temporarily override sys.argv with the provided arguments."""
24+
original_argv = sys.argv
25+
sys.argv = argv
26+
try:
27+
yield
28+
finally:
29+
sys.argv = original_argv
30+
31+
1932
def django_discovery_runner(manage_py_path: str, args: List[str]) -> None:
2033
# Attempt a small amount of validation on the manage.py path.
2134
if not pathlib.Path(manage_py_path).exists():
@@ -72,31 +85,24 @@ def django_execution_runner(manage_py_path: str, test_ids: List[str], args: List
7285
else:
7386
env["PYTHONPATH"] = os.fspath(custom_test_runner_dir)
7487

75-
# Build command to run 'python manage.py test'.
76-
command: List[str] = [
77-
sys.executable,
88+
django_project_dir: pathlib.Path = pathlib.Path(manage_py_path).parent
89+
sys.path.insert(0, os.fspath(django_project_dir))
90+
print(f"Django project directory: {django_project_dir}")
91+
92+
manage_spec = importlib.util.spec_from_file_location("manage", manage_py_path)
93+
manage_module = importlib.util.module_from_spec(manage_spec)
94+
manage_spec.loader.exec_module(manage_module)
95+
96+
manage_argv: List[str] = [
7897
manage_py_path,
7998
"test",
8099
"--testrunner=django_test_runner.CustomExecutionTestRunner",
100+
*args,
101+
*test_ids,
81102
]
82-
# Add any additional arguments to the command provided by the user.
83-
command.extend(args)
84-
# Add the test_ids to the command.
85-
print("Test IDs: ", test_ids)
86-
print("args: ", args)
87-
command.extend(test_ids)
88-
print("Running Django run tests with command: ", command)
89-
subprocess_execution = subprocess.run(
90-
command,
91-
capture_output=True,
92-
text=True,
93-
env=env,
94-
)
95-
print(subprocess_execution.stderr, file=sys.stderr)
96-
print(subprocess_execution.stdout, file=sys.stdout)
97-
# Zero return code indicates success, 1 indicates test failures, so both are considered successful.
98-
if subprocess_execution.returncode not in (0, 1):
99-
error_msg = "Django test execution process exited with non-zero error code See stderr above for more details."
100-
print(error_msg, file=sys.stderr)
103+
print(f"Django manage.py arguments: {manage_argv}")
104+
105+
with override_argv(manage_argv), suppress(SystemExit):
106+
manage_module.main()
101107
except Exception as e:
102108
print(f"Error during Django test execution: {e}", file=sys.stderr)

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.