Skip to content

Commit c59eb26

Browse files
committed
implement django tests with custom runner
1 parent 9b5f58a commit c59eb26

File tree

4 files changed

+159
-8
lines changed

4 files changed

+159
-8
lines changed
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import subprocess
2+
import os
3+
import pathlib
4+
import re
5+
import sys
6+
7+
8+
def find_settings_module(path_to_manage_py):
9+
dj_settings_module = None
10+
with open(path_to_manage_py, "r") as manage_py:
11+
pattern = r"^os\.environ\.setdefault\((['\"])(DJANGO_SETTINGS_MODULE)\1, (['\"])(?P<settings_path>[\w.]+)\3\)$"
12+
for line in manage_py.readlines():
13+
match_result = re.match(pattern, line.strip())
14+
if match_result is not None:
15+
dj_settings_module = match_result.groupdict().get("settings_path", None)
16+
break
17+
return dj_settings_module
18+
19+
20+
def configure_test_runner(path_to_manage_py):
21+
# Getting the DJANGO_SETTINGS_MODULE from manage.py
22+
dj_settings_module = find_settings_module(path_to_manage_py)
23+
if dj_settings_module is None:
24+
raise Exception("DJANGO_SETTINGS_MODULE not found in manage.py")
25+
26+
# Construct the path to the settings.py file
27+
settings_file = os.path.join(
28+
os.path.dirname(dj_settings_module.replace(".", os.sep)), "settings.py"
29+
)
30+
# Check if the settings.py file exists
31+
if not os.path.exists(settings_file):
32+
raise Exception(f"settings.py file not found at {settings_file}")
33+
# Read the content of the existing settings.py file
34+
with open(settings_file, "r") as f:
35+
original_settings_content = f.read()
36+
37+
# Check if TEST_RUNNER is already defined in the settings
38+
if "TEST_RUNNER" in original_settings_content:
39+
print("TEST_RUNNER is already defined in settings.py. but continuing")
40+
print("settings_content: ", original_settings_content)
41+
else:
42+
# Add the custom test runner to the settings.py file
43+
44+
# Get path to the custom_test_runner.py parent folder, add to sys.path
45+
custom_test_runner_dir = pathlib.Path(__file__).parent
46+
sys.path.insert(0, custom_test_runner_dir)
47+
48+
# Import your custom test runner class
49+
# from execution import UnittestTestResult
50+
51+
# Set the TEST_RUNNER setting
52+
setting_content = original_settings_content + (
53+
"\n\n"
54+
+ "# Use custom test runner\n"
55+
+ "import sys\n"
56+
+ f"sys.path.insert(0, '{custom_test_runner_dir}')\n"
57+
+ f"TEST_RUNNER = 'django_test_runner.CustomTestRunner'\n"
58+
)
59+
60+
# Write the updated content back to the settings.py file
61+
with open(settings_file, "w") as f:
62+
f.write(setting_content)
63+
64+
print("TEST_RUNNER setting added to settings.py.")
65+
return settings_file, original_settings_content
66+
67+
68+
# Define a cleanup method
69+
def cleanup(settings_file, original_settings_content):
70+
# Restore the original content of settings.py
71+
with open(settings_file, "w") as f:
72+
f.write(original_settings_content)
73+
print("Settings.py has been restored to its original state.")
74+
75+
return True
76+
77+
78+
def runner():
79+
# Define the path to your manage.py file
80+
# could get path to manage.py from environment variable
81+
# get Django test boolean
82+
django_test_enabled = os.environ.get("DJANGO_TEST_ENABLED")
83+
manage_py_path = os.environ.get("MANAGE_PY_PATH")
84+
85+
if (
86+
django_test_enabled is not None
87+
and django_test_enabled.lower() == "true"
88+
and manage_py_path is not None
89+
):
90+
# attempt to configure and run tests as django tests
91+
try:
92+
settings_file, original_settings_content = configure_test_runner(
93+
manage_py_path
94+
)
95+
# Command to run 'python manage.py test'
96+
python_executable = sys.executable
97+
command = [python_executable, "manage.py", "test"]
98+
print("running test command: ", command)
99+
# Run the command
100+
try:
101+
subprocess.run(" ".join(command), shell=True, check=True)
102+
# Cleanup
103+
cleanup(settings_file, original_settings_content)
104+
except subprocess.CalledProcessError as e:
105+
print(f"Error running 'manage.py test': {e}")
106+
except Exception as e:
107+
print(f"Error configuring Django test runner: {e}")
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from django.test.runner import DiscoverRunner
2+
import sys
3+
import os
4+
import pathlib
5+
6+
script_dir = pathlib.Path(__file__).parent
7+
sys.path.append(os.fspath(script_dir))
8+
9+
from execution import UnittestTestResult
10+
11+
12+
class CustomTestRunner(DiscoverRunner):
13+
def get_test_runner_kwargs(self):
14+
print("get_test_runner_kwargs")
15+
kwargs = super().get_test_runner_kwargs()
16+
kwargs["resultclass"] = UnittestTestResult
17+
return kwargs

