forked from microsoft/vscode-python
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathdiscovery.py
More file actions
158 lines (132 loc) · 5.48 KB
/
discovery.py
File metadata and controls
158 lines (132 loc) · 5.48 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.
import os
import pathlib
import sys
import traceback
import unittest
from typing import List, Optional
script_dir = pathlib.Path(__file__).parent
sys.path.append(os.fspath(script_dir))
from django_handler import django_discovery_runner # noqa: E402
# If I use from utils then there will be an import error in test_discovery.py.
from unittestadapter.pvsc_utils import ( # noqa: E402
DiscoveryPayloadDict,
VSCodeUnittestError,
build_test_tree,
parse_unittest_args,
send_post_request,
)
def discover_tests(
start_dir: str,
pattern: str,
top_level_dir: Optional[str],
cwd_override: Optional[str] = None,
) -> DiscoveryPayloadDict:
"""Returns a dictionary containing details of the discovered tests.
The returned dict has the following keys:
- cwd: Absolute path to the test start directory (or cwd_override if provided);
- status: Test discovery status, can be "success" or "error";
- tests: Discoverered tests if any, not present otherwise. Note that the status can be "error" but the payload can still contain tests;
- error: Discovery error if any, not present otherwise.
Payload format for a successful discovery:
{
"status": "success",
"cwd": <test discovery directory>,
"tests": <test tree>
}
Payload format for a successful discovery with no tests:
{
"status": "success",
"cwd": <test discovery directory>,
}
Payload format when there are errors:
{
"cwd": <test discovery directory>
"": [list of errors]
"status": "error",
}
Args:
start_dir: Directory where test discovery starts
pattern: Pattern to match test files (e.g., "test*.py")
top_level_dir: Top-level directory for the test tree hierarchy
cwd_override: Optional override for the cwd in the response payload
(used for project-based testing to set project root)
"""
cwd = os.path.abspath(cwd_override if cwd_override else start_dir) # noqa: PTH100
if "/" in start_dir: # is a subdir
parent_dir = os.path.dirname(start_dir) # noqa: PTH120
sys.path.insert(0, parent_dir)
else:
sys.path.insert(0, cwd)
payload: DiscoveryPayloadDict = {"cwd": cwd, "status": "success", "tests": None}
tests = None
error: List[str] = []
try:
loader = unittest.TestLoader()
suite = loader.discover(start_dir, pattern, top_level_dir)
# If the top level directory is not provided, then use the start directory.
if top_level_dir is None:
top_level_dir = start_dir
# Get abspath of top level directory for build_test_tree.
top_level_dir = os.path.abspath(top_level_dir) # noqa: PTH100
tests, error = build_test_tree(suite, top_level_dir) # test tree built successfully here.
except Exception:
error.append(traceback.format_exc())
# Only include tests in the payload if tests were discovered.
# If no tests found (tests is None), omit the tests key per the docstring contract.
if tests is not None:
payload["tests"] = tests
if len(error):
payload["status"] = "error"
payload["error"] = error
return payload
if __name__ == "__main__":
# Get unittest discovery arguments.
argv = sys.argv[1:]
index = argv.index("--udiscovery")
(
start_dir,
pattern,
top_level_dir,
_verbosity,
_failfast,
_locals,
) = parse_unittest_args(argv[index + 1 :])
test_run_pipe = os.getenv("TEST_RUN_PIPE")
if not test_run_pipe:
error_msg = (
"UNITTEST ERROR: TEST_RUN_PIPE is not set at the time of unittest trying to send data. "
"Please confirm this environment variable is not being changed or removed "
"as it is required for successful test discovery and execution."
f"TEST_RUN_PIPE = {test_run_pipe}\n"
)
print(error_msg, file=sys.stderr)
raise VSCodeUnittestError(error_msg)
if manage_py_path := os.environ.get("MANAGE_PY_PATH"):
# Django configuration requires manage.py path to enable.
print(
f"MANAGE_PY_PATH is set, running Django discovery with path to manage.py as: ${manage_py_path}"
)
try:
# collect args for Django discovery runner.
args = argv[index + 1 :] or []
django_discovery_runner(manage_py_path, args)
except Exception as e:
error_msg = f"Error configuring Django test runner: {e}"
print(error_msg, file=sys.stderr)
raise VSCodeUnittestError(error_msg) # noqa: B904
else:
# Check for PROJECT_ROOT_PATH environment variable (project-based testing).
# When set, this overrides top_level_dir to root the test tree at the project directory.
project_root_path = os.environ.get("PROJECT_ROOT_PATH")
if project_root_path:
top_level_dir = project_root_path
print(
f"PROJECT_ROOT_PATH is set, using {project_root_path} as top_level_dir for discovery"
)
# Perform regular unittest test discovery.
# Pass project_root_path as cwd_override so the payload's cwd matches the project root.
payload = discover_tests(start_dir, pattern, top_level_dir, cwd_override=project_root_path)
# Post this discovery payload.
send_post_request(payload, test_run_pipe)