Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@
if __name__ == "__main__":
args = sys.argv[1:]
manage_py_path = args[0]
test_ids = args[1:]
# currently doesn't support additional args past test_ids.
django_execution_runner(manage_py_path, test_ids, [])
remaining = args[1:]
if "--" in remaining:
separator_index = remaining.index("--")
extra_args = remaining[:separator_index]
test_ids = remaining[separator_index + 1 :]
else:
extra_args = []
test_ids = remaining
django_execution_runner(manage_py_path, test_ids, extra_args)
44 changes: 44 additions & 0 deletions python_files/tests/unittestadapter/test_discovery.py
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,50 @@ def test_simple_django_collect():
)


def test_django_collect_with_default_unittest_args():
"""Django discovery should succeed when passed the default unittest discovery
arguments (-v, -s, -p) that the extension sends when no custom unittestArgs are
configured. See https://github.com/microsoft/vscode-python/issues/24991.
"""
test_data_path: pathlib.Path = pathlib.Path(__file__).parent / ".data"
python_files_path: pathlib.Path = pathlib.Path(__file__).parent.parent.parent
discovery_script_path: str = os.fsdecode(python_files_path / "unittestadapter" / "discovery.py")
data_path: pathlib.Path = test_data_path / "simple_django"
manage_py_path: str = os.fsdecode(pathlib.Path(data_path, "manage.py"))

actual = helpers.runner_with_cwd_env(
[
discovery_script_path,
"--udiscovery",
"-v",
"-s",
".",
"-p",
"test*.py",
],
data_path,
{"MANAGE_PY_PATH": manage_py_path},
)

assert actual
actual_list: List[Dict[str, Any]] = actual
assert actual_list is not None
if actual_list is not None:
actual_item = actual_list.pop(0)
assert all(item in actual_item for item in ("status", "cwd"))
assert actual_item.get("status") == "success", (
f"Status is not 'success', error is: {actual_item.get('error')}"
)
assert actual_item.get("cwd") == os.fspath(data_path)
assert len(actual_item["tests"]["children"]) == 1
assert actual_item["tests"]["children"][0]["children"][0]["id_"] == os.fsdecode(
pathlib.PurePath(test_data_path, "simple_django", "polls", "tests.py")
)
assert (
len(actual_item["tests"]["children"][0]["children"][0]["children"][0]["children"]) == 3
)


def test_project_root_path_with_cwd_override() -> None:
"""Test unittest discovery with project_root_path parameter.

Expand Down
39 changes: 39 additions & 0 deletions python_files/tests/unittestadapter/test_execution.py
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,45 @@ def test_basic_run_django():
assert id_result["outcome"] == "success"


def test_django_run_with_default_unittest_args():
"""Django test execution should succeed when passed the default unittest discovery
arguments (-v, -s, -p) that the extension sends when no custom unittestArgs are
configured. See https://github.com/microsoft/vscode-python/issues/24991.
"""
data_path: pathlib.Path = TEST_DATA_PATH / "simple_django"
manage_py_path: str = os.fsdecode(data_path / "manage.py")
execution_script: pathlib.Path = (
pathlib.Path(__file__).parent / "django_test_execution_script.py"
)

test_ids = [
"polls.tests.QuestionModelTests.test_was_published_recently_with_future_question",
]
script_str = os.fsdecode(execution_script)
actual = helpers.runner_with_cwd_env(
[
script_str,
manage_py_path,
"-v",
"-s",
".",
"-p",
"test*.py",
"--",
*test_ids,
],
data_path,
{"MANAGE_PY_PATH": manage_py_path},
)
assert actual
actual_list: List[Dict[str, Dict[str, Any]]] = actual
assert len(actual_list) == 1
actual_item = actual_list[0]
assert all(item in actual_item for item in ("status", "cwd", "result"))
assert actual_item.get("cwd") == os.fspath(data_path)
assert actual_item["result"][test_ids[0]]["outcome"] == "success"


def test_project_root_path_with_cwd_override(mock_send_run_data) -> None: # noqa: ARG001
"""Test unittest execution with project_root_path parameter.

Expand Down
41 changes: 39 additions & 2 deletions python_files/unittestadapter/django_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,43 @@ def override_argv(argv: List[str]) -> Generator:
sys.argv = original_argv


_START_DIRECTORY_FLAGS = ("-s", "--start-directory")
_VERBOSE_FLAGS = ("-v", "--verbose")
_QUIET_FLAGS = ("-q", "--quiet")


def _to_manage_py_args(args: List[str]) -> List[str]:
"""Adapt unittest discovery/execution args for 'manage.py test'.

Most unittest discovery flags (-p/--pattern, -t/--top-level-directory,
-f/--failfast, ...) are understood by Django's 'manage.py test' the same
way, so they -- and any Django-specific options a user adds to
unittestArgs, e.g. --keepdb, --settings -- are passed through unchanged.
Only two flags need adapting:
- -s/--start-directory has no Django equivalent, since Django discovers
tests across installed apps rather than a start directory, so it (and
its value) is dropped.
- -v/--verbose and -q/--quiet are value-less unittest flags, but Django's
-v/--verbosity requires an explicit value, so they're translated to an
explicit "-v 2"/"-v 0".
"""
manage_py_args: List[str] = []
skip_next = False
for arg in args:
if skip_next:
skip_next = False
continue
if arg in _START_DIRECTORY_FLAGS:
skip_next = True
elif arg in _VERBOSE_FLAGS:
manage_py_args.extend(["-v", "2"])
elif arg in _QUIET_FLAGS:
manage_py_args.extend(["-v", "0"])
else:
manage_py_args.append(arg)
return manage_py_args


def django_discovery_runner(manage_py_path: str, args: List[str]) -> None:
# Attempt a small amount of validation on the manage.py path.
if not pathlib.Path(manage_py_path).exists():
Expand All @@ -50,7 +87,7 @@ def django_discovery_runner(manage_py_path: str, args: List[str]) -> None:
"test",
"--testrunner=django_test_runner.CustomDiscoveryTestRunner",
]
command.extend(args)
command.extend(_to_manage_py_args(args))
print("Running Django tests with command:", command)

subprocess_discovery = subprocess.run(
Expand Down Expand Up @@ -93,7 +130,7 @@ def django_execution_runner(manage_py_path: str, test_ids: List[str], args: List
str(manage_path),
"test",
"--testrunner=django_test_runner.CustomExecutionTestRunner",
*args,
*_to_manage_py_args(args),
*test_ids,
]
print(f"Django manage.py arguments: {manage_argv}")
Expand Down