|
1 | 1 | # Copyright (c) Microsoft Corporation. All rights reserved.
|
2 | 2 | # Licensed under the MIT License.
|
3 | 3 |
|
| 4 | +import importlib.util |
4 | 5 | import os
|
5 | 6 | import pathlib
|
6 | 7 | import subprocess
|
7 | 8 | import sys
|
8 |
| -from typing import List |
| 9 | +from contextlib import contextmanager, suppress |
| 10 | +from typing import TYPE_CHECKING, Generator, List |
| 11 | + |
| 12 | +if TYPE_CHECKING: |
| 13 | + from importlib.machinery import ModuleSpec |
| 14 | + |
9 | 15 |
|
10 | 16 | script_dir = pathlib.Path(__file__).parent
|
11 | 17 | sys.path.append(os.fspath(script_dir))
|
|
16 | 22 | )
|
17 | 23 |
|
18 | 24 |
|
| 25 | +@contextmanager |
| 26 | +def override_argv(argv: List[str]) -> Generator: |
| 27 | + """Context manager to temporarily override sys.argv with the provided arguments.""" |
| 28 | + original_argv = sys.argv |
| 29 | + sys.argv = argv |
| 30 | + try: |
| 31 | + yield |
| 32 | + finally: |
| 33 | + sys.argv = original_argv |
| 34 | + |
| 35 | + |
19 | 36 | def django_discovery_runner(manage_py_path: str, args: List[str]) -> None:
|
20 | 37 | # Attempt a small amount of validation on the manage.py path.
|
21 | 38 | if not pathlib.Path(manage_py_path).exists():
|
@@ -72,31 +89,28 @@ def django_execution_runner(manage_py_path: str, test_ids: List[str], args: List
|
72 | 89 | else:
|
73 | 90 | env["PYTHONPATH"] = os.fspath(custom_test_runner_dir)
|
74 | 91 |
|
75 |
| - # Build command to run 'python manage.py test'. |
76 |
| - command: List[str] = [ |
77 |
| - sys.executable, |
| 92 | + django_project_dir: pathlib.Path = pathlib.Path(manage_py_path).parent |
| 93 | + sys.path.insert(0, os.fspath(django_project_dir)) |
| 94 | + print(f"Django project directory: {django_project_dir}") |
| 95 | + |
| 96 | + manage_spec: ModuleSpec | None = importlib.util.spec_from_file_location( |
| 97 | + "manage", manage_py_path |
| 98 | + ) |
| 99 | + if manage_spec is None or manage_spec.loader is None: |
| 100 | + raise VSCodeUnittestError("Error importing manage.py when running Django testing.") |
| 101 | + manage_module = importlib.util.module_from_spec(manage_spec) |
| 102 | + manage_spec.loader.exec_module(manage_module) |
| 103 | + |
| 104 | + manage_argv: List[str] = [ |
78 | 105 | manage_py_path,
|
79 | 106 | "test",
|
80 | 107 | "--testrunner=django_test_runner.CustomExecutionTestRunner",
|
| 108 | + *args, |
| 109 | + *test_ids, |
81 | 110 | ]
|
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) |
| 111 | + print(f"Django manage.py arguments: {manage_argv}") |
| 112 | + |
| 113 | + with override_argv(manage_argv), suppress(SystemExit): |
| 114 | + manage_module.main() |
101 | 115 | except Exception as e:
|
102 | 116 | print(f"Error during Django test execution: {e}", file=sys.stderr)
|
0 commit comments