pythonFiles/unittestadapter/execution.py

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from testing_tools import process_json_util, socket_manager
2121
from typing_extensions import Literal, NotRequired, TypeAlias, TypedDict
2222
from unittestadapter.utils import parse_unittest_args
23+
from django_runner import runner
2324

2425
ErrorType = Union[
2526
Tuple[Type[BaseException], BaseException, TracebackType], Tuple[None, None, None]
@@ -103,7 +104,6 @@ def formatResult(
103104
subtest: Union[unittest.TestCase, None] = None,
104105
):
105106
tb = None
106-
107107
message = ""
108108
# error is a tuple of the form returned by sys.exc_info(): (type, value, traceback).
109109
if error is not None:
@@ -128,9 +128,16 @@ def formatResult(
128128
"subtest": subtest.id() if subtest else None,
129129
}
130130
self.formatted[test_id] = result
131-
if testPort == 0 or testUuid == 0:
132-
print("Error sending response, port or uuid unknown to python server.")
133-
send_run_data(result, testPort, testUuid)
131+
testPort2 = int(os.environ.get("TEST_PORT", DEFAULT_PORT))
132+
testUuid2 = os.environ.get("TEST_UUID")
133+
if testPort2 == 0 or testUuid2 == 0:
134+
print(
135+
"Error sending response, port or uuid unknown to python server.",
136+
testPort,
137+
testUuid,
138+
)
139+
140+
send_run_data(result, testPort2, testUuid2)
134141

135142

136143
class TestExecutionStatus(str, enum.Enum):
@@ -318,9 +325,19 @@ def post_response(
318325
testUuid = "unknown"
319326
if test_ids_from_buffer:
320327
# Perform test execution.
321-
payload = run_tests(
322-
start_dir, test_ids_from_buffer, pattern, top_level_dir, testUuid
323-
)
328+
329+
# get django test boolean
330+
django_test_enabled = os.environ.get("DJANGO_TEST_ENABLED")
331+
print("DJANGO_TEST_ENABLED = ", django_test_enabled)
332+
if django_test_enabled:
333+
# run django runner
334+
print("running django runner")
335+
runner()
336+
else:
337+
print("running unittest runner")
338+
payload = run_tests(
339+
start_dir, test_ids_from_buffer, pattern, top_level_dir, testUuid
340+
)
324341
else:
325342
cwd = os.path.abspath(start_dir)
326343
status = TestExecutionStatus.error

src/client/testing/testController/common/server.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,14 +184,24 @@ export class PythonTestServer implements ITestServer, Disposable {
184184
mutableEnv.TEST_PORT = this.getPort().toString();
185185
mutableEnv.RUN_TEST_IDS_PORT = runTestIdPort;
186186

187+
const isRun = runTestIdPort !== undefined;
188+
189+
// NEEDS TO BE UNCOMMENTED TO GET DJANGO WORKING
190+
// if (isRun) {
191+
// mutableEnv.DJANGO_TEST_ENABLED = 'true';
192+
// mutableEnv.MANAGE_PY_PATH = [options.cwd, 'manage.py'].join('/');
193+
// console.log('DJANGO_TEST_ENABLED', mutableEnv.DJANGO_TEST_ENABLED);
194+
// console.log('MANAGE_PY_PATH', mutableEnv.MANAGE_PY_PATH);
195+
// }
196+
187197
const spawnOptions: SpawnOptions = {
188198
token: options.token,
189199
cwd: options.cwd,
190200
throwOnStdErr: true,
191201
outputChannel: options.outChannel,
192202
env: mutableEnv,
193203
};
194-
const isRun = runTestIdPort !== undefined;
204+
195205
// Create the Python environment in which to execute the command.
196206
const creationOptions: ExecutionFactoryCreateWithEnvironmentOptions = {
197207
allowEnvironmentFetchExceptions: false,

0 commit comments

Comments
 (0)