Skip to content

Commit 81b8ba7

Browse files
authored
Merge branch 'main' into luabud-patch-1
2 parents c5a1fe2 + bb6e909 commit 81b8ba7

File tree

6 files changed

+99
-27
lines changed

6 files changed

+99
-27
lines changed

build/azure-pipeline.stable.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ extends:
128128
project: 'Monaco'
129129
definition: 593
130130
buildVersionToDownload: 'latestFromBranch'
131-
branchName: 'refs/heads/release/2025.2'
131+
branchName: 'refs/heads/release/2025.4'
132132
targetPath: '$(Build.SourcesDirectory)/python-env-tools/bin'
133133
artifactName: 'bin-$(buildTarget)'
134134
itemPattern: |

build/test-requirements.txt

Lines 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

python_files/tests/pytestadapter/helpers.py

Lines 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")
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#!/usr/bin/env python
2+
import os
3+
import sys
4+
if __name__ == "__main__":
5+
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings')
6+
try:
7+
from django.core.management import execute_from_command_line
8+
except ImportError:
9+
# The above import may fail for some other reason. Ensure that the
10+
# issue is really that Django is missing to avoid masking other
11+
# exceptions on Python 2.
12+
try:
13+
import django
14+
except ImportError:
15+
raise ImportError(
16+
"Couldn't import Django. Are you sure it's installed and "
17+
"available on your PYTHONPATH environment variable? Did you "
18+
"forget to activate a virtual environment?"
19+
)
20+
raise
21+
execute_from_command_line(sys.argv)

python_files/tests/unittestadapter/test_coverage.py

Lines changed: 41 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,42 @@ 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.parametrize("manage_py_file", ["manage.py", "old_manage.py"])
57+
@pytest.mark.timeout(30)
58+
def test_basic_django_coverage(manage_py_file):
59+
"""This test validates that the coverage is correctly calculated for a Django project."""
60+
data_path: pathlib.Path = TEST_DATA_PATH / "simple_django"
61+
manage_py_path: str = os.fsdecode(data_path / manage_py_file)
62+
execution_script: pathlib.Path = python_files_path / "unittestadapter" / "execution.py"
63+
64+
test_ids = [
65+
"polls.tests.QuestionModelTests.test_was_published_recently_with_future_question",
66+
"polls.tests.QuestionModelTests.test_was_published_recently_with_future_question_2",
67+
"polls.tests.QuestionModelTests.test_question_creation_and_retrieval",
68+
]
69+
70+
script_str = os.fsdecode(execution_script)
71+
actual = helpers.runner_with_cwd_env(
72+
[script_str, "--udiscovery", "-p", "*test*.py", *test_ids],
73+
data_path,
74+
{
75+
"MANAGE_PY_PATH": manage_py_path,
76+
"_TEST_VAR_UNITTEST": "True",
77+
"COVERAGE_ENABLED": os.fspath(data_path),
78+
},
79+
)
80+
81+
assert actual
82+
coverage = actual[-1]
83+
assert coverage
84+
results = coverage["result"]
85+
assert results
86+
assert len(results) == 16
87+
polls_views_coverage = results.get(str(data_path / "polls" / "views.py"))
88+
assert polls_views_coverage
89+
assert polls_views_coverage.get("lines_covered") is not None
90+
assert polls_views_coverage.get("lines_missed") is not None
91+
assert set(polls_views_coverage.get("lines_covered")) == {3, 4, 6}
92+
assert set(polls_views_coverage.get("lines_missed")) == {7}

python_files/unittestadapter/django_handler.py

Lines changed: 34 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
import pathlib
66
import subprocess
77
import sys
8-
from typing import List
8+
from contextlib import contextmanager, suppress
9+
from typing import Generator, List
910

1011
script_dir = pathlib.Path(__file__).parent
1112
sys.path.append(os.fspath(script_dir))
@@ -16,6 +17,17 @@
1617
)
1718

1819

20+
@contextmanager
21+
def override_argv(argv: List[str]) -> Generator:
22+
"""Context manager to temporarily override sys.argv with the provided arguments."""
23+
original_argv = sys.argv
24+
sys.argv = argv
25+
try:
26+
yield
27+
finally:
28+
sys.argv = original_argv
29+
30+
1931
def django_discovery_runner(manage_py_path: str, args: List[str]) -> None:
2032
# Attempt a small amount of validation on the manage.py path.
2133
if not pathlib.Path(manage_py_path).exists():
@@ -58,8 +70,9 @@ def django_discovery_runner(manage_py_path: str, args: List[str]) -> None:
5870

5971

6072
def django_execution_runner(manage_py_path: str, test_ids: List[str], args: List[str]) -> None:
73+
manage_path: pathlib.Path = pathlib.Path(manage_py_path)
6174
# Attempt a small amount of validation on the manage.py path.
62-
if not pathlib.Path(manage_py_path).exists():
75+
if not manage_path.exists():
6376
raise VSCodeUnittestError("Error running Django, manage.py path does not exist.")
6477

6578
try:
@@ -72,31 +85,27 @@ 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,
78-
manage_py_path,
88+
django_project_dir: pathlib.Path = manage_path.parent
89+
sys.path.insert(0, os.fspath(django_project_dir))
90+
print(f"Django project directory: {django_project_dir}")
91+
92+
manage_argv: List[str] = [
93+
str(manage_path),
7994
"test",
8095
"--testrunner=django_test_runner.CustomExecutionTestRunner",
96+
*args,
97+
*test_ids,
8198
]
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)
99+
print(f"Django manage.py arguments: {manage_argv}")
100+
101+
try:
102+
argv_context = override_argv(manage_argv)
103+
suppress_context = suppress(SystemExit)
104+
manage_file = manage_path.open()
105+
with argv_context, suppress_context, manage_file:
106+
manage_code = manage_file.read()
107+
exec(manage_code, {"__name__": "__main__"})
108+
except OSError as e:
109+
raise VSCodeUnittestError("Error running Django, unable to read manage.py") from e
101110
except Exception as e:
102111
print(f"Error during Django test execution: {e}", file=sys.stderr)

0 commit comments

Comments
 (0)