From a40bdc99f335db101b5bb8780792307983d7b10e Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Tue, 6 Aug 2024 14:30:16 -0700 Subject: [PATCH 01/17] working so far --- python_files/unittestadapter/discovery.py | 57 +++++++++++++ python_files/unittestadapter/django_new.py | 34 ++++++++ python_files/unittestadapter/django_runner.py | 81 +++++++++++++++++++ .../unittestadapter/django_test_runner.py | 81 +++++++++++++++++++ python_files/unittestadapter/execution.py | 31 ++++--- 5 files changed, 274 insertions(+), 10 deletions(-) create mode 100644 python_files/unittestadapter/django_new.py create mode 100644 python_files/unittestadapter/django_runner.py create mode 100644 python_files/unittestadapter/django_test_runner.py diff --git a/python_files/unittestadapter/discovery.py b/python_files/unittestadapter/discovery.py index 604fe7beaeb1..1dd07d301c76 100644 --- a/python_files/unittestadapter/discovery.py +++ b/python_files/unittestadapter/discovery.py @@ -1,6 +1,7 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. +import subprocess import os import pathlib import sys @@ -8,6 +9,17 @@ import unittest from typing import List, Optional +script_dir = pathlib.Path(__file__).parent.parent +sys.path.append(os.fspath(script_dir)) +sys.path.insert(0, os.fspath(script_dir / "lib" / "python")) + +from django_runner import django_execution_runner # noqa: E402 + + +print(sys.path) +sys.path.append("/Users/eleanorboyd/vscode-python/.venv/lib/python3.10/site-packages") +import debugpy + script_dir = pathlib.Path(__file__).parent.parent sys.path.append(os.fspath(script_dir)) @@ -21,6 +33,9 @@ send_post_request, ) +debugpy.connect(5678) +debugpy.breakpoint() + def discover_tests( start_dir: str, @@ -118,6 +133,48 @@ def discover_tests( print(error_msg, file=sys.stderr) raise VSCodeUnittestError(error_msg) + # if django build discovery run command for this + # CustomTestRunner2 + # manage_py_path = os.environ.get("MANAGE_PY_PATH") + # print("DJANGO_TEST_ENABLED = ", manage_py_path) + # payload = None + if True: + # # run django runner + # print("running django runner") + # custom_test_runner_dir = pathlib.Path(__file__).parent + # sys.path.insert(0, custom_test_runner_dir) + # subprocess.run( + # [ + # sys.executable, + # "manage.py", + # "test", + # "--testrunner", + # "django_new.CustomTestRunner2", + # ], + # check=True, + # ) + try: + custom_test_runner_dir = pathlib.Path(__file__).parent + sys.path.append(custom_test_runner_dir) + custom_test_runner = "django_test_runner.CustomTestRunner" + + django_execution_runner(start_dir) + + # Build command to run 'python manage.py test'. + python_executable = sys.executable + command = [ + python_executable, + "manage.py", # switch for manage.py path + "test", + "--testrunner=${custom_test_runner}", + "--verbosity=2", + ] + subprocess.run(" ".join(command), shell=True, check=True) + except Exception as e: + error_msg = "Error configuring Django test runner: {e}" + print(error_msg, file=sys.stderr) + raise VSCodeUnittestError(error_msg) + # else: # Perform test discovery. payload = discover_tests(start_dir, pattern, top_level_dir) # Post this discovery payload. diff --git a/python_files/unittestadapter/django_new.py b/python_files/unittestadapter/django_new.py new file mode 100644 index 000000000000..377e990e9d3c --- /dev/null +++ b/python_files/unittestadapter/django_new.py @@ -0,0 +1,34 @@ +from django.test.runner import DiscoverRunner + + +class CustomTestRunner2(DiscoverRunner): + def run_tests(self, test_labels, extra_tests=None, **kwargs): + # Set up the test environment + self.setup_test_environment() + + # Set up the test databases + old_config = self.setup_databases() + + # Call the default build_suite method to create the test suite + suite = self.build_suite(test_labels, extra_tests, **kwargs) + + # Print out the test suite + test_names = [str(test) for test in suite] + for name in sorted(test_names): + print(name) + + # Optionally, prevent the tests from running by returning early + # return 0 # Uncomment this line to skip test execution + + # Run the tests normally + result = self.test_runner( + verbosity=self.verbosity, failfast=self.failfast, keepdb=self.keepdb + ).run(suite) + + # Tear down the test databases + self.teardown_databases(old_config) + + # Tear down the test environment + self.teardown_test_environment() + + return self.suite_result(suite, result) diff --git a/python_files/unittestadapter/django_runner.py b/python_files/unittestadapter/django_runner.py new file mode 100644 index 000000000000..c7c01a9217e0 --- /dev/null +++ b/python_files/unittestadapter/django_runner.py @@ -0,0 +1,81 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +import os +import pathlib +import subprocess +import sys +from typing import Union +import traceback + +script_dir = pathlib.Path(__file__).parent.parent +sys.path.append(os.fspath(script_dir)) +sys.path.insert(0, os.fspath(script_dir / "lib" / "python")) + +from pvsc_utils import ( # noqa: E402 + VSCodeUnittestError, +) + + +def django_execution_runner(start_dir: Union[str, None]) -> None: + # Get path to manage.py if set as an env var, otherwise use the default + manage_py_path = os.environ.get("MANAGE_PY_PATH") + + # if manage_py_path is None: + # # Search for default manage.py path at the root of the workspace + # if not start_dir: + # print("Error running Django, no start_dir provided or value for MANAGE_PY_PATH") + + # cwd = pathlib.Path.resolve(start_dir) + # manage_py_path = cwd / "manage.py" + + try: + # Get path to the custom_test_runner.py parent folder, add to sys.path. + custom_test_runner_dir = pathlib.Path(__file__).parent + sys.path.insert(0, custom_test_runner_dir) + custom_test_runner_dir2 = pathlib.Path(__file__).parent.parent + sys.path.insert(0, custom_test_runner_dir2) + custom_test_runner = "django_test_runner.CustomTestRunner" + + # Build command to run 'python manage.py test'. + python_executable = sys.executable + command = [ + python_executable, + "/Users/eleanorboyd/testingFiles/django-polls/manage.py", # switch for manage.py path + "test", + "--testrunner=django_test_runner.CustomTestRunner", + "--verbosity=3", + ] + print("Running Django run tests with command: ", command) + env = os.environ.copy() + if "PYTHONPATH" in env: + env["PYTHONPATH"] = ( + custom_test_runner_dir + + os.pathsep + + custom_test_runner_dir2 + + os.pathsep + + env["PYTHONPATH"] + ) + else: + env["PYTHONPATH"] = ( + "/Users/eleanorboyd/vscode-python/python_files/unittestadapter" + + os.pathsep + + "/Users/eleanorboyd/vscode-python/python_files" + ) + try: + abc = subprocess.run( + command, + capture_output=True, + text=True, + env=env, + ) + print(abc.stderr) + print(abc.stdout) + print("Django tests ran successfully.") + except subprocess.CalledProcessError as e: + print(f"Error running 'manage.py test': {e}") + print(traceback.format_exc()) + raise VSCodeUnittestError(f"Error running 'manage.py test': {e}") # noqa: B904 + except Exception as e: + print(f"Error configuring Django test runner: {e}") + raise VSCodeUnittestError(f"Error configuring Django test runner: {e}") # noqa: B904 diff --git a/python_files/unittestadapter/django_test_runner.py b/python_files/unittestadapter/django_test_runner.py new file mode 100644 index 000000000000..fb9903a2d7bf --- /dev/null +++ b/python_files/unittestadapter/django_test_runner.py @@ -0,0 +1,81 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + + +import os +import pathlib +import sys +from unittest import TestSuite, TextTestResult + +from django.test.runner import DiscoverRunner +# from execution import UnittestTestResult + +script_dir = pathlib.Path(__file__).parent +sys.path.append(os.fspath(script_dir)) + +print("HELLO!!!") + + +class CustomTestRunner(DiscoverRunner): + def get_test_runner_kwargs(self): + print("get_test_runner_kwargs") + kwargs = super().get_test_runner_kwargs() + if kwargs["resultclass"] is not None: + raise ValueError( + "Resultclass already set, cannot use custom test runner design for VS Code compatibility." + ) + # kwargs["resultclass"] = UnittestTestResult + return kwargs + + def build_suite(self, test_labels, extra_tests=None, **kwargs): + print("build_suite") + suite = super().build_suite(test_labels) + return suite + + def run_suite(self, suite: TestSuite, **kwargs) -> TextTestResult: + print("EJFB HELLO!!!") + return super().run_suite(suite, **kwargs) + + +# import unittest +# from django.test.runner import DiscoverRunner + + +# class CustomTestRunner(DiscoverRunner): +# def run_tests(self, test_labels, extra_tests=None, **kwargs): +# try: +# # Set up the test environment +# print("Setting up test environment...") +# self.setup_test_environment() + +# # Set up the test databases +# print("Setting up test databases...") +# old_config = self.setup_databases() + +# # Call the default build_suite method to create the test suite +# print("Building test suite...") +# print(f"test_labels: {test_labels}") +# print(f"extra_tests: {extra_tests}") +# suite = self.build_suite(test_labels) + +# # Print out the test suite +# test_names = [str(test) for test in suite] +# for name in sorted(test_names): +# print(name) + +# # Run the tests normally +# print("Running tests...") +# result = self.test_runner(verbosity=self.verbosity).run(suite) + +# # Tear down the test databases +# print("Tearing down test databases...") +# self.teardown_databases(old_config) + +# # Tear down the test environment +# print("Tearing down test environment...") +# self.teardown_test_environment() + +# return self.suite_result(suite, result) +# except Exception as e: +# print(f"Error running tests: {e}") +# raise diff --git a/python_files/unittestadapter/execution.py b/python_files/unittestadapter/execution.py index e81407e1e83c..00948ff5eb83 100644 --- a/python_files/unittestadapter/execution.py +++ b/python_files/unittestadapter/execution.py @@ -24,6 +24,8 @@ sys.path.append(os.fspath(script_dir)) sys.path.insert(0, os.fspath(script_dir / "lib" / "python")) +from django_runner import django_execution_runner # noqa: E402 + from testing_tools import process_json_util, socket_manager # noqa: E402 from unittestadapter.pvsc_utils import ( # noqa: E402 EOTPayloadDict, @@ -321,16 +323,25 @@ def send_run_data(raw_data, test_run_pipe): if raw_json and "params" in raw_json: test_ids_from_buffer = raw_json["params"] if test_ids_from_buffer: - # Perform test execution. - payload = run_tests( - start_dir, - test_ids_from_buffer, - pattern, - top_level_dir, - verbosity, - failfast, - locals_, - ) + # Check to see if we are running django tests. + manage_py_path = os.environ.get("MANAGE_PY_PATH") + print("DJANGO_TEST_ENABLED = ", manage_py_path) + if manage_py_path: + # run django runner + print("running django runner") + django_execution_runner(start_dir, manage_py_path) + else: + print("running unittest runner") + # Perform test execution. + payload = run_tests( + start_dir, + test_ids_from_buffer, + pattern, + top_level_dir, + verbosity, + failfast, + locals_, + ) else: # No test ids received from buffer cwd = os.path.abspath(start_dir) # noqa: PTH100 From be57ff50f281c222dec52f4bcdfab2af497a1309 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Wed, 7 Aug 2024 11:13:14 -0700 Subject: [PATCH 02/17] discovery and execution working!! --- python_files/unittestadapter/discovery.py | 63 ++--- python_files/unittestadapter/django_runner.py | 95 ++++--- .../unittestadapter/django_test_runner.py | 234 +++++++++++++----- python_files/unittestadapter/execution.py | 22 +- 4 files changed, 277 insertions(+), 137 deletions(-) diff --git a/python_files/unittestadapter/discovery.py b/python_files/unittestadapter/discovery.py index 1dd07d301c76..346d76639029 100644 --- a/python_files/unittestadapter/discovery.py +++ b/python_files/unittestadapter/discovery.py @@ -13,7 +13,7 @@ sys.path.append(os.fspath(script_dir)) sys.path.insert(0, os.fspath(script_dir / "lib" / "python")) -from django_runner import django_execution_runner # noqa: E402 +from django_runner import django_discovery_runner, django_execution_runner # noqa: E402 print(sys.path) @@ -33,8 +33,8 @@ send_post_request, ) -debugpy.connect(5678) -debugpy.breakpoint() +# debugpy.connect(5678) +# debugpy.breakpoint() def discover_tests( @@ -135,50 +135,21 @@ def discover_tests( # if django build discovery run command for this # CustomTestRunner2 - # manage_py_path = os.environ.get("MANAGE_PY_PATH") - # print("DJANGO_TEST_ENABLED = ", manage_py_path) - # payload = None + manage_py_path = os.environ.get("MANAGE_PY_PATH") + print("DJANGO_TEST_ENABLED = ", manage_py_path) if True: - # # run django runner - # print("running django runner") - # custom_test_runner_dir = pathlib.Path(__file__).parent - # sys.path.insert(0, custom_test_runner_dir) - # subprocess.run( - # [ - # sys.executable, - # "manage.py", - # "test", - # "--testrunner", - # "django_new.CustomTestRunner2", - # ], - # check=True, - # ) try: - custom_test_runner_dir = pathlib.Path(__file__).parent - sys.path.append(custom_test_runner_dir) - custom_test_runner = "django_test_runner.CustomTestRunner" - - django_execution_runner(start_dir) - - # Build command to run 'python manage.py test'. - python_executable = sys.executable - command = [ - python_executable, - "manage.py", # switch for manage.py path - "test", - "--testrunner=${custom_test_runner}", - "--verbosity=2", - ] - subprocess.run(" ".join(command), shell=True, check=True) + # this will run and send directly from the runner + django_discovery_runner(start_dir, manage_py_path) except Exception as e: - error_msg = "Error configuring Django test runner: {e}" + error_msg = f"Error configuring Django test runner: {e}" print(error_msg, file=sys.stderr) - raise VSCodeUnittestError(error_msg) - # else: - # Perform test discovery. - payload = discover_tests(start_dir, pattern, top_level_dir) - # Post this discovery payload. - send_post_request(payload, test_run_pipe) - # Post EOT token. - eot_payload: EOTPayloadDict = {"command_type": "discovery", "eot": True} - send_post_request(eot_payload, test_run_pipe) + raise VSCodeUnittestError(error_msg) # noqa: B904 + else: + # Perform test discovery. + payload = discover_tests(start_dir, pattern, top_level_dir) + # Post this discovery payload. + send_post_request(payload, test_run_pipe) + # Post EOT token. + eot_payload: EOTPayloadDict = {"command_type": "discovery", "eot": True} + send_post_request(eot_payload, test_run_pipe) diff --git a/python_files/unittestadapter/django_runner.py b/python_files/unittestadapter/django_runner.py index c7c01a9217e0..e3e776804e39 100644 --- a/python_files/unittestadapter/django_runner.py +++ b/python_files/unittestadapter/django_runner.py @@ -17,51 +17,90 @@ ) -def django_execution_runner(start_dir: Union[str, None]) -> None: +def django_discovery_runner( + top_level_dir: Union[str, None], manage_py_path: Union[str, None] +) -> None: # Get path to manage.py if set as an env var, otherwise use the default - manage_py_path = os.environ.get("MANAGE_PY_PATH") - # if manage_py_path is None: - # # Search for default manage.py path at the root of the workspace - # if not start_dir: - # print("Error running Django, no start_dir provided or value for MANAGE_PY_PATH") + if manage_py_path is None: + # Search for default manage.py path at the root of the workspace + if not top_level_dir: + print("Error running Django, no start_dir provided or value for MANAGE_PY_PATH") - # cwd = pathlib.Path.resolve(start_dir) - # manage_py_path = cwd / "manage.py" + cwd = pathlib.Path(top_level_dir) + manage_py_path = os.fspath(cwd / "manage.py") try: # Get path to the custom_test_runner.py parent folder, add to sys.path. custom_test_runner_dir = pathlib.Path(__file__).parent sys.path.insert(0, custom_test_runner_dir) - custom_test_runner_dir2 = pathlib.Path(__file__).parent.parent - sys.path.insert(0, custom_test_runner_dir2) - custom_test_runner = "django_test_runner.CustomTestRunner" # Build command to run 'python manage.py test'. - python_executable = sys.executable command = [ - python_executable, - "/Users/eleanorboyd/testingFiles/django-polls/manage.py", # switch for manage.py path + sys.executable, + manage_py_path, "test", - "--testrunner=django_test_runner.CustomTestRunner", - "--verbosity=3", - ] + "--testrunner=django_test_runner.CustomDiscoveryTestRunner", + "--verbosity=3", # remove when no longer in development mode + ] print("Running Django run tests with command: ", command) env = os.environ.copy() if "PYTHONPATH" in env: - env["PYTHONPATH"] = ( - custom_test_runner_dir - + os.pathsep - + custom_test_runner_dir2 - + os.pathsep - + env["PYTHONPATH"] - ) + env["PYTHONPATH"] = os.fspath(custom_test_runner_dir) + os.pathsep + env["PYTHONPATH"] else: - env["PYTHONPATH"] = ( - "/Users/eleanorboyd/vscode-python/python_files/unittestadapter" - + os.pathsep - + "/Users/eleanorboyd/vscode-python/python_files" + env["PYTHONPATH"] = os.fspath(custom_test_runner_dir) + try: + abc = subprocess.run( + command, + capture_output=True, + text=True, + env=env, ) + print(abc.stderr) + print(abc.stdout) + print("Django tests ran successfully.") + except subprocess.CalledProcessError as e: + print(f"Error running 'manage.py test': {e}") + print(traceback.format_exc()) + raise VSCodeUnittestError(f"Error running 'manage.py test': {e}") # noqa: B904 + except Exception as e: + print(f"Error configuring Django test runner: {e}") + raise VSCodeUnittestError(f"Error configuring Django test runner: {e}") # noqa: B904 + + +def django_execution_runner( + top_level_dir: Union[str, None], manage_py_path: Union[str, None], test_ids: list[str] +) -> None: + # Get path to manage.py if set as an env var, otherwise use the default + + if manage_py_path is None: + # Search for default manage.py path at the root of the workspace + if not top_level_dir: + print("Error running Django, no start_dir provided or value for MANAGE_PY_PATH") + + cwd = pathlib.Path(top_level_dir) + manage_py_path = os.fspath(cwd / "manage.py") + + try: + # Get path to the custom_test_runner.py parent folder, add to sys.path. + custom_test_runner_dir = pathlib.Path(__file__).parent + sys.path.insert(0, custom_test_runner_dir) + + # Build command to run 'python manage.py test'. + command = [ + sys.executable, + manage_py_path, + "test", + "--testrunner=django_test_runner.CustomExecutionTestRunner", + "--verbosity=3", # remove when no longer in development mode + ] + command.extend(test_ids) + print("Running Django run tests with command: ", command) + env = os.environ.copy() + if "PYTHONPATH" in env: + env["PYTHONPATH"] = os.fspath(custom_test_runner_dir) + os.pathsep + env["PYTHONPATH"] + else: + env["PYTHONPATH"] = os.fspath(custom_test_runner_dir) try: abc = subprocess.run( command, diff --git a/python_files/unittestadapter/django_test_runner.py b/python_files/unittestadapter/django_test_runner.py index fb9903a2d7bf..85fcc969dc7f 100644 --- a/python_files/unittestadapter/django_test_runner.py +++ b/python_files/unittestadapter/django_test_runner.py @@ -6,76 +6,190 @@ import pathlib import sys from unittest import TestSuite, TextTestResult +import unittest from django.test.runner import DiscoverRunner + + # from execution import UnittestTestResult -script_dir = pathlib.Path(__file__).parent +script_dir = pathlib.Path(__file__).parent.parent sys.path.append(os.fspath(script_dir)) +from pvsc_utils import ( + DiscoveryPayloadDict, + EOTPayloadDict, + VSCodeUnittestError, + build_test_tree, + send_post_request, +) +from execution import UnittestTestResult + print("HELLO!!!") -class CustomTestRunner(DiscoverRunner): +class CustomDiscoveryTestRunner(DiscoverRunner): + # def get_test_runner_kwargs(self): + # print("get_test_runner_kwargs") + # kwargs = super().get_test_runner_kwargs() + # if kwargs["resultclass"] is not None: + # raise ValueError( + # "Resultclass already set, cannot use custom test runner design for VS Code compatibility." + # ) + # # kwargs["resultclass"] = UnittestTestResult + # return kwargs + + # def build_suite(self, test_labels, extra_tests=None, **kwargs): + # print("build_suite") + # suite = super().build_suite(test_labels) + # return suite + + # def run_suite(self, suite: TestSuite, **kwargs) -> TextTestResult: + # print("EJFB HELLO!!!") + # return super().run_suite(suite, **kwargs) + + def run_tests(self, test_labels, **kwargs): + try: + print("Running tests...") + print("test labels: ", test_labels) + print("kwargs: ", kwargs) + suite: unittest.TestSuite = self.build_suite(test_labels, **kwargs) + test_names = [str(test) for test in suite] + for name in sorted(test_names): + print(name) + + # # If the top level directory is not provided, then use the start directory. + # if top_level_dir is None: + # top_level_dir = start_dir + cwd = pathlib.Path.cwd() + # Get abspath of top level directory for build_test_tree. + top_level_dir = os.path.abspath(cwd) # noqa: PTH100 + + payload: DiscoveryPayloadDict = { + "cwd": os.fspath(cwd), + "status": "success", + "tests": None, + } + + tests, error = build_test_tree( + suite, top_level_dir + ) # test tree built successfully here. + + payload["tests"] = tests if tests is not None else None # or overloading + + if len(error): + payload["status"] = "error" + payload["error"] = error + + 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) + send_post_request(payload, test_run_pipe) + # Post EOT token. + eot_payload: EOTPayloadDict = {"command_type": "discovery", "eot": True} + send_post_request(eot_payload, test_run_pipe) + + return 0 # Skip actual test execution for now + except Exception as e: + print(f"Error running tests: {e}") + raise + + +class CustomExecutionTestRunner(DiscoverRunner): def get_test_runner_kwargs(self): - print("get_test_runner_kwargs") + """Override to provide custom test runner kwargs, such as resultclass.""" + # Get existing kwargs kwargs = super().get_test_runner_kwargs() - if kwargs["resultclass"] is not None: - raise ValueError( - "Resultclass already set, cannot use custom test runner design for VS Code compatibility." - ) - # kwargs["resultclass"] = UnittestTestResult + # Add custom resultclass + kwargs["resultclass"] = UnittestTestResult return kwargs - def build_suite(self, test_labels, extra_tests=None, **kwargs): - print("build_suite") - suite = super().build_suite(test_labels) - return suite - - def run_suite(self, suite: TestSuite, **kwargs) -> TextTestResult: - print("EJFB HELLO!!!") - return super().run_suite(suite, **kwargs) - - -# import unittest -# from django.test.runner import DiscoverRunner - - -# class CustomTestRunner(DiscoverRunner): -# def run_tests(self, test_labels, extra_tests=None, **kwargs): -# try: -# # Set up the test environment -# print("Setting up test environment...") -# self.setup_test_environment() - -# # Set up the test databases -# print("Setting up test databases...") -# old_config = self.setup_databases() - -# # Call the default build_suite method to create the test suite -# print("Building test suite...") -# print(f"test_labels: {test_labels}") -# print(f"extra_tests: {extra_tests}") -# suite = self.build_suite(test_labels) - -# # Print out the test suite -# test_names = [str(test) for test in suite] -# for name in sorted(test_names): -# print(name) - -# # Run the tests normally -# print("Running tests...") -# result = self.test_runner(verbosity=self.verbosity).run(suite) - -# # Tear down the test databases -# print("Tearing down test databases...") -# self.teardown_databases(old_config) - -# # Tear down the test environment -# print("Tearing down test environment...") -# self.teardown_test_environment() - -# return self.suite_result(suite, result) -# except Exception as e: -# print(f"Error running tests: {e}") -# raise + def get_test_runner(self): + return unittest.TextTestRunner(resultclass=UnittestTestResult) + + def run_tests(self, test_labels, **kwargs): + print("Running tests...") + print("test labels:", test_labels) + print("kwargs: ", kwargs) + result = super().run_tests(test_labels, **kwargs) + return result + + def addSuccess(self, test): + print("add success??") + super().addSuccess(test) + self.successes.append(test) + + def suite_result(self, suite, result, **kwargs): + # send ending here + test_run_pipe = os.getenv("TEST_RUN_PIPE") + if not test_run_pipe: + print("Error[vscode-unittest]: TEST_RUN_PIPE env var is not set.") + raise VSCodeUnittestError("Error[vscode-unittest]: TEST_RUN_PIPE env var is not set.") + eot_payload: EOTPayloadDict = {"command_type": "execution", "eot": True} + send_post_request(eot_payload, test_run_pipe) + print("suite_result") + print("suite: ", suite) + print("result: ", result) + return super().suite_result(suite, result, **kwargs) + + # def run_tests(self, test_labels, **kwargs): + # suite: unittest.TestSuite = self.build_suite(test_labels, **kwargs) + # super().run_suite(suite, **kwargs) + # try: + # print("Running tests...") + # print("test labels: ", test_labels) + # print("kwargs: ", kwargs) + # suite: unittest.TestSuite = self.build_suite(test_labels, **kwargs) + # test_names = [str(test) for test in suite] + # for name in sorted(test_names): + # print(name) + + # # # If the top level directory is not provided, then use the start directory. + # # if top_level_dir is None: + # # top_level_dir = start_dir + # cwd = pathlib.Path.cwd() + # # Get abspath of top level directory for build_test_tree. + # top_level_dir = os.path.abspath(cwd) # noqa: PTH100 + + # payload: DiscoveryPayloadDict = { + # "cwd": os.fspath(cwd), + # "status": "success", + # "tests": None, + # } + + # tests, error = build_test_tree( + # suite, top_level_dir + # ) # test tree built successfully here. + + # payload["tests"] = tests if tests is not None else None + + # if len(error): + # payload["status"] = "error" + # payload["error"] = error + + # 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) + # send_post_request(payload, test_run_pipe) + # # Post EOT token. + # eot_payload: EOTPayloadDict = {"command_type": "discovery", "eot": True} + # send_post_request(eot_payload, test_run_pipe) + + # return 0 # Skip actual test execution for now + # except Exception as e: + # print(f"Error running tests: {e}") + # raise diff --git a/python_files/unittestadapter/execution.py b/python_files/unittestadapter/execution.py index 00948ff5eb83..e8bacd991c0c 100644 --- a/python_files/unittestadapter/execution.py +++ b/python_files/unittestadapter/execution.py @@ -19,6 +19,17 @@ sysconfig.get_paths()["scripts"] + os.pathsep + os.environ[path_var_name] ) +print(sys.path) +sys.path.append("/Users/eleanorboyd/vscode-python/.venv/lib/python3.10/site-packages") +import debugpy + +script_dir = pathlib.Path(__file__).parent.parent +sys.path.append(os.fspath(script_dir)) + + +# debugpy.connect(5678) +# debugpy.breakpoint() + script_dir = pathlib.Path(__file__).parent.parent sys.path.append(os.fspath(script_dir)) @@ -326,10 +337,11 @@ def send_run_data(raw_data, test_run_pipe): # Check to see if we are running django tests. manage_py_path = os.environ.get("MANAGE_PY_PATH") print("DJANGO_TEST_ENABLED = ", manage_py_path) + manage_py_path = "/Users/eleanorboyd/testingFiles/django-polls/manage.py" if manage_py_path: # run django runner print("running django runner") - django_execution_runner(start_dir, manage_py_path) + django_execution_runner(start_dir, manage_py_path, test_ids_from_buffer) else: print("running unittest runner") # Perform test execution. @@ -342,6 +354,8 @@ def send_run_data(raw_data, test_run_pipe): failfast, locals_, ) + eot_payload: EOTPayloadDict = {"command_type": "execution", "eot": True} + send_post_request(eot_payload, test_run_pipe) else: # No test ids received from buffer cwd = os.path.abspath(start_dir) # noqa: PTH100 @@ -353,9 +367,11 @@ def send_run_data(raw_data, test_run_pipe): "result": None, } send_post_request(payload, test_run_pipe) + eot_payload: EOTPayloadDict = {"command_type": "execution", "eot": True} + send_post_request(eot_payload, test_run_pipe) except json.JSONDecodeError as exc: msg = "Error: Could not parse test ids from stdin" print(msg) raise VSCodeUnittestError(msg) from exc - eot_payload: EOTPayloadDict = {"command_type": "execution", "eot": True} - send_post_request(eot_payload, test_run_pipe) + eot_payload: EOTPayloadDict = {"command_type": "execution", "eot": True} + send_post_request(eot_payload, test_run_pipe) From 932f84d484654e8a2fe3c84395b58ea9a02fcab0 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Thu, 8 Aug 2024 15:17:21 -0700 Subject: [PATCH 03/17] clean up --- python_files/unittestadapter/discovery.py | 32 ++- .../unittestadapter/django_handler.py | 123 ++++++++++++ python_files/unittestadapter/django_new.py | 34 ---- python_files/unittestadapter/django_runner.py | 120 ------------ .../unittestadapter/django_test_runner.py | 185 ++++-------------- python_files/unittestadapter/execution.py | 24 +-- 6 files changed, 181 insertions(+), 337 deletions(-) create mode 100644 python_files/unittestadapter/django_handler.py delete mode 100644 python_files/unittestadapter/django_new.py delete mode 100644 python_files/unittestadapter/django_runner.py diff --git a/python_files/unittestadapter/discovery.py b/python_files/unittestadapter/discovery.py index 346d76639029..20eacfdb84f6 100644 --- a/python_files/unittestadapter/discovery.py +++ b/python_files/unittestadapter/discovery.py @@ -1,7 +1,6 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. -import subprocess import os import pathlib import sys @@ -13,12 +12,7 @@ sys.path.append(os.fspath(script_dir)) sys.path.insert(0, os.fspath(script_dir / "lib" / "python")) -from django_runner import django_discovery_runner, django_execution_runner # noqa: E402 - - -print(sys.path) -sys.path.append("/Users/eleanorboyd/vscode-python/.venv/lib/python3.10/site-packages") -import debugpy +from django_handler import django_discovery_runner # noqa: E402 script_dir = pathlib.Path(__file__).parent.parent sys.path.append(os.fspath(script_dir)) @@ -33,9 +27,6 @@ send_post_request, ) -# debugpy.connect(5678) -# debugpy.breakpoint() - def discover_tests( start_dir: str, @@ -85,10 +76,6 @@ def discover_tests( 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 @@ -133,20 +120,23 @@ def discover_tests( print(error_msg, file=sys.stderr) raise VSCodeUnittestError(error_msg) - # if django build discovery run command for this - # CustomTestRunner2 manage_py_path = os.environ.get("MANAGE_PY_PATH") - print("DJANGO_TEST_ENABLED = ", manage_py_path) - if True: + if 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: - # this will run and send directly from the runner - django_discovery_runner(start_dir, manage_py_path) + # collect args for Django discovery runner. + args = argv[index + 1 :] or [] + django_discovery_runner(manage_py_path, args) + # eot payload sent within Django runner. 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: - # Perform test discovery. + # Perform regular unittest test discovery. payload = discover_tests(start_dir, pattern, top_level_dir) # Post this discovery payload. send_post_request(payload, test_run_pipe) diff --git a/python_files/unittestadapter/django_handler.py b/python_files/unittestadapter/django_handler.py new file mode 100644 index 000000000000..60b1b4535634 --- /dev/null +++ b/python_files/unittestadapter/django_handler.py @@ -0,0 +1,123 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +import os +import pathlib +import subprocess +import sys +import traceback + +script_dir = pathlib.Path(__file__).parent.parent +sys.path.append(os.fspath(script_dir)) +sys.path.insert(0, os.fspath(script_dir / "lib" / "python")) + +from pvsc_utils import ( # noqa: E402 + EOTPayloadDict, + VSCodeUnittestError, + send_post_request, +) + + +def django_discovery_runner(manage_py_path: str, args: list[str]) -> None: + # Attempt a small amount of validation on the manage.py path. + try: + pathlib.Path(manage_py_path) + except Exception as e: + raise VSCodeUnittestError(f"Error running Django, manage.py path is not a valid path: {e}") # noqa: B904 + + try: + # Get path to the custom_test_runner.py parent folder, add to sys.path and new environment used for subprocess. + custom_test_runner_dir = pathlib.Path(__file__).parent + sys.path.insert(0, custom_test_runner_dir) + env = os.environ.copy() + if "PYTHONPATH" in env: + env["PYTHONPATH"] = os.fspath(custom_test_runner_dir) + os.pathsep + env["PYTHONPATH"] + else: + env["PYTHONPATH"] = os.fspath(custom_test_runner_dir) + + # Build command to run 'python manage.py test'. + command = [ + sys.executable, + manage_py_path, + "test", + "--testrunner=django_test_runner.CustomDiscoveryTestRunner", + ] + command.extend(args) + print("Running Django tests with command:", command) + + subprocess_discovery = subprocess.run( + command, + capture_output=True, + text=True, + env=env, + ) + print(subprocess_discovery.stderr, file=sys.stderr) + print(subprocess_discovery.stdout, file=sys.stdout) + # Zero return code indicates success, 1 indicates test failures, so both are considered successful. + if subprocess_discovery.returncode not in (0, 1): + error_msg = "Django test discovery process exited with non-zero error code See stderr above for more details." + print(error_msg, file=sys.stderr) + try: + test_run_pipe = os.getenv("TEST_RUN_PIPE") + eot_payload: EOTPayloadDict = {"command_type": "discovery", "eot": True} + send_post_request(eot_payload, test_run_pipe) + except Exception: + raise VSCodeUnittestError( # noqa: B904 + "Connection failure, likely means failure in Django subprocess run, see specific error output above." + ) + except Exception as e: + raise VSCodeUnittestError(f"Error during Django discovery: {e}") # noqa: B904 + + +def django_execution_runner(manage_py_path: str, test_ids: list[str], args: list[str]) -> None: + # Attempt a small amount of validation on the manage.py path. + try: + pathlib.Path(manage_py_path) + except Exception as e: + raise VSCodeUnittestError(f"Error running Django, manage.py path is not a valid path: {e}") # noqa: B904 + + try: + # Get path to the custom_test_runner.py parent folder, add to sys.path. + custom_test_runner_dir: pathlib.Path = pathlib.Path(__file__).parent + sys.path.insert(0, custom_test_runner_dir) + env: dict[str, str] = os.environ.copy() + if "PYTHONPATH" in env: + env["PYTHONPATH"] = os.fspath(custom_test_runner_dir) + os.pathsep + env["PYTHONPATH"] + else: + env["PYTHONPATH"] = os.fspath(custom_test_runner_dir) + + # Build command to run 'python manage.py test'. + command: list[str] = [ + sys.executable, + manage_py_path, + "test", + "--testrunner=django_test_runner.CustomExecutionTestRunner", + ] + # Add any additional arguments to the command provided by the user. + command.extend(args) + # Add the test_ids to the command. + print("Test IDs: ", test_ids) + print("args: ", args) + command.extend(test_ids) + print("Running Django run tests with command: ", command) + subprocess_execution = subprocess.run( + command, + capture_output=True, + text=True, + env=env, + ) + print(subprocess_execution.stderr, file=sys.stderr) + print(subprocess_execution.stdout, file=sys.stdout) + # Zero return code indicates success, 1 indicates test failures, so both are considered successful. + if subprocess_execution.returncode not in (0, 1): + try: + print("ERROR NUM", subprocess_execution.returncode) + test_run_pipe = os.getenv("TEST_RUN_PIPE") + eot_payload: EOTPayloadDict = {"command_type": "discovery", "eot": True} + send_post_request(eot_payload, test_run_pipe) + except Exception: + raise VSCodeUnittestError( # noqa: B904 + "Connection failure, likely means failure in Django subprocess run, see specific error output above." + ) + except Exception as e: + raise VSCodeUnittestError(f"Error during Django test execution: {e}") # noqa: B904 diff --git a/python_files/unittestadapter/django_new.py b/python_files/unittestadapter/django_new.py deleted file mode 100644 index 377e990e9d3c..000000000000 --- a/python_files/unittestadapter/django_new.py +++ /dev/null @@ -1,34 +0,0 @@ -from django.test.runner import DiscoverRunner - - -class CustomTestRunner2(DiscoverRunner): - def run_tests(self, test_labels, extra_tests=None, **kwargs): - # Set up the test environment - self.setup_test_environment() - - # Set up the test databases - old_config = self.setup_databases() - - # Call the default build_suite method to create the test suite - suite = self.build_suite(test_labels, extra_tests, **kwargs) - - # Print out the test suite - test_names = [str(test) for test in suite] - for name in sorted(test_names): - print(name) - - # Optionally, prevent the tests from running by returning early - # return 0 # Uncomment this line to skip test execution - - # Run the tests normally - result = self.test_runner( - verbosity=self.verbosity, failfast=self.failfast, keepdb=self.keepdb - ).run(suite) - - # Tear down the test databases - self.teardown_databases(old_config) - - # Tear down the test environment - self.teardown_test_environment() - - return self.suite_result(suite, result) diff --git a/python_files/unittestadapter/django_runner.py b/python_files/unittestadapter/django_runner.py deleted file mode 100644 index e3e776804e39..000000000000 --- a/python_files/unittestadapter/django_runner.py +++ /dev/null @@ -1,120 +0,0 @@ -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. - -import os -import pathlib -import subprocess -import sys -from typing import Union -import traceback - -script_dir = pathlib.Path(__file__).parent.parent -sys.path.append(os.fspath(script_dir)) -sys.path.insert(0, os.fspath(script_dir / "lib" / "python")) - -from pvsc_utils import ( # noqa: E402 - VSCodeUnittestError, -) - - -def django_discovery_runner( - top_level_dir: Union[str, None], manage_py_path: Union[str, None] -) -> None: - # Get path to manage.py if set as an env var, otherwise use the default - - if manage_py_path is None: - # Search for default manage.py path at the root of the workspace - if not top_level_dir: - print("Error running Django, no start_dir provided or value for MANAGE_PY_PATH") - - cwd = pathlib.Path(top_level_dir) - manage_py_path = os.fspath(cwd / "manage.py") - - try: - # Get path to the custom_test_runner.py parent folder, add to sys.path. - custom_test_runner_dir = pathlib.Path(__file__).parent - sys.path.insert(0, custom_test_runner_dir) - - # Build command to run 'python manage.py test'. - command = [ - sys.executable, - manage_py_path, - "test", - "--testrunner=django_test_runner.CustomDiscoveryTestRunner", - "--verbosity=3", # remove when no longer in development mode - ] - print("Running Django run tests with command: ", command) - env = os.environ.copy() - if "PYTHONPATH" in env: - env["PYTHONPATH"] = os.fspath(custom_test_runner_dir) + os.pathsep + env["PYTHONPATH"] - else: - env["PYTHONPATH"] = os.fspath(custom_test_runner_dir) - try: - abc = subprocess.run( - command, - capture_output=True, - text=True, - env=env, - ) - print(abc.stderr) - print(abc.stdout) - print("Django tests ran successfully.") - except subprocess.CalledProcessError as e: - print(f"Error running 'manage.py test': {e}") - print(traceback.format_exc()) - raise VSCodeUnittestError(f"Error running 'manage.py test': {e}") # noqa: B904 - except Exception as e: - print(f"Error configuring Django test runner: {e}") - raise VSCodeUnittestError(f"Error configuring Django test runner: {e}") # noqa: B904 - - -def django_execution_runner( - top_level_dir: Union[str, None], manage_py_path: Union[str, None], test_ids: list[str] -) -> None: - # Get path to manage.py if set as an env var, otherwise use the default - - if manage_py_path is None: - # Search for default manage.py path at the root of the workspace - if not top_level_dir: - print("Error running Django, no start_dir provided or value for MANAGE_PY_PATH") - - cwd = pathlib.Path(top_level_dir) - manage_py_path = os.fspath(cwd / "manage.py") - - try: - # Get path to the custom_test_runner.py parent folder, add to sys.path. - custom_test_runner_dir = pathlib.Path(__file__).parent - sys.path.insert(0, custom_test_runner_dir) - - # Build command to run 'python manage.py test'. - command = [ - sys.executable, - manage_py_path, - "test", - "--testrunner=django_test_runner.CustomExecutionTestRunner", - "--verbosity=3", # remove when no longer in development mode - ] - command.extend(test_ids) - print("Running Django run tests with command: ", command) - env = os.environ.copy() - if "PYTHONPATH" in env: - env["PYTHONPATH"] = os.fspath(custom_test_runner_dir) + os.pathsep + env["PYTHONPATH"] - else: - env["PYTHONPATH"] = os.fspath(custom_test_runner_dir) - try: - abc = subprocess.run( - command, - capture_output=True, - text=True, - env=env, - ) - print(abc.stderr) - print(abc.stdout) - print("Django tests ran successfully.") - except subprocess.CalledProcessError as e: - print(f"Error running 'manage.py test': {e}") - print(traceback.format_exc()) - raise VSCodeUnittestError(f"Error running 'manage.py test': {e}") # noqa: B904 - except Exception as e: - print(f"Error configuring Django test runner: {e}") - raise VSCodeUnittestError(f"Error configuring Django test runner: {e}") # noqa: B904 diff --git a/python_files/unittestadapter/django_test_runner.py b/python_files/unittestadapter/django_test_runner.py index 85fcc969dc7f..47d1bc509c90 100644 --- a/python_files/unittestadapter/django_test_runner.py +++ b/python_files/unittestadapter/django_test_runner.py @@ -5,191 +5,92 @@ import os import pathlib import sys -from unittest import TestSuite, TextTestResult -import unittest +print("PATH!", sys.path) from django.test.runner import DiscoverRunner - -# from execution import UnittestTestResult - script_dir = pathlib.Path(__file__).parent.parent sys.path.append(os.fspath(script_dir)) -from pvsc_utils import ( +from typing import TYPE_CHECKING # noqa: E402 + +from execution import UnittestTestResult # noqa: E402 +from pvsc_utils import ( # noqa: E402 DiscoveryPayloadDict, EOTPayloadDict, VSCodeUnittestError, build_test_tree, send_post_request, ) -from execution import UnittestTestResult - -print("HELLO!!!") +if TYPE_CHECKING: + import unittest class CustomDiscoveryTestRunner(DiscoverRunner): - # def get_test_runner_kwargs(self): - # print("get_test_runner_kwargs") - # kwargs = super().get_test_runner_kwargs() - # if kwargs["resultclass"] is not None: - # raise ValueError( - # "Resultclass already set, cannot use custom test runner design for VS Code compatibility." - # ) - # # kwargs["resultclass"] = UnittestTestResult - # return kwargs - - # def build_suite(self, test_labels, extra_tests=None, **kwargs): - # print("build_suite") - # suite = super().build_suite(test_labels) - # return suite - - # def run_suite(self, suite: TestSuite, **kwargs) -> TextTestResult: - # print("EJFB HELLO!!!") - # return super().run_suite(suite, **kwargs) + """Custom test runner for Django to handle test DISCOVERY and building the test tree.""" def run_tests(self, test_labels, **kwargs): + eot_payload: EOTPayloadDict = {"command_type": "discovery", "eot": True} + test_run_pipe: str = 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) try: - print("Running tests...") - print("test labels: ", test_labels) - print("kwargs: ", kwargs) - suite: unittest.TestSuite = self.build_suite(test_labels, **kwargs) - test_names = [str(test) for test in suite] - for name in sorted(test_names): - print(name) + top_level_dir: pathlib.Path = pathlib.Path.cwd() - # # If the top level directory is not provided, then use the start directory. - # if top_level_dir is None: - # top_level_dir = start_dir - cwd = pathlib.Path.cwd() - # Get abspath of top level directory for build_test_tree. - top_level_dir = os.path.abspath(cwd) # noqa: PTH100 + # Discover tests and build into a tree. + suite: unittest.TestSuite = self.build_suite(test_labels, **kwargs) + tests, error = build_test_tree(suite, os.fspath(top_level_dir)) payload: DiscoveryPayloadDict = { - "cwd": os.fspath(cwd), + "cwd": os.fspath(top_level_dir), "status": "success", "tests": None, } - - tests, error = build_test_tree( - suite, top_level_dir - ) # test tree built successfully here. - - payload["tests"] = tests if tests is not None else None # or overloading - + payload["tests"] = tests if tests is not None else None if len(error): payload["status"] = "error" payload["error"] = error - 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) + # Send discovery payload. send_post_request(payload, test_run_pipe) - # Post EOT token. - eot_payload: EOTPayloadDict = {"command_type": "discovery", "eot": True} + # Send EOT token. send_post_request(eot_payload, test_run_pipe) - - return 0 # Skip actual test execution for now + return 0 # Skip actual test execution, return 0 as no tests were run. except Exception as e: - print(f"Error running tests: {e}") - raise + error_msg = ( + "DJANGO ERROR: An error occurred while discovering and building the test suite. " + f"Error: {e}\n" + ) + send_post_request(eot_payload, test_run_pipe) + print(error_msg, file=sys.stderr) + raise VSCodeUnittestError(error_msg) # noqa: B904 class CustomExecutionTestRunner(DiscoverRunner): + """Custom test runner for Django to handle test EXECUTION and uses UnittestTestResult to send dynamic run results.""" + def get_test_runner_kwargs(self): - """Override to provide custom test runner kwargs, such as resultclass.""" + """Override to provide custom test runner; resultclass.""" # Get existing kwargs kwargs = super().get_test_runner_kwargs() - # Add custom resultclass + # Add custom resultclass, same resultclass as used in unittest. kwargs["resultclass"] = UnittestTestResult return kwargs - def get_test_runner(self): - return unittest.TextTestRunner(resultclass=UnittestTestResult) - - def run_tests(self, test_labels, **kwargs): - print("Running tests...") - print("test labels:", test_labels) - print("kwargs: ", kwargs) - result = super().run_tests(test_labels, **kwargs) - return result - - def addSuccess(self, test): - print("add success??") - super().addSuccess(test) - self.successes.append(test) - def suite_result(self, suite, result, **kwargs): - # send ending here - test_run_pipe = os.getenv("TEST_RUN_PIPE") + # After run finishes, send EOT token. + test_run_pipe: str = os.getenv("TEST_RUN_PIPE") if not test_run_pipe: - print("Error[vscode-unittest]: TEST_RUN_PIPE env var is not set.") + print("Error[vscode-unittest]: TEST_RUN_PIPE env var is not set.", file=sys.stderr) raise VSCodeUnittestError("Error[vscode-unittest]: TEST_RUN_PIPE env var is not set.") eot_payload: EOTPayloadDict = {"command_type": "execution", "eot": True} send_post_request(eot_payload, test_run_pipe) - print("suite_result") - print("suite: ", suite) - print("result: ", result) + # call super to finish the suite result as Django intends. return super().suite_result(suite, result, **kwargs) - - # def run_tests(self, test_labels, **kwargs): - # suite: unittest.TestSuite = self.build_suite(test_labels, **kwargs) - # super().run_suite(suite, **kwargs) - # try: - # print("Running tests...") - # print("test labels: ", test_labels) - # print("kwargs: ", kwargs) - # suite: unittest.TestSuite = self.build_suite(test_labels, **kwargs) - # test_names = [str(test) for test in suite] - # for name in sorted(test_names): - # print(name) - - # # # If the top level directory is not provided, then use the start directory. - # # if top_level_dir is None: - # # top_level_dir = start_dir - # cwd = pathlib.Path.cwd() - # # Get abspath of top level directory for build_test_tree. - # top_level_dir = os.path.abspath(cwd) # noqa: PTH100 - - # payload: DiscoveryPayloadDict = { - # "cwd": os.fspath(cwd), - # "status": "success", - # "tests": None, - # } - - # tests, error = build_test_tree( - # suite, top_level_dir - # ) # test tree built successfully here. - - # payload["tests"] = tests if tests is not None else None - - # if len(error): - # payload["status"] = "error" - # payload["error"] = error - - # 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) - # send_post_request(payload, test_run_pipe) - # # Post EOT token. - # eot_payload: EOTPayloadDict = {"command_type": "discovery", "eot": True} - # send_post_request(eot_payload, test_run_pipe) - - # return 0 # Skip actual test execution for now - # except Exception as e: - # print(f"Error running tests: {e}") - # raise diff --git a/python_files/unittestadapter/execution.py b/python_files/unittestadapter/execution.py index e8bacd991c0c..12e5a665a004 100644 --- a/python_files/unittestadapter/execution.py +++ b/python_files/unittestadapter/execution.py @@ -19,23 +19,10 @@ sysconfig.get_paths()["scripts"] + os.pathsep + os.environ[path_var_name] ) -print(sys.path) -sys.path.append("/Users/eleanorboyd/vscode-python/.venv/lib/python3.10/site-packages") -import debugpy - -script_dir = pathlib.Path(__file__).parent.parent -sys.path.append(os.fspath(script_dir)) - - -# debugpy.connect(5678) -# debugpy.breakpoint() - - script_dir = pathlib.Path(__file__).parent.parent sys.path.append(os.fspath(script_dir)) -sys.path.insert(0, os.fspath(script_dir / "lib" / "python")) -from django_runner import django_execution_runner # noqa: E402 +from django_handler import django_execution_runner # noqa: E402 from testing_tools import process_json_util, socket_manager # noqa: E402 from unittestadapter.pvsc_utils import ( # noqa: E402 @@ -336,12 +323,11 @@ def send_run_data(raw_data, test_run_pipe): if test_ids_from_buffer: # Check to see if we are running django tests. manage_py_path = os.environ.get("MANAGE_PY_PATH") - print("DJANGO_TEST_ENABLED = ", manage_py_path) - manage_py_path = "/Users/eleanorboyd/testingFiles/django-polls/manage.py" if manage_py_path: # run django runner - print("running django runner") - django_execution_runner(start_dir, manage_py_path, test_ids_from_buffer) + args = argv[index + 1 :] or [] + django_execution_runner(manage_py_path, test_ids_from_buffer, args) + # the django run subprocesses sends the eot payload. else: print("running unittest runner") # Perform test execution. @@ -373,5 +359,3 @@ def send_run_data(raw_data, test_run_pipe): msg = "Error: Could not parse test ids from stdin" print(msg) raise VSCodeUnittestError(msg) from exc - eot_payload: EOTPayloadDict = {"command_type": "execution", "eot": True} - send_post_request(eot_payload, test_run_pipe) From da8565899efeef96d289386918a38279aa30c600 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Thu, 8 Aug 2024 15:25:55 -0700 Subject: [PATCH 04/17] cleanup --- python_files/unittestadapter/discovery.py | 10 ++--- .../unittestadapter/django_handler.py | 1 - .../unittestadapter/django_test_runner.py | 12 +++-- python_files/unittestadapter/execution.py | 44 +++++++++---------- 4 files changed, 35 insertions(+), 32 deletions(-) diff --git a/python_files/unittestadapter/discovery.py b/python_files/unittestadapter/discovery.py index 20eacfdb84f6..c7039c49c042 100644 --- a/python_files/unittestadapter/discovery.py +++ b/python_files/unittestadapter/discovery.py @@ -8,11 +8,7 @@ import unittest from typing import List, Optional -script_dir = pathlib.Path(__file__).parent.parent -sys.path.append(os.fspath(script_dir)) -sys.path.insert(0, os.fspath(script_dir / "lib" / "python")) - -from django_handler import django_discovery_runner # noqa: E402 +from django_handler import django_discovery_runner script_dir = pathlib.Path(__file__).parent.parent sys.path.append(os.fspath(script_dir)) @@ -76,6 +72,10 @@ def discover_tests( 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 diff --git a/python_files/unittestadapter/django_handler.py b/python_files/unittestadapter/django_handler.py index 60b1b4535634..eeb47de0f108 100644 --- a/python_files/unittestadapter/django_handler.py +++ b/python_files/unittestadapter/django_handler.py @@ -5,7 +5,6 @@ import pathlib import subprocess import sys -import traceback script_dir = pathlib.Path(__file__).parent.parent sys.path.append(os.fspath(script_dir)) diff --git a/python_files/unittestadapter/django_test_runner.py b/python_files/unittestadapter/django_test_runner.py index 47d1bc509c90..4d336d0bfc75 100644 --- a/python_files/unittestadapter/django_test_runner.py +++ b/python_files/unittestadapter/django_test_runner.py @@ -6,11 +6,9 @@ import pathlib import sys -print("PATH!", sys.path) -from django.test.runner import DiscoverRunner - script_dir = pathlib.Path(__file__).parent.parent sys.path.append(os.fspath(script_dir)) + from typing import TYPE_CHECKING # noqa: E402 from execution import UnittestTestResult # noqa: E402 @@ -22,6 +20,14 @@ send_post_request, ) +try: + from django.test.runner import DiscoverRunner +except ImportError: + raise ImportError( # noqa: B904 + "Django module not found. Please only use the environment variable MANAGE_PY_PATH if you want to use Django." + ) + + if TYPE_CHECKING: import unittest diff --git a/python_files/unittestadapter/execution.py b/python_files/unittestadapter/execution.py index 12e5a665a004..74ce8600dfbf 100644 --- a/python_files/unittestadapter/execution.py +++ b/python_files/unittestadapter/execution.py @@ -318,30 +318,28 @@ def send_run_data(raw_data, test_run_pipe): raise VSCodeUnittestError(msg) from e try: - if raw_json and "params" in raw_json: + if raw_json and "params" in raw_json and raw_json["params"]: test_ids_from_buffer = raw_json["params"] - if test_ids_from_buffer: - # Check to see if we are running django tests. - manage_py_path = os.environ.get("MANAGE_PY_PATH") - if manage_py_path: - # run django runner - args = argv[index + 1 :] or [] - django_execution_runner(manage_py_path, test_ids_from_buffer, args) - # the django run subprocesses sends the eot payload. - else: - print("running unittest runner") - # Perform test execution. - payload = run_tests( - start_dir, - test_ids_from_buffer, - pattern, - top_level_dir, - verbosity, - failfast, - locals_, - ) - eot_payload: EOTPayloadDict = {"command_type": "execution", "eot": True} - send_post_request(eot_payload, test_run_pipe) + # Check to see if we are running django tests. + manage_py_path = os.environ.get("MANAGE_PY_PATH") + if manage_py_path: + # run django runner + args = argv[index + 1 :] or [] + django_execution_runner(manage_py_path, test_ids_from_buffer, args) + # the django run subprocesses sends the eot payload. + else: + # Perform test execution. + payload = run_tests( + start_dir, + test_ids_from_buffer, + pattern, + top_level_dir, + verbosity, + failfast, + locals_, + ) + eot_payload: EOTPayloadDict = {"command_type": "execution", "eot": True} + send_post_request(eot_payload, test_run_pipe) else: # No test ids received from buffer cwd = os.path.abspath(start_dir) # noqa: PTH100 From 4b41008c2cfcedbb5adf644c4a7610bf2b365936 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Fri, 9 Aug 2024 12:30:44 -0700 Subject: [PATCH 05/17] adding tests --- python_files/tests/pytestadapter/helpers.py | 22 ++- .../.data/simple_django/db.sqlite3 | Bin 0 -> 143360 bytes .../.data/simple_django/manage.py | 22 +++ .../.data/simple_django/mysite/__init__.py | 0 .../.data/simple_django/mysite/asgi.py | 16 +++ .../.data/simple_django/mysite/settings.py | 126 ++++++++++++++++++ .../.data/simple_django/mysite/urls.py | 22 +++ .../.data/simple_django/mysite/wsgi.py | 16 +++ .../.data/simple_django/polls/__init__.py | 0 .../.data/simple_django/polls/admin.py | 3 + .../.data/simple_django/polls/apps.py | 6 + .../polls/migrations/__init__.py | 0 .../.data/simple_django/polls/models.py | 22 +++ .../.data/simple_django/polls/tests.py | 35 +++++ .../.data/simple_django/polls/urls.py | 15 +++ .../.data/simple_django/polls/views.py | 17 +++ .../django_test_execution_script.py | 16 +++ .../tests/unittestadapter/test_discovery.py | 45 ++++++- .../tests/unittestadapter/test_execution.py | 52 +++++++- python_files/unittestadapter/discovery.py | 6 +- .../unittestadapter/django_handler.py | 4 +- .../unittestadapter/django_test_runner.py | 1 + python_files/unittestadapter/execution.py | 2 +- 23 files changed, 435 insertions(+), 13 deletions(-) create mode 100644 python_files/tests/unittestadapter/.data/simple_django/db.sqlite3 create mode 100755 python_files/tests/unittestadapter/.data/simple_django/manage.py create mode 100644 python_files/tests/unittestadapter/.data/simple_django/mysite/__init__.py create mode 100644 python_files/tests/unittestadapter/.data/simple_django/mysite/asgi.py create mode 100644 python_files/tests/unittestadapter/.data/simple_django/mysite/settings.py create mode 100644 python_files/tests/unittestadapter/.data/simple_django/mysite/urls.py create mode 100644 python_files/tests/unittestadapter/.data/simple_django/mysite/wsgi.py create mode 100644 python_files/tests/unittestadapter/.data/simple_django/polls/__init__.py create mode 100644 python_files/tests/unittestadapter/.data/simple_django/polls/admin.py create mode 100644 python_files/tests/unittestadapter/.data/simple_django/polls/apps.py create mode 100644 python_files/tests/unittestadapter/.data/simple_django/polls/migrations/__init__.py create mode 100644 python_files/tests/unittestadapter/.data/simple_django/polls/models.py create mode 100644 python_files/tests/unittestadapter/.data/simple_django/polls/tests.py create mode 100644 python_files/tests/unittestadapter/.data/simple_django/polls/urls.py create mode 100644 python_files/tests/unittestadapter/.data/simple_django/polls/views.py create mode 100644 python_files/tests/unittestadapter/django_test_execution_script.py diff --git a/python_files/tests/pytestadapter/helpers.py b/python_files/tests/pytestadapter/helpers.py index 9ec0550fb4b9..e3552070feb7 100644 --- a/python_files/tests/pytestadapter/helpers.py +++ b/python_files/tests/pytestadapter/helpers.py @@ -200,10 +200,23 @@ def runner(args: List[str]) -> Optional[List[Dict[str, Any]]]: def runner_with_cwd(args: List[str], path: pathlib.Path) -> Optional[List[Dict[str, Any]]]: """Run the pytest discovery and return the JSON data from the server.""" - process_args: List[str] = [sys.executable, "-m", "pytest", "-p", "vscode_pytest", "-s", *args] + return runner_with_cwd_env(args, path, {}) + + +def runner_with_cwd_env( + args: List[str], path: pathlib.Path, env_add: Dict[str, str] +) -> Optional[List[Dict[str, Any]]]: + """Run the pytest discovery and return the JSON data from the server.""" + process_args: List[str] + pipe_name: str + if "MANAGE_PY_PATH" in env_add: + process_args = [sys.executable, *args] + pipe_name = generate_random_pipe_name("unittest-discovery-test") + else: + process_args = [sys.executable, "-m", "pytest", "-p", "vscode_pytest", "-s", *args] + pipe_name = generate_random_pipe_name("pytest-discovery-test") # Generate pipe name, pipe name specific per OS type. - pipe_name = generate_random_pipe_name("pytest-discovery-test") # Windows design if sys.platform == "win32": @@ -244,6 +257,8 @@ def runner_with_cwd(args: List[str], path: pathlib.Path) -> Optional[List[Dict[s "PYTHONPATH": os.fspath(pathlib.Path(__file__).parent.parent.parent), } ) + if env_add: + env.update(env_add) server = UnixPipeServer(pipe_name) server.start() @@ -255,10 +270,11 @@ def runner_with_cwd(args: List[str], path: pathlib.Path) -> Optional[List[Dict[s ) t1.start() - t2 = threading.Thread( + t2: threading.Thread = threading.Thread( target=_run_test_code, args=(process_args, env, path, completed), ) + t2.start() t1.join() diff --git a/python_files/tests/unittestadapter/.data/simple_django/db.sqlite3 b/python_files/tests/unittestadapter/.data/simple_django/db.sqlite3 new file mode 100644 index 0000000000000000000000000000000000000000..519ec5e1a11ca6cfaa9418e6540bca99936887b3 GIT binary patch literal 143360 zcmeI5Yit|Yb;mg(#fL?5^sqd;Ys(r@tF;+fE50duv)yRDj8|P-UfXhZ4RkRbk|Ua4 zij+ypk8aVHT5l30ZGk3efEH*|1Sr~~{m>T>6ls7WMUlP}6iI+SQncuYv_aFfDYE+^ z3D9%z%y1qgC9S&wcI7`|k7n*Y_sluJd*{yFbMKH>FJGv2G|> zpYWvNh28uA#l4KzHIdz=WGYcEZ59UGJ#iqr`;_fx zvRQzvy(tEzuD2lU&L7BJhqFqeTuG<0rG(XT`Us5=Z$EvqR+HT0Gza#Lnv_EgIs15& zs%FtqH8zoQv(eES9i?+`TVqWswVBE1v`mRtjU^`zs1qw!8K%W%w(?$^ zkYBvAc5(H}b@_$jb$RvL#-+7&QhBkszCosPB?C)cDSohcrMP~sc-3}N;~a7MIhK2c zo3ysRwz0N);lgzzvG`nHBkZ@SPrJq9VCRVMCdLBN(von0%7vC+VK$)pad;R;J^Nh+ zPwyiRRx4cgfXH5dUnn5W&I%u%cOvUGz9T~*JrJF3wcWncY*CB6t+vWH)Yj=_Y9-!l zmefe143{(w*;7K>QfqbVO!=&JCMwSi?WmiZ{YFxbTJF?tla+4!na9e_X04$SqCWTI z$s>NLI4gK~ci6sFr^hvgDvIRk`G)No4>-x*?%3{nFd!8Q!u`Bezj|A;t}gw;=t~U^ zsQEsj2WkeomUp}{6Lrfe_5k(6=wrI${(v+$Cv;zR0p-lOXMthh9tf7R4X*UJ=Xg5c zm(I`a*(`M8db{BJL3)a2|2?~pj|Qa0Md9_Iw*0Xua;L0A{{YXd+;_metj#^DPWL0h z-W6K8sWz%j$IaU?Y5$v3@BVD{ObMGU@$U1!fYeM2-PicVHsDl>y1lK`)sj|Mw&+Qu zuH1x5{+&Jo1`!M!!>Pay+}G2eSOv4I6Qiyo`0-hh-&3%fhK#s`!f zkmPgL`~Zn}p!Sn?Z`7}5?{^E$AHSO!%PouwVYqxf^Mp<(mdL90`GEcK> z@C=oVcdrWpX?a=LRqgDyYSot7VS_P)iQQ^e;{ha#k%jLvd$Ziv%hfXQqg*? z@9skV!1VD5ZKuXgdc-e^pAswLT=Z+vpN&2n`9b7!kvox0_#5F*hd&ga82i%L8)Gk$ z7(O5X0w4eaAOHd&00JOzhzTU7Mm^HP>UOhUZzmFoq*80tIyJSPN~AJNiTqMJDW{TW zQ>nAbs7>|9StYXW{&+5&z*B8Aal|)x6!qtItre7BmoVDwQv!*GB2OC7UW+wFaq3 zv*0pKZnf_5dS-#xW<%Mmt5v0ZqgkW4z(7^WOnxP?>LXP|*-(|TR2gQ`Roi502dhXF z(z%3}_adf>A-#xEwTWCJm0cborVD2C*t0;UkWJ-sd4bR^xY6xhFu#(`rP%mCC{{e; ze~4cZKPTQ6KQ8{V_?wh~4+ww&2!H?xfB*=900@8p2!H?xfWX@#Fdp_U2t0;pgd4n5 zKJTL7T4p0hLf*L`Pvp_I@DcBA$h%NVZfS<;M9nBDQINy zpaY9pObgy+Q`%aoFm;4H35r#Z_+R2*iT@yeUi>w&D&7@8{4<^}tT2;2T> z#5X<7!w4)w;EVdEC4R)i2NYwNp5uqsQjC!aI%|lB1k42yZ2n&!%Xq|p5`SO(s8|xu zis9%#NB=PTsc0*@9(^)88Ts$X-$s5n@>3klCTEKi)q+OA@D+ zgY-tYMqk&UCo&m5!IV0wOVM{?G^%zfJ;9JVIYUx&@gQ5FZYiQ8Q_BgiCBUsQ2NOMe z%!Jd&Nop}2Ff1~YZ8n)nrytWL4Xey#CTZAZCdD2l$%Ul=v&>9mV$3%4IQ9sa@pJ2p zK~2v%0s9~OM0LH`(6CDNU9S~N)u+vq*u+?55PVavRi`h|T zuuGa5Z5KU3FL(&Er+nOUHtg$}%noFFd4oLn8K#=~hK*c;-ppVUhHa*VVH7)jj9$4A zW)^(R7&Dt$!S+wmYZTNLz5MKanAtl$KZ?$ADVx2(!^jWunYbE9G%|fgp4$$&EEf-91w&LFO+eoThgKLW^0$P`jML z&XS~e14z;^(k^M3*prOWO97#UWoDpVjG5PyVe|iTr0WsCO|$=hMlpb25#JEE#FxbL zVn&=3$3-Ff?daE|e-r&|^jD*AL>ti`i=L0BqO;L(M4do7SV`C zeZq{;3wA^}R9k!_%n3LG8e@8rp@c^yj_@;E;ffHq&mEu$NbaO#V?6}A^xfOdGWL2?}@)H{<`>S@t4F;ia#fQMC^*Mi92Fbtchju74aqU zviJfizy}0C00ck)1V8`;KmY_l00ck)1P(iaQLi8bWxk!|+mn1d!?uAFe0!X4ALH9c z+1CFE-%5OYjBlsecJwIUPVwy|-%hZtZ=7#MzK!y2gl)ZHz8&M+BYYcT+mRsO2Kd&` zx1&C9Nbs?Zmu*J8-cShh|A)PC!aooI0T2KI5C8!X009sH0T2KI5b$_x-~R_3AOHd& z00JNY0w4eaAOHd&00JNY0*90U#{Y-3V&N7DfB*=900@8p2!H?xfB*=900`I!VEk`` zfn5*)0T2KI5C8!X009sH0T2KI5ICd+F#bQJ6$`gO00ck)1V8`;KmY_l00ck)1VF$> z0ONlf4D5mc2!H?xfB*=900@8p2!H?xfWRRo5D9$OBMM*ih>u7AF7#FZ7b17UAM+m_ zeaZKWV{e6?2^PoR@BMS(izAnWPk6rUdD;b`J3bYVE-eY&WUW!r?y5VT8_G^wYbn)M zb7#A)@7F3yPEDq?%1X&CaP-`j;_60GUR!^z__7>xXZMwl$(Ppq#bWa57(Z*R5{q}A znG8s;l1c5oP2v_RWLqJV&L+xW6n`)!lRMg5=t)bML zRrm2p!>^6iXD<}xm^+&hC75!p(b1|}OJ2XUA+KM%a6x|Y%G$-%E7#>0ir3}UYa5r= z)=AOD;`#=8&@A25${n&MCU>;E9bUjnZfV=Cn0#Apm2ar6)2T!v-qT7#YPmyRDVue* z8k5^wYQ0XUmUkNMTD757dXnD=4axS2iom}^&xA1q!euAeJj?eVY|Fp$LM=a}3T&TVadZDVcq!iDQbV(~dT zi`f$U;n~m7YiI}7Yq;F0ZINpgwWG1U9+`OeN2P$&Tot;vtX9KK;Tx;9xLZzgvsB7% zrpi5o^o!0`;IUyB=>avno4>mlBVe75Rm{VB5ogNTav`Phs_47oCkIqRpOCv-Ff`h# zps&av{EOoO=@sgME?RA=ubke;v(BfKOeM;t&B9>2Ck|w{uHL3>Kak-O?RbypqhQxl%5ja@~a8r5`cO z8g@H&%OB)ywuUy)V&*c>p!%im-C;NNR8vO-(q($w$hsWY8=rNN6!O_@A)Cqc5F5`( z#|B&;{B*+0_={pz`v`TB*?ml_&S^L;`?n!(=B2R-xN z*~Z*gCkIe(Q7}B&=e8cF=iXz|+?>#T)ef|~ax5T~>7IMtRry2Pp<2jlY9(oV5O#|` zI;=go1$%AV)ah;*?gI9P9Dg@Hx!)A26Y&;=-FXXVU(>U$D610LO(;>Wq*K{a!sl|$RR=w8Il(u$jM{ATd*P+*e(3N?*p>Ans zsARl*T?k0a%fhb8bsD3kty;CEvSCZRFJZ)?*BoA11s^#FipRZ|JzafI*v;~A$ab?{Zz~jE zX|rI6^JvZ)2ytdUP!z;GkHY4dZ4nRGg!1-INnyc`K_QRs(PTin8W*|+9%nS7H*K0W zW^>Tm-R)XSk6EjTj=me&J?PC@3q?OZgKkXvSeh22y3D3f89 zv#e`+#+k_!o6fwd-Ln{FY;*4RnS6>)n(~B9?VmWen>iqa^i;wxU5X1HPSXgYQP{G{ zj}@nJO44~emVDAL6=#PPwxXZO-fplvu^8aN_$>~KXYlx|wI@2;ot5!GZO$27OJ?mH z^{J@1_vby6mlv5E{HOTDj_E;T-7~&Fd*mCQ)In&xo>}5;<-_x@zmN9z zn#v0dK*55$4@6>HZMW|>9U2S*Rij!Kye(BKy3ui> zQnOj_tzV#AmbyP_66B}YLy|p(*45ZRYH)}2OeGkLp-N$1zvA*b-ShwQaihaEao6|^bX7V{Lqt6yua#5p3?W?A& z+@V@*vlW&!YXz>;QcrYsE+E}o6m~^xBE~AxcY<=WqA{QlN>8@`YR;6(+KN_kn|6F3)9e+-`o*%LXSj*35;2;<_uj_(NUV=MB%6>-Rx-X{`C-aFSENKmumE6 zO>3*o1=;rH61t(LxN(!(=V>91`Ts*0)PYMN00JNY0w4eaAOHd&00JNY0wCZbfbqWz z2o6C21V8`;KmY_l00ck)1V8`;K;RG(!1(_VRw`Tq0T2KI5C8!X009sH0T2KI5C8!e z0gV4$KyU~GAOHd&00JNY0w4eaAOHd&00M`Q0LK4^uu|a?2!H?xfB*=900@8p2!H?x zfB*=%2t?>OC~kVhZ;5{|{-yZG;-|%riFYUg9}oZm5C8!X009sH0T2KI5C8!X0D-qj zKn(gk(xdzf`t@G}muz$+$^UNGKeu=GZ{5?ME^DN98KWEapx2?5p`(dBY zGd<0IxrqHFOTQ|XZ&rt191=l z0T2KI5C8!X009sH0T2KI5CDPq6am}%fAKwKIE)1W5C8!X009sH0T2KI5C8!X009s< zFag{6|G;G+2m&Ag0w4eaAOHd&00JNY0w4eaAn@KIz}Ei@;@3R%4<8T!0T2KI5C8!X z009sH0T2KI5CDPq5`p+>deMJw^`#qX=TuwX*?uOKNM)81`K5GHP9@K#(q~hNCsz{5 zL?OY(|6cJ+9`S#~f1^M6fB*=900@8p2!H?xfB*=900@8p2)z3VOnOH>r-C6i@@E5o zxzemY6CN4$91n&zZfJ7*)q4?P)H4$dtv5UJ)=v2bTmSDBzvB_VFTO>8@Bsl3009sH g0T2KI5C8!X009sH0T4J81bp6*;ANW;uQwF>KZ^#Ur2qf` literal 0 HcmV?d00001 diff --git a/python_files/tests/unittestadapter/.data/simple_django/manage.py b/python_files/tests/unittestadapter/.data/simple_django/manage.py new file mode 100755 index 000000000000..a7da6671aab8 --- /dev/null +++ b/python_files/tests/unittestadapter/.data/simple_django/manage.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +"""Django's command-line utility for administrative tasks.""" +import os +import sys + + +def main(): + """Run administrative tasks.""" + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings') + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc + execute_from_command_line(sys.argv) + + +if __name__ == '__main__': + main() diff --git a/python_files/tests/unittestadapter/.data/simple_django/mysite/__init__.py b/python_files/tests/unittestadapter/.data/simple_django/mysite/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/python_files/tests/unittestadapter/.data/simple_django/mysite/asgi.py b/python_files/tests/unittestadapter/.data/simple_django/mysite/asgi.py new file mode 100644 index 000000000000..fcc892069d16 --- /dev/null +++ b/python_files/tests/unittestadapter/.data/simple_django/mysite/asgi.py @@ -0,0 +1,16 @@ +""" +ASGI config for mysite project. + +It exposes the ASGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/3.2/howto/deployment/asgi/ +""" + +import os + +from django.core.asgi import get_asgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings') + +application = get_asgi_application() diff --git a/python_files/tests/unittestadapter/.data/simple_django/mysite/settings.py b/python_files/tests/unittestadapter/.data/simple_django/mysite/settings.py new file mode 100644 index 000000000000..6ddb9d4616fb --- /dev/null +++ b/python_files/tests/unittestadapter/.data/simple_django/mysite/settings.py @@ -0,0 +1,126 @@ +""" +Django settings for mysite project. + +Generated by 'django-admin startproject' using Django 3.2.22. + +For more information on this file, see +https://docs.djangoproject.com/en/3.2/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/3.2/ref/settings/ +""" + +from pathlib import Path + +# Build paths inside the project like this: BASE_DIR / 'subdir'. +BASE_DIR = Path(__file__).resolve().parent.parent + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 'django-insecure-f2*4t6_!)%2d@91f(*ybqura=uvf5h4@2(qe+(zogq=0q=1iy0' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = [] + + +# Application definition + +INSTALLED_APPS = [ + "polls.apps.PollsConfig", + "django.contrib.admin", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", +] + +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'mysite.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'mysite.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/3.2/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': BASE_DIR / 'db.sqlite3', + } +} + + +# Password validation +# https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/3.2/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_L10N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/3.2/howto/static-files/ + +STATIC_URL = '/static/' + +# Default primary key field type +# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field + +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' diff --git a/python_files/tests/unittestadapter/.data/simple_django/mysite/urls.py b/python_files/tests/unittestadapter/.data/simple_django/mysite/urls.py new file mode 100644 index 000000000000..5f33223268b5 --- /dev/null +++ b/python_files/tests/unittestadapter/.data/simple_django/mysite/urls.py @@ -0,0 +1,22 @@ +"""mysite URL Configuration + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/3.2/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: path('', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.urls import include, path + 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) +""" +from django.contrib import admin +from django.urls import include, path + +urlpatterns = [ + path("polls/", include("polls.urls")), + path("admin/", admin.site.urls), +] diff --git a/python_files/tests/unittestadapter/.data/simple_django/mysite/wsgi.py b/python_files/tests/unittestadapter/.data/simple_django/mysite/wsgi.py new file mode 100644 index 000000000000..fa2e052a9032 --- /dev/null +++ b/python_files/tests/unittestadapter/.data/simple_django/mysite/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for mysite project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/3.2/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings') + +application = get_wsgi_application() diff --git a/python_files/tests/unittestadapter/.data/simple_django/polls/__init__.py b/python_files/tests/unittestadapter/.data/simple_django/polls/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/python_files/tests/unittestadapter/.data/simple_django/polls/admin.py b/python_files/tests/unittestadapter/.data/simple_django/polls/admin.py new file mode 100644 index 000000000000..8c38f3f3dad5 --- /dev/null +++ b/python_files/tests/unittestadapter/.data/simple_django/polls/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/python_files/tests/unittestadapter/.data/simple_django/polls/apps.py b/python_files/tests/unittestadapter/.data/simple_django/polls/apps.py new file mode 100644 index 000000000000..5184937ac15e --- /dev/null +++ b/python_files/tests/unittestadapter/.data/simple_django/polls/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class PollsConfig(AppConfig): + default_auto_field = "django.db.models.BigAutoField" + name = "polls" diff --git a/python_files/tests/unittestadapter/.data/simple_django/polls/migrations/__init__.py b/python_files/tests/unittestadapter/.data/simple_django/polls/migrations/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/python_files/tests/unittestadapter/.data/simple_django/polls/models.py b/python_files/tests/unittestadapter/.data/simple_django/polls/models.py new file mode 100644 index 000000000000..d83dc41ff572 --- /dev/null +++ b/python_files/tests/unittestadapter/.data/simple_django/polls/models.py @@ -0,0 +1,22 @@ +from django.db import models +from django.utils import timezone +import datetime + + +class Question(models.Model): + question_text = models.CharField(max_length=200) + pub_date = models.DateTimeField("date published") + def __str__(self): + return self.question_text + def was_published_recently(self): + if self.pub_date > timezone.now(): + return False + return self.pub_date >= timezone.now() - datetime.timedelta(days=1) + + +class Choice(models.Model): + question = models.ForeignKey(Question, on_delete=models.CASCADE) + choice_text = models.CharField(max_length=200) + votes = models.IntegerField(default=0) + def __str__(self): + return self.choice_text diff --git a/python_files/tests/unittestadapter/.data/simple_django/polls/tests.py b/python_files/tests/unittestadapter/.data/simple_django/polls/tests.py new file mode 100644 index 000000000000..89b0577765c5 --- /dev/null +++ b/python_files/tests/unittestadapter/.data/simple_django/polls/tests.py @@ -0,0 +1,35 @@ +from django.utils import timezone +from django.test import TestCase +from .models import Question +import datetime + +class QuestionModelTests(TestCase): + def test_was_published_recently_with_future_question(self): + """ + was_published_recently() returns False for questions whose pub_date + is in the future. + """ + time = timezone.now() + datetime.timedelta(days=30) + future_question = Question(pub_date=time) + self.assertIs(future_question.was_published_recently(), False) + + def test_was_published_recently_with_future_question_2(self): + """ + was_published_recently() returns False for questions whose pub_date + is in the future. + """ + time = timezone.now() + datetime.timedelta(days=30) + future_question = Question(pub_date=time) + self.assertIs(future_question.was_published_recently(), True) + + def test_question_creation_and_retrieval(self): + """ + Test that a Question can be created and retrieved from the database. + """ + time = timezone.now() + question = Question.objects.create(pub_date=time, question_text="What's new?") + retrieved_question = Question.objects.get(id=question.id) + self.assertEqual(question, retrieved_question) + self.assertEqual(retrieved_question.question_text, "What's new?") + self.assertEqual(retrieved_question.pub_date, time) + diff --git a/python_files/tests/unittestadapter/.data/simple_django/polls/urls.py b/python_files/tests/unittestadapter/.data/simple_django/polls/urls.py new file mode 100644 index 000000000000..20e1b91d6a0b --- /dev/null +++ b/python_files/tests/unittestadapter/.data/simple_django/polls/urls.py @@ -0,0 +1,15 @@ + +from django.urls import path + +from . import views + +urlpatterns = [ + # ex: /polls/ + path("", views.index, name="index"), + # ex: /polls/5/ + path("/", views.detail, name="detail"), + # ex: /polls/5/results/ + path("/results/", views.results, name="results"), + # ex: /polls/5/vote/ + path("/vote/", views.vote, name="vote"), +] \ No newline at end of file diff --git a/python_files/tests/unittestadapter/.data/simple_django/polls/views.py b/python_files/tests/unittestadapter/.data/simple_django/polls/views.py new file mode 100644 index 000000000000..a91dfcf0dedf --- /dev/null +++ b/python_files/tests/unittestadapter/.data/simple_django/polls/views.py @@ -0,0 +1,17 @@ +from django.http import HttpResponse + + +def index(request): + return HttpResponse("Hello, world. You're at the polls index.") + +def detail(request, question_id): + return HttpResponse("You're looking at question %s." % question_id) + + +def results(request, question_id): + response = "You're looking at the results of question %s." + return HttpResponse(response % question_id) + + +def vote(request, question_id): + return HttpResponse("You're voting on question %s." % question_id) \ No newline at end of file diff --git a/python_files/tests/unittestadapter/django_test_execution_script.py b/python_files/tests/unittestadapter/django_test_execution_script.py new file mode 100644 index 000000000000..4c7cf8d89939 --- /dev/null +++ b/python_files/tests/unittestadapter/django_test_execution_script.py @@ -0,0 +1,16 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +import os +import pathlib +import sys + +sys.path.append(os.fspath(pathlib.Path(__file__).parent.parent)) + +from unittestadapter.django_handler import django_execution_runner + +if __name__ == "__main__": + args = sys.argv[1:] + manage_py_path = args[0] + test_ids = args[1:] + django_execution_runner(manage_py_path, test_ids, []) diff --git a/python_files/tests/unittestadapter/test_discovery.py b/python_files/tests/unittestadapter/test_discovery.py index 9afff6762fcc..46a5ac3a6ed4 100644 --- a/python_files/tests/unittestadapter/test_discovery.py +++ b/python_files/tests/unittestadapter/test_discovery.py @@ -4,9 +4,10 @@ import os import pathlib import sys -from typing import List +from typing import Any, Dict, List import pytest +from python_files.tests.pytestadapter import helpers from unittestadapter.discovery import discover_tests from unittestadapter.pvsc_utils import TestNodeTypeEnum, parse_unittest_args @@ -290,3 +291,45 @@ def test_complex_tree() -> None: expected_discovery_test_output.complex_tree_expected_output, ["id_", "lineno", "name"], ) + + +TEST_DATA_PATH: pathlib.Path = pathlib.Path(__file__).parent / ".data" +PYTHON_FILES_PATH: pathlib.Path = pathlib.Path(__file__).parent.parent.parent +DISCOVERY_SCRIPT: str = os.fsdecode(PYTHON_FILES_PATH / "unittestadapter" / "discovery.py") + + +def test_simple_django_collect(): + data_path: str = os.fsdecode(pathlib.PurePath(TEST_DATA_PATH, "simple_django")) + manage_py_path: str = os.fsdecode(pathlib.PurePath(data_path, "manage.py")) + + actual = helpers.runner_with_cwd_env( + [ + DISCOVERY_SCRIPT, + "--udiscovery", + ], + data_path, + {"MANAGE_PY_PATH": manage_py_path}, + ) + + assert actual + actual_list: List[Dict[str, Any]] = actual + if actual_list is not None: + actual_item = actual_list.pop(0) + assert all(item in actual_item for item in ("status", "cwd", "error")) + assert ( + actual_item.get("status") == "success" + ), f"Status is not 'success', error is: {actual_item.get('error')}" + assert actual_item.get("cwd") == 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"]) == 2 + ) + assert ( + actual_item["tests"]["children"][0]["children"][0]["children"][0]["children"][0][ + "runID" + ] + == "polls.tests.QuestionModelTests.test_was_published_recently_with_future_question" + ) diff --git a/python_files/tests/unittestadapter/test_execution.py b/python_files/tests/unittestadapter/test_execution.py index 71f1ca1ec73b..0f642c897559 100644 --- a/python_files/tests/unittestadapter/test_execution.py +++ b/python_files/tests/unittestadapter/test_execution.py @@ -4,13 +4,17 @@ import os import pathlib import sys -from typing import TYPE_CHECKING, Dict, Optional +from typing import TYPE_CHECKING, Any, Dict, List, Optional from unittest.mock import patch import pytest +from python_files.tests.pytestadapter import helpers -script_dir = pathlib.Path(__file__).parent.parent.parent -sys.path.insert(0, os.fspath(script_dir / "lib" / "python")) +sys.path.append(os.fspath(pathlib.Path(__file__).parent)) +sys.path.append(os.fspath(pathlib.Path(__file__).parent.parent.parent)) + +python_files_path = pathlib.Path(__file__).parent.parent.parent +sys.path.insert(0, os.fspath(python_files_path / "lib" / "python")) from unittestadapter.execution import run_tests # noqa: E402 @@ -296,3 +300,45 @@ def test_incorrect_path(): assert all(item in actual for item in ("cwd", "status", "error")) assert actual["status"] == "error" assert actual["cwd"] == os.fspath(TEST_DATA_PATH / "unknown_folder") + + +def test_basic_run_django(): + """This test runs on a simple django project with three tests, two of which pass and one that fails.""" + data_path: str = os.fsdecode(pathlib.PurePath(TEST_DATA_PATH, "simple_django")) + manage_py_path: str = os.fsdecode(pathlib.PurePath(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", + "polls.tests.QuestionModelTests.test_was_published_recently_with_future_question_2", + "polls.tests.QuestionModelTests.test_question_creation_and_retrieval", + ] + script_str = os.fsdecode(execution_script) + actual = helpers.runner_with_cwd_env( + [script_str, manage_py_path, *test_ids], + data_path, + {"MANAGE_PY_PATH": manage_py_path}, + ) + assert actual + actual_list: List[Dict[str, Dict[str, Any]]] = actual + actual_result_dict = {} + assert len(actual_list) == 3 + if actual_list is not None: + for actual_item in actual_list: + assert all(item in actual_item for item in ("status", "cwd", "result")) + assert actual_item.get("cwd") == data_path + actual_result_dict.update(actual_item["result"]) + for test_id in test_ids: + assert test_id in actual_result_dict + id_result = actual_result_dict[test_id] + assert id_result is not None + assert "outcome" in id_result + if ( + test_id + == "polls.tests.QuestionModelTests.test_was_published_recently_with_future_question_2" + ): + assert id_result["outcome"] == "failure" + else: + assert id_result["outcome"] == "success" diff --git a/python_files/unittestadapter/discovery.py b/python_files/unittestadapter/discovery.py index c7039c49c042..49eefc0eae91 100644 --- a/python_files/unittestadapter/discovery.py +++ b/python_files/unittestadapter/discovery.py @@ -8,11 +8,11 @@ import unittest from typing import List, Optional -from django_handler import django_discovery_runner - -script_dir = pathlib.Path(__file__).parent.parent +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, diff --git a/python_files/unittestadapter/django_handler.py b/python_files/unittestadapter/django_handler.py index eeb47de0f108..1bacb97df9ec 100644 --- a/python_files/unittestadapter/django_handler.py +++ b/python_files/unittestadapter/django_handler.py @@ -6,7 +6,7 @@ import subprocess import sys -script_dir = pathlib.Path(__file__).parent.parent +script_dir = pathlib.Path(__file__).parent sys.path.append(os.fspath(script_dir)) sys.path.insert(0, os.fspath(script_dir / "lib" / "python")) @@ -119,4 +119,4 @@ def django_execution_runner(manage_py_path: str, test_ids: list[str], args: list "Connection failure, likely means failure in Django subprocess run, see specific error output above." ) except Exception as e: - raise VSCodeUnittestError(f"Error during Django test execution: {e}") # noqa: B904 + print(f"Error during Django test execution: {e}", file=sys.stderr) diff --git a/python_files/unittestadapter/django_test_runner.py b/python_files/unittestadapter/django_test_runner.py index 4d336d0bfc75..e638f943113e 100644 --- a/python_files/unittestadapter/django_test_runner.py +++ b/python_files/unittestadapter/django_test_runner.py @@ -58,6 +58,7 @@ def run_tests(self, test_labels, **kwargs): "cwd": os.fspath(top_level_dir), "status": "success", "tests": None, + "error": None, } payload["tests"] = tests if tests is not None else None if len(error): diff --git a/python_files/unittestadapter/execution.py b/python_files/unittestadapter/execution.py index 74ce8600dfbf..ce1356a4d604 100644 --- a/python_files/unittestadapter/execution.py +++ b/python_files/unittestadapter/execution.py @@ -19,7 +19,7 @@ sysconfig.get_paths()["scripts"] + os.pathsep + os.environ[path_var_name] ) -script_dir = pathlib.Path(__file__).parent.parent +script_dir = pathlib.Path(__file__).parent sys.path.append(os.fspath(script_dir)) from django_handler import django_execution_runner # noqa: E402 From 0d8f0d6e7b6c252044d4f6d7846ac9d9fa1b5ca5 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Fri, 9 Aug 2024 12:47:30 -0700 Subject: [PATCH 06/17] pyright and license --- .../unittestadapter/.data/simple_django/mysite/__init__.py | 2 ++ .../unittestadapter/.data/simple_django/mysite/asgi.py | 2 ++ .../unittestadapter/.data/simple_django/mysite/settings.py | 2 ++ .../unittestadapter/.data/simple_django/mysite/urls.py | 2 ++ .../unittestadapter/.data/simple_django/mysite/wsgi.py | 2 ++ .../unittestadapter/.data/simple_django/polls/__init__.py | 2 ++ .../unittestadapter/.data/simple_django/polls/admin.py | 5 ++--- .../unittestadapter/.data/simple_django/polls/apps.py | 3 +++ .../.data/simple_django/polls/migrations/__init__.py | 2 ++ .../unittestadapter/.data/simple_django/polls/models.py | 3 +++ .../unittestadapter/.data/simple_django/polls/tests.py | 3 +++ .../unittestadapter/.data/simple_django/polls/urls.py | 4 +++- .../unittestadapter/.data/simple_django/polls/views.py | 4 +++- python_files/tests/unittestadapter/test_discovery.py | 6 +++--- python_files/tests/unittestadapter/test_execution.py | 2 +- python_files/unittestadapter/django_handler.py | 6 +++--- python_files/unittestadapter/django_test_runner.py | 5 ++--- python_files/unittestadapter/pvsc_utils.py | 2 +- src/testMultiRootWkspc/workspace5/djangoApp/home/views.py | 7 +++---- src/testMultiRootWkspc/workspace5/djangoApp/manage.py | 2 +- src/testMultiRootWkspc/workspace5/djangoApp/mysite/urls.py | 6 +++--- 21 files changed, 48 insertions(+), 24 deletions(-) diff --git a/python_files/tests/unittestadapter/.data/simple_django/mysite/__init__.py b/python_files/tests/unittestadapter/.data/simple_django/mysite/__init__.py index e69de29bb2d1..5b7f7a925cc0 100644 --- a/python_files/tests/unittestadapter/.data/simple_django/mysite/__init__.py +++ b/python_files/tests/unittestadapter/.data/simple_django/mysite/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. diff --git a/python_files/tests/unittestadapter/.data/simple_django/mysite/asgi.py b/python_files/tests/unittestadapter/.data/simple_django/mysite/asgi.py index fcc892069d16..709745cd8b68 100644 --- a/python_files/tests/unittestadapter/.data/simple_django/mysite/asgi.py +++ b/python_files/tests/unittestadapter/.data/simple_django/mysite/asgi.py @@ -1,3 +1,5 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. """ ASGI config for mysite project. diff --git a/python_files/tests/unittestadapter/.data/simple_django/mysite/settings.py b/python_files/tests/unittestadapter/.data/simple_django/mysite/settings.py index 6ddb9d4616fb..52795d7ec108 100644 --- a/python_files/tests/unittestadapter/.data/simple_django/mysite/settings.py +++ b/python_files/tests/unittestadapter/.data/simple_django/mysite/settings.py @@ -1,3 +1,5 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. """ Django settings for mysite project. diff --git a/python_files/tests/unittestadapter/.data/simple_django/mysite/urls.py b/python_files/tests/unittestadapter/.data/simple_django/mysite/urls.py index 5f33223268b5..34723c537af5 100644 --- a/python_files/tests/unittestadapter/.data/simple_django/mysite/urls.py +++ b/python_files/tests/unittestadapter/.data/simple_django/mysite/urls.py @@ -1,3 +1,5 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. """mysite URL Configuration The `urlpatterns` list routes URLs to views. For more information please see: diff --git a/python_files/tests/unittestadapter/.data/simple_django/mysite/wsgi.py b/python_files/tests/unittestadapter/.data/simple_django/mysite/wsgi.py index fa2e052a9032..41572422f0b0 100644 --- a/python_files/tests/unittestadapter/.data/simple_django/mysite/wsgi.py +++ b/python_files/tests/unittestadapter/.data/simple_django/mysite/wsgi.py @@ -1,3 +1,5 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. """ WSGI config for mysite project. diff --git a/python_files/tests/unittestadapter/.data/simple_django/polls/__init__.py b/python_files/tests/unittestadapter/.data/simple_django/polls/__init__.py index e69de29bb2d1..5b7f7a925cc0 100644 --- a/python_files/tests/unittestadapter/.data/simple_django/polls/__init__.py +++ b/python_files/tests/unittestadapter/.data/simple_django/polls/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. diff --git a/python_files/tests/unittestadapter/.data/simple_django/polls/admin.py b/python_files/tests/unittestadapter/.data/simple_django/polls/admin.py index 8c38f3f3dad5..5b7f7a925cc0 100644 --- a/python_files/tests/unittestadapter/.data/simple_django/polls/admin.py +++ b/python_files/tests/unittestadapter/.data/simple_django/polls/admin.py @@ -1,3 +1,2 @@ -from django.contrib import admin - -# Register your models here. +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. diff --git a/python_files/tests/unittestadapter/.data/simple_django/polls/apps.py b/python_files/tests/unittestadapter/.data/simple_django/polls/apps.py index 5184937ac15e..645ae5aed250 100644 --- a/python_files/tests/unittestadapter/.data/simple_django/polls/apps.py +++ b/python_files/tests/unittestadapter/.data/simple_django/polls/apps.py @@ -1,3 +1,6 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + from django.apps import AppConfig diff --git a/python_files/tests/unittestadapter/.data/simple_django/polls/migrations/__init__.py b/python_files/tests/unittestadapter/.data/simple_django/polls/migrations/__init__.py index e69de29bb2d1..5b7f7a925cc0 100644 --- a/python_files/tests/unittestadapter/.data/simple_django/polls/migrations/__init__.py +++ b/python_files/tests/unittestadapter/.data/simple_django/polls/migrations/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. diff --git a/python_files/tests/unittestadapter/.data/simple_django/polls/models.py b/python_files/tests/unittestadapter/.data/simple_django/polls/models.py index d83dc41ff572..f0181a0b7ccb 100644 --- a/python_files/tests/unittestadapter/.data/simple_django/polls/models.py +++ b/python_files/tests/unittestadapter/.data/simple_django/polls/models.py @@ -1,3 +1,6 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + from django.db import models from django.utils import timezone import datetime diff --git a/python_files/tests/unittestadapter/.data/simple_django/polls/tests.py b/python_files/tests/unittestadapter/.data/simple_django/polls/tests.py index 89b0577765c5..fee3ce154d09 100644 --- a/python_files/tests/unittestadapter/.data/simple_django/polls/tests.py +++ b/python_files/tests/unittestadapter/.data/simple_django/polls/tests.py @@ -1,3 +1,6 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + from django.utils import timezone from django.test import TestCase from .models import Question diff --git a/python_files/tests/unittestadapter/.data/simple_django/polls/urls.py b/python_files/tests/unittestadapter/.data/simple_django/polls/urls.py index 20e1b91d6a0b..25c1d536ccfb 100644 --- a/python_files/tests/unittestadapter/.data/simple_django/polls/urls.py +++ b/python_files/tests/unittestadapter/.data/simple_django/polls/urls.py @@ -1,3 +1,5 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. from django.urls import path @@ -12,4 +14,4 @@ path("/results/", views.results, name="results"), # ex: /polls/5/vote/ path("/vote/", views.vote, name="vote"), -] \ No newline at end of file +] diff --git a/python_files/tests/unittestadapter/.data/simple_django/polls/views.py b/python_files/tests/unittestadapter/.data/simple_django/polls/views.py index a91dfcf0dedf..28547aaa5534 100644 --- a/python_files/tests/unittestadapter/.data/simple_django/polls/views.py +++ b/python_files/tests/unittestadapter/.data/simple_django/polls/views.py @@ -1,3 +1,5 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. from django.http import HttpResponse @@ -14,4 +16,4 @@ def results(request, question_id): def vote(request, question_id): - return HttpResponse("You're voting on question %s." % question_id) \ No newline at end of file + return HttpResponse("You're voting on question %s." % question_id) diff --git a/python_files/tests/unittestadapter/test_discovery.py b/python_files/tests/unittestadapter/test_discovery.py index 46a5ac3a6ed4..a2974d750b41 100644 --- a/python_files/tests/unittestadapter/test_discovery.py +++ b/python_files/tests/unittestadapter/test_discovery.py @@ -299,8 +299,8 @@ def test_complex_tree() -> None: def test_simple_django_collect(): - data_path: str = os.fsdecode(pathlib.PurePath(TEST_DATA_PATH, "simple_django")) - manage_py_path: str = os.fsdecode(pathlib.PurePath(data_path, "manage.py")) + data_path: pathlib.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( [ @@ -315,7 +315,7 @@ def test_simple_django_collect(): actual_list: List[Dict[str, Any]] = actual if actual_list is not None: actual_item = actual_list.pop(0) - assert all(item in actual_item for item in ("status", "cwd", "error")) + 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')}" diff --git a/python_files/tests/unittestadapter/test_execution.py b/python_files/tests/unittestadapter/test_execution.py index 0f642c897559..d0e1b8b36123 100644 --- a/python_files/tests/unittestadapter/test_execution.py +++ b/python_files/tests/unittestadapter/test_execution.py @@ -304,7 +304,7 @@ def test_incorrect_path(): def test_basic_run_django(): """This test runs on a simple django project with three tests, two of which pass and one that fails.""" - data_path: str = os.fsdecode(pathlib.PurePath(TEST_DATA_PATH, "simple_django")) + data_path: pathlib.Path = pathlib.Path(TEST_DATA_PATH, "simple_django") manage_py_path: str = os.fsdecode(pathlib.PurePath(data_path, "manage.py")) execution_script: pathlib.Path = ( pathlib.Path(__file__).parent / "django_test_execution_script.py" diff --git a/python_files/unittestadapter/django_handler.py b/python_files/unittestadapter/django_handler.py index 1bacb97df9ec..fca939b98c0f 100644 --- a/python_files/unittestadapter/django_handler.py +++ b/python_files/unittestadapter/django_handler.py @@ -27,7 +27,7 @@ def django_discovery_runner(manage_py_path: str, args: list[str]) -> None: try: # Get path to the custom_test_runner.py parent folder, add to sys.path and new environment used for subprocess. custom_test_runner_dir = pathlib.Path(__file__).parent - sys.path.insert(0, custom_test_runner_dir) + sys.path.insert(0, os.fspath(custom_test_runner_dir)) env = os.environ.copy() if "PYTHONPATH" in env: env["PYTHONPATH"] = os.fspath(custom_test_runner_dir) + os.pathsep + env["PYTHONPATH"] @@ -78,7 +78,7 @@ def django_execution_runner(manage_py_path: str, test_ids: list[str], args: list try: # Get path to the custom_test_runner.py parent folder, add to sys.path. custom_test_runner_dir: pathlib.Path = pathlib.Path(__file__).parent - sys.path.insert(0, custom_test_runner_dir) + sys.path.insert(0, os.fspath(custom_test_runner_dir)) env: dict[str, str] = os.environ.copy() if "PYTHONPATH" in env: env["PYTHONPATH"] = os.fspath(custom_test_runner_dir) + os.pathsep + env["PYTHONPATH"] @@ -111,7 +111,7 @@ def django_execution_runner(manage_py_path: str, test_ids: list[str], args: list if subprocess_execution.returncode not in (0, 1): try: print("ERROR NUM", subprocess_execution.returncode) - test_run_pipe = os.getenv("TEST_RUN_PIPE") + test_run_pipe: str | None = os.getenv("TEST_RUN_PIPE") eot_payload: EOTPayloadDict = {"command_type": "discovery", "eot": True} send_post_request(eot_payload, test_run_pipe) except Exception: diff --git a/python_files/unittestadapter/django_test_runner.py b/python_files/unittestadapter/django_test_runner.py index e638f943113e..96a8b271f495 100644 --- a/python_files/unittestadapter/django_test_runner.py +++ b/python_files/unittestadapter/django_test_runner.py @@ -37,7 +37,7 @@ class CustomDiscoveryTestRunner(DiscoverRunner): def run_tests(self, test_labels, **kwargs): eot_payload: EOTPayloadDict = {"command_type": "discovery", "eot": True} - test_run_pipe: str = os.getenv("TEST_RUN_PIPE") + test_run_pipe: str | None = 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. " @@ -58,7 +58,6 @@ def run_tests(self, test_labels, **kwargs): "cwd": os.fspath(top_level_dir), "status": "success", "tests": None, - "error": None, } payload["tests"] = tests if tests is not None else None if len(error): @@ -93,7 +92,7 @@ def get_test_runner_kwargs(self): def suite_result(self, suite, result, **kwargs): # After run finishes, send EOT token. - test_run_pipe: str = os.getenv("TEST_RUN_PIPE") + test_run_pipe: str | None = os.getenv("TEST_RUN_PIPE") if not test_run_pipe: print("Error[vscode-unittest]: TEST_RUN_PIPE env var is not set.", file=sys.stderr) raise VSCodeUnittestError("Error[vscode-unittest]: TEST_RUN_PIPE env var is not set.") diff --git a/python_files/unittestadapter/pvsc_utils.py b/python_files/unittestadapter/pvsc_utils.py index 99577fc8e9c5..c62895c1e2e7 100644 --- a/python_files/unittestadapter/pvsc_utils.py +++ b/python_files/unittestadapter/pvsc_utils.py @@ -301,7 +301,7 @@ def parse_unittest_args( def send_post_request( payload: Union[ExecutionPayloadDict, DiscoveryPayloadDict, EOTPayloadDict], - test_run_pipe: str, + test_run_pipe: str | None, ): """ Sends a post request to the server. diff --git a/src/testMultiRootWkspc/workspace5/djangoApp/home/views.py b/src/testMultiRootWkspc/workspace5/djangoApp/home/views.py index 0494f868dc6f..a75f16597da1 100644 --- a/src/testMultiRootWkspc/workspace5/djangoApp/home/views.py +++ b/src/testMultiRootWkspc/workspace5/djangoApp/home/views.py @@ -1,10 +1,9 @@ from django.shortcuts import render -from django.template import loader def index(request): context = { - 'value_from_server':'this_is_a_value_from_server', - 'another_value_from_server':'this_is_another_value_from_server' + "value_from_server": "this_is_a_value_from_server", + "another_value_from_server": "this_is_another_value_from_server", } - return render(request, 'index.html', context) + return render(request, "index.html", context) diff --git a/src/testMultiRootWkspc/workspace5/djangoApp/manage.py b/src/testMultiRootWkspc/workspace5/djangoApp/manage.py index afbc784aafd8..3cb1277efa92 100644 --- a/src/testMultiRootWkspc/workspace5/djangoApp/manage.py +++ b/src/testMultiRootWkspc/workspace5/djangoApp/manage.py @@ -11,7 +11,7 @@ # issue is really that Django is missing to avoid masking other # exceptions on Python 2. try: - import django + import django # noqa: F401 except ImportError: raise ImportError( "Couldn't import Django. Are you sure it's installed and " diff --git a/src/testMultiRootWkspc/workspace5/djangoApp/mysite/urls.py b/src/testMultiRootWkspc/workspace5/djangoApp/mysite/urls.py index 9db383365e3e..97077eaebdb6 100644 --- a/src/testMultiRootWkspc/workspace5/djangoApp/mysite/urls.py +++ b/src/testMultiRootWkspc/workspace5/djangoApp/mysite/urls.py @@ -13,11 +13,11 @@ 1. Import the include() function: from django.conf.urls import url, include 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) """ + from django.conf.urls import url, include -from django.contrib import admin from django.views.generic import RedirectView urlpatterns = [ - url(r'^home/', include('home.urls')), - url('', RedirectView.as_view(url='/home/')), + url(r"^home/", include("home.urls")), + url("", RedirectView.as_view(url="/home/")), ] From 2a544ab79be4d4342ebb484812d6155be7eeee77 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Fri, 9 Aug 2024 13:05:17 -0700 Subject: [PATCH 07/17] fix more pyright errors --- .../polls/migrations/0001_initial.py | 52 +++++++++++++++++++ .../tests/unittestadapter/test_discovery.py | 12 ++--- .../tests/unittestadapter/test_execution.py | 5 +- 3 files changed, 58 insertions(+), 11 deletions(-) create mode 100644 python_files/tests/unittestadapter/.data/simple_django/polls/migrations/0001_initial.py diff --git a/python_files/tests/unittestadapter/.data/simple_django/polls/migrations/0001_initial.py b/python_files/tests/unittestadapter/.data/simple_django/polls/migrations/0001_initial.py new file mode 100644 index 000000000000..c23140f5da15 --- /dev/null +++ b/python_files/tests/unittestadapter/.data/simple_django/polls/migrations/0001_initial.py @@ -0,0 +1,52 @@ +# Generated by Django 5.0.8 on 2024-08-09 20:04 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [] + + operations = [ + migrations.CreateModel( + name="Question", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("question_text", models.CharField(max_length=200)), + ("pub_date", models.DateTimeField(verbose_name="date published")), + ], + ), + migrations.CreateModel( + name="Choice", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("choice_text", models.CharField(max_length=200)), + ("votes", models.IntegerField(default=0)), + ( + "question", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="polls.question" + ), + ), + ], + ), + ] diff --git a/python_files/tests/unittestadapter/test_discovery.py b/python_files/tests/unittestadapter/test_discovery.py index a2974d750b41..84cb5a252465 100644 --- a/python_files/tests/unittestadapter/test_discovery.py +++ b/python_files/tests/unittestadapter/test_discovery.py @@ -7,7 +7,6 @@ from typing import Any, Dict, List import pytest -from python_files.tests.pytestadapter import helpers from unittestadapter.discovery import discover_tests from unittestadapter.pvsc_utils import TestNodeTypeEnum, parse_unittest_args @@ -15,6 +14,7 @@ script_dir = pathlib.Path(__file__).parent.parent sys.path.append(os.fspath(script_dir)) +from python_files.tests.pytestadapter import helpers # noqa: E402 from tests.tree_comparison_helper import is_same_tree # noqa: E402 @@ -319,17 +319,11 @@ def test_simple_django_collect(): assert ( actual_item.get("status") == "success" ), f"Status is not 'success', error is: {actual_item.get('error')}" - assert actual_item.get("cwd") == data_path + 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"]) == 2 - ) - assert ( - actual_item["tests"]["children"][0]["children"][0]["children"][0]["children"][0][ - "runID" - ] - == "polls.tests.QuestionModelTests.test_was_published_recently_with_future_question" + len(actual_item["tests"]["children"][0]["children"][0]["children"][0]["children"]) == 3 ) diff --git a/python_files/tests/unittestadapter/test_execution.py b/python_files/tests/unittestadapter/test_execution.py index d0e1b8b36123..3dc9d1378e7f 100644 --- a/python_files/tests/unittestadapter/test_execution.py +++ b/python_files/tests/unittestadapter/test_execution.py @@ -8,7 +8,6 @@ from unittest.mock import patch import pytest -from python_files.tests.pytestadapter import helpers sys.path.append(os.fspath(pathlib.Path(__file__).parent)) sys.path.append(os.fspath(pathlib.Path(__file__).parent.parent.parent)) @@ -16,6 +15,8 @@ python_files_path = pathlib.Path(__file__).parent.parent.parent sys.path.insert(0, os.fspath(python_files_path / "lib" / "python")) +from python_files.tests.pytestadapter import helpers # noqa: E402 + from unittestadapter.execution import run_tests # noqa: E402 if TYPE_CHECKING: @@ -328,7 +329,7 @@ def test_basic_run_django(): if actual_list is not None: for actual_item in actual_list: assert all(item in actual_item for item in ("status", "cwd", "result")) - assert actual_item.get("cwd") == data_path + assert actual_item.get("cwd") == os.fspath(data_path) actual_result_dict.update(actual_item["result"]) for test_id in test_ids: assert test_id in actual_result_dict From 407f706749ca2442f0fcb78971df4b086ad94d7c Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Fri, 9 Aug 2024 13:20:59 -0700 Subject: [PATCH 08/17] fixing django file pylint errors --- .../tests/unittestadapter/.data/simple_django/polls/apps.py | 6 +++++- .../.data/simple_django/polls/migrations/0001_initial.py | 4 ++-- .../unittestadapter/.data/simple_django/polls/models.py | 2 +- .../unittestadapter/.data/simple_django/polls/tests.py | 4 ++-- .../unittestadapter/.data/simple_django/polls/views.py | 2 +- python_files/tests/unittestadapter/test_discovery.py | 3 +-- python_files/tests/unittestadapter/test_execution.py | 5 ++--- python_files/unittestadapter/pvsc_utils.py | 2 +- 8 files changed, 15 insertions(+), 13 deletions(-) diff --git a/python_files/tests/unittestadapter/.data/simple_django/polls/apps.py b/python_files/tests/unittestadapter/.data/simple_django/polls/apps.py index 645ae5aed250..e31968ce16c0 100644 --- a/python_files/tests/unittestadapter/.data/simple_django/polls/apps.py +++ b/python_files/tests/unittestadapter/.data/simple_django/polls/apps.py @@ -2,8 +2,12 @@ # Licensed under the MIT License. from django.apps import AppConfig +from django.utils.functional import cached_property class PollsConfig(AppConfig): - default_auto_field = "django.db.models.BigAutoField" + @cached_property + def default_auto_field(self): + return "django.db.models.BigAutoField" + name = "polls" diff --git a/python_files/tests/unittestadapter/.data/simple_django/polls/migrations/0001_initial.py b/python_files/tests/unittestadapter/.data/simple_django/polls/migrations/0001_initial.py index c23140f5da15..e33d24a3f704 100644 --- a/python_files/tests/unittestadapter/.data/simple_django/polls/migrations/0001_initial.py +++ b/python_files/tests/unittestadapter/.data/simple_django/polls/migrations/0001_initial.py @@ -23,8 +23,8 @@ class Migration(migrations.Migration): verbose_name="ID", ), ), - ("question_text", models.CharField(max_length=200)), - ("pub_date", models.DateTimeField(verbose_name="date published")), + ("question_text", models.CharField(max_length=200, default="")), + ("pub_date", models.DateTimeField(verbose_name="date published", auto_now_add=True)), ], ), migrations.CreateModel( diff --git a/python_files/tests/unittestadapter/.data/simple_django/polls/models.py b/python_files/tests/unittestadapter/.data/simple_django/polls/models.py index f0181a0b7ccb..260a3da60f99 100644 --- a/python_files/tests/unittestadapter/.data/simple_django/polls/models.py +++ b/python_files/tests/unittestadapter/.data/simple_django/polls/models.py @@ -20,6 +20,6 @@ def was_published_recently(self): class Choice(models.Model): question = models.ForeignKey(Question, on_delete=models.CASCADE) choice_text = models.CharField(max_length=200) - votes = models.IntegerField(default=0) + votes = models.IntegerField() def __str__(self): return self.choice_text diff --git a/python_files/tests/unittestadapter/.data/simple_django/polls/tests.py b/python_files/tests/unittestadapter/.data/simple_django/polls/tests.py index fee3ce154d09..fa9d2fca3d82 100644 --- a/python_files/tests/unittestadapter/.data/simple_django/polls/tests.py +++ b/python_files/tests/unittestadapter/.data/simple_django/polls/tests.py @@ -13,7 +13,7 @@ def test_was_published_recently_with_future_question(self): is in the future. """ time = timezone.now() + datetime.timedelta(days=30) - future_question = Question(pub_date=time) + future_question = Question.objects.create(pub_date=time) self.assertIs(future_question.was_published_recently(), False) def test_was_published_recently_with_future_question_2(self): @@ -22,7 +22,7 @@ def test_was_published_recently_with_future_question_2(self): is in the future. """ time = timezone.now() + datetime.timedelta(days=30) - future_question = Question(pub_date=time) + future_question = Question.objects.create(pub_date=time) self.assertIs(future_question.was_published_recently(), True) def test_question_creation_and_retrieval(self): diff --git a/python_files/tests/unittestadapter/.data/simple_django/polls/views.py b/python_files/tests/unittestadapter/.data/simple_django/polls/views.py index 28547aaa5534..2ca4f66ddfe1 100644 --- a/python_files/tests/unittestadapter/.data/simple_django/polls/views.py +++ b/python_files/tests/unittestadapter/.data/simple_django/polls/views.py @@ -1,7 +1,7 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. from django.http import HttpResponse - +from .models import Question # noqa: F401 def index(request): return HttpResponse("Hello, world. You're at the polls index.") diff --git a/python_files/tests/unittestadapter/test_discovery.py b/python_files/tests/unittestadapter/test_discovery.py index 84cb5a252465..36f73a4f68ec 100644 --- a/python_files/tests/unittestadapter/test_discovery.py +++ b/python_files/tests/unittestadapter/test_discovery.py @@ -14,8 +14,7 @@ script_dir = pathlib.Path(__file__).parent.parent sys.path.append(os.fspath(script_dir)) -from python_files.tests.pytestadapter import helpers # noqa: E402 - +from tests.pytestadapter import helpers # noqa: E402 from tests.tree_comparison_helper import is_same_tree # noqa: E402 from . import expected_discovery_test_output # noqa: E402 diff --git a/python_files/tests/unittestadapter/test_execution.py b/python_files/tests/unittestadapter/test_execution.py index 3dc9d1378e7f..65f5b5149bd2 100644 --- a/python_files/tests/unittestadapter/test_execution.py +++ b/python_files/tests/unittestadapter/test_execution.py @@ -10,13 +10,12 @@ import pytest sys.path.append(os.fspath(pathlib.Path(__file__).parent)) -sys.path.append(os.fspath(pathlib.Path(__file__).parent.parent.parent)) python_files_path = pathlib.Path(__file__).parent.parent.parent +sys.path.insert(0, os.fspath(python_files_path)) sys.path.insert(0, os.fspath(python_files_path / "lib" / "python")) -from python_files.tests.pytestadapter import helpers # noqa: E402 - +from tests.pytestadapter import helpers # noqa: E402 from unittestadapter.execution import run_tests # noqa: E402 if TYPE_CHECKING: diff --git a/python_files/unittestadapter/pvsc_utils.py b/python_files/unittestadapter/pvsc_utils.py index c62895c1e2e7..12a299a8992f 100644 --- a/python_files/unittestadapter/pvsc_utils.py +++ b/python_files/unittestadapter/pvsc_utils.py @@ -301,7 +301,7 @@ def parse_unittest_args( def send_post_request( payload: Union[ExecutionPayloadDict, DiscoveryPayloadDict, EOTPayloadDict], - test_run_pipe: str | None, + test_run_pipe: Optional[str], ): """ Sends a post request to the server. From 221e1d8ce4e37a2ae051d4e64065f38e5ea72c03 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Fri, 9 Aug 2024 13:59:29 -0700 Subject: [PATCH 09/17] fixing types --- build/test-requirements.txt | 3 +++ .../tests/unittestadapter/.data/simple_django/polls/tests.py | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/build/test-requirements.txt b/build/test-requirements.txt index 6457f988d320..4229104ddcc9 100644 --- a/build/test-requirements.txt +++ b/build/test-requirements.txt @@ -24,3 +24,6 @@ freezegun # testing custom pytest plugin require the use of named pipes namedpipe; platform_system == "Windows" + +# typing for Django files +django-stubs diff --git a/python_files/tests/unittestadapter/.data/simple_django/polls/tests.py b/python_files/tests/unittestadapter/.data/simple_django/polls/tests.py index fa9d2fca3d82..84d64e233236 100644 --- a/python_files/tests/unittestadapter/.data/simple_django/polls/tests.py +++ b/python_files/tests/unittestadapter/.data/simple_django/polls/tests.py @@ -13,7 +13,7 @@ def test_was_published_recently_with_future_question(self): is in the future. """ time = timezone.now() + datetime.timedelta(days=30) - future_question = Question.objects.create(pub_date=time) + future_question: Question = Question.objects.create(pub_date=time) self.assertIs(future_question.was_published_recently(), False) def test_was_published_recently_with_future_question_2(self): @@ -31,7 +31,7 @@ def test_question_creation_and_retrieval(self): """ time = timezone.now() question = Question.objects.create(pub_date=time, question_text="What's new?") - retrieved_question = Question.objects.get(id=question.id) + retrieved_question = Question.objects.get(id=question.question_text) self.assertEqual(question, retrieved_question) self.assertEqual(retrieved_question.question_text, "What's new?") self.assertEqual(retrieved_question.pub_date, time) From bced2bd63a13d112b86ef0b883fa019ea8de1a6e Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Fri, 9 Aug 2024 14:02:38 -0700 Subject: [PATCH 10/17] list type --- python_files/unittestadapter/django_handler.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/python_files/unittestadapter/django_handler.py b/python_files/unittestadapter/django_handler.py index fca939b98c0f..e37bee72c5b4 100644 --- a/python_files/unittestadapter/django_handler.py +++ b/python_files/unittestadapter/django_handler.py @@ -5,6 +5,7 @@ import pathlib import subprocess import sys +from typing import List script_dir = pathlib.Path(__file__).parent sys.path.append(os.fspath(script_dir)) @@ -17,7 +18,7 @@ ) -def django_discovery_runner(manage_py_path: str, args: list[str]) -> None: +def django_discovery_runner(manage_py_path: str, args: List[str]) -> None: # Attempt a small amount of validation on the manage.py path. try: pathlib.Path(manage_py_path) @@ -68,7 +69,7 @@ def django_discovery_runner(manage_py_path: str, args: list[str]) -> None: raise VSCodeUnittestError(f"Error during Django discovery: {e}") # noqa: B904 -def django_execution_runner(manage_py_path: str, test_ids: list[str], args: list[str]) -> None: +def django_execution_runner(manage_py_path: str, test_ids: List[str], args: List[str]) -> None: # Attempt a small amount of validation on the manage.py path. try: pathlib.Path(manage_py_path) @@ -86,7 +87,7 @@ def django_execution_runner(manage_py_path: str, test_ids: list[str], args: list env["PYTHONPATH"] = os.fspath(custom_test_runner_dir) # Build command to run 'python manage.py test'. - command: list[str] = [ + command: List[str] = [ sys.executable, manage_py_path, "test", From 879642f14462bb77060e3765e667c62d23318999 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Fri, 9 Aug 2024 14:12:35 -0700 Subject: [PATCH 11/17] fixing retrieved test --- .../tests/unittestadapter/.data/simple_django/polls/tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python_files/tests/unittestadapter/.data/simple_django/polls/tests.py b/python_files/tests/unittestadapter/.data/simple_django/polls/tests.py index 84d64e233236..243262f195a8 100644 --- a/python_files/tests/unittestadapter/.data/simple_django/polls/tests.py +++ b/python_files/tests/unittestadapter/.data/simple_django/polls/tests.py @@ -31,7 +31,7 @@ def test_question_creation_and_retrieval(self): """ time = timezone.now() question = Question.objects.create(pub_date=time, question_text="What's new?") - retrieved_question = Question.objects.get(id=question.question_text) + retrieved_question = Question.objects.get(question_text=question.question_text) self.assertEqual(question, retrieved_question) self.assertEqual(retrieved_question.question_text, "What's new?") self.assertEqual(retrieved_question.pub_date, time) From 9d7e69e9e9e416cb9567c181ec1bf32b8472f1f0 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Mon, 12 Aug 2024 10:28:40 -0700 Subject: [PATCH 12/17] switch EOT to atexit --- .../unittestadapter/django_handler.py | 21 ++------------ .../unittestadapter/django_test_runner.py | 28 +++++++++++++++++-- 2 files changed, 27 insertions(+), 22 deletions(-) diff --git a/python_files/unittestadapter/django_handler.py b/python_files/unittestadapter/django_handler.py index e37bee72c5b4..dc520c13aff1 100644 --- a/python_files/unittestadapter/django_handler.py +++ b/python_files/unittestadapter/django_handler.py @@ -12,9 +12,7 @@ sys.path.insert(0, os.fspath(script_dir / "lib" / "python")) from pvsc_utils import ( # noqa: E402 - EOTPayloadDict, VSCodeUnittestError, - send_post_request, ) @@ -57,14 +55,6 @@ def django_discovery_runner(manage_py_path: str, args: List[str]) -> None: if subprocess_discovery.returncode not in (0, 1): error_msg = "Django test discovery process exited with non-zero error code See stderr above for more details." print(error_msg, file=sys.stderr) - try: - test_run_pipe = os.getenv("TEST_RUN_PIPE") - eot_payload: EOTPayloadDict = {"command_type": "discovery", "eot": True} - send_post_request(eot_payload, test_run_pipe) - except Exception: - raise VSCodeUnittestError( # noqa: B904 - "Connection failure, likely means failure in Django subprocess run, see specific error output above." - ) except Exception as e: raise VSCodeUnittestError(f"Error during Django discovery: {e}") # noqa: B904 @@ -110,14 +100,7 @@ def django_execution_runner(manage_py_path: str, test_ids: List[str], args: List print(subprocess_execution.stdout, file=sys.stdout) # Zero return code indicates success, 1 indicates test failures, so both are considered successful. if subprocess_execution.returncode not in (0, 1): - try: - print("ERROR NUM", subprocess_execution.returncode) - test_run_pipe: str | None = os.getenv("TEST_RUN_PIPE") - eot_payload: EOTPayloadDict = {"command_type": "discovery", "eot": True} - send_post_request(eot_payload, test_run_pipe) - except Exception: - raise VSCodeUnittestError( # noqa: B904 - "Connection failure, likely means failure in Django subprocess run, see specific error output above." - ) + error_msg = "Django test execution process exited with non-zero error code See stderr above for more details." + print(error_msg, file=sys.stderr) except Exception as e: print(f"Error during Django test execution: {e}", file=sys.stderr) diff --git a/python_files/unittestadapter/django_test_runner.py b/python_files/unittestadapter/django_test_runner.py index 96a8b271f495..8e8c7a59bd60 100644 --- a/python_files/unittestadapter/django_test_runner.py +++ b/python_files/unittestadapter/django_test_runner.py @@ -2,6 +2,7 @@ # Licensed under the MIT License. +import atexit import os import pathlib import sys @@ -32,11 +33,16 @@ import unittest +def send_post_request_on_exit(command_type: str, test_run_pipe: str): + """Send an EOT token to indicate the end of the test run, registered to run at exit.""" + eot_payload: EOTPayloadDict = {"command_type": command_type, "eot": True} + send_post_request(eot_payload, test_run_pipe) + + class CustomDiscoveryTestRunner(DiscoverRunner): """Custom test runner for Django to handle test DISCOVERY and building the test tree.""" def run_tests(self, test_labels, **kwargs): - eot_payload: EOTPayloadDict = {"command_type": "discovery", "eot": True} test_run_pipe: str | None = os.getenv("TEST_RUN_PIPE") if not test_run_pipe: error_msg = ( @@ -47,6 +53,10 @@ def run_tests(self, test_labels, **kwargs): ) print(error_msg, file=sys.stderr) raise VSCodeUnittestError(error_msg) + # register atexit to ensure EOT is sent in all scenarios. + atexit.register( + send_post_request_on_exit, command_type="discovery", test_run_pipe=test_run_pipe + ) try: top_level_dir: pathlib.Path = pathlib.Path.cwd() @@ -67,14 +77,12 @@ def run_tests(self, test_labels, **kwargs): # Send discovery payload. send_post_request(payload, test_run_pipe) # Send EOT token. - send_post_request(eot_payload, test_run_pipe) return 0 # Skip actual test execution, return 0 as no tests were run. except Exception as e: error_msg = ( "DJANGO ERROR: An error occurred while discovering and building the test suite. " f"Error: {e}\n" ) - send_post_request(eot_payload, test_run_pipe) print(error_msg, file=sys.stderr) raise VSCodeUnittestError(error_msg) # noqa: B904 @@ -84,6 +92,20 @@ class CustomExecutionTestRunner(DiscoverRunner): def get_test_runner_kwargs(self): """Override to provide custom test runner; resultclass.""" + test_run_pipe: str | None = os.getenv("TEST_RUN_PIPE") + if not test_run_pipe: + error_msg = ( + "UNITTEST ERROR: TEST_RUN_PIPE is not set at the time of Django 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) + # register atexit to ensure EOT is sent in all scenarios. + atexit.register( + send_post_request_on_exit, command_type="execution", test_run_pipe=test_run_pipe + ) # Get existing kwargs kwargs = super().get_test_runner_kwargs() # Add custom resultclass, same resultclass as used in unittest. From 2508411c03be7980217ba9378a960d417b56b673 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Mon, 12 Aug 2024 10:30:53 -0700 Subject: [PATCH 13/17] remove double EOT for execution --- python_files/unittestadapter/django_test_runner.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/python_files/unittestadapter/django_test_runner.py b/python_files/unittestadapter/django_test_runner.py index 8e8c7a59bd60..b52c287d7369 100644 --- a/python_files/unittestadapter/django_test_runner.py +++ b/python_files/unittestadapter/django_test_runner.py @@ -113,12 +113,5 @@ def get_test_runner_kwargs(self): return kwargs def suite_result(self, suite, result, **kwargs): - # After run finishes, send EOT token. - test_run_pipe: str | None = os.getenv("TEST_RUN_PIPE") - if not test_run_pipe: - print("Error[vscode-unittest]: TEST_RUN_PIPE env var is not set.", file=sys.stderr) - raise VSCodeUnittestError("Error[vscode-unittest]: TEST_RUN_PIPE env var is not set.") - eot_payload: EOTPayloadDict = {"command_type": "execution", "eot": True} - send_post_request(eot_payload, test_run_pipe) # call super to finish the suite result as Django intends. return super().suite_result(suite, result, **kwargs) From 11f9ad44c93af4227532f56d266a33049a2884fb Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Mon, 12 Aug 2024 12:32:09 -0700 Subject: [PATCH 14/17] update for windows EOT handling --- python_files/tests/pytestadapter/helpers.py | 2 ++ .../unittestadapter/django_test_runner.py | 18 ++---------------- python_files/unittestadapter/execution.py | 19 +++++++++++++++++++ 3 files changed, 23 insertions(+), 16 deletions(-) diff --git a/python_files/tests/pytestadapter/helpers.py b/python_files/tests/pytestadapter/helpers.py index e3552070feb7..5fdcce11689a 100644 --- a/python_files/tests/pytestadapter/helpers.py +++ b/python_files/tests/pytestadapter/helpers.py @@ -229,6 +229,8 @@ def runner_with_cwd_env( "PYTHONPATH": os.fspath(pathlib.Path(__file__).parent.parent.parent), } ) + if env_add: + env.update(env_add) completed = threading.Event() diff --git a/python_files/unittestadapter/django_test_runner.py b/python_files/unittestadapter/django_test_runner.py index b52c287d7369..caf312e5cb2e 100644 --- a/python_files/unittestadapter/django_test_runner.py +++ b/python_files/unittestadapter/django_test_runner.py @@ -1,8 +1,6 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. - -import atexit import os import pathlib import sys @@ -33,12 +31,6 @@ import unittest -def send_post_request_on_exit(command_type: str, test_run_pipe: str): - """Send an EOT token to indicate the end of the test run, registered to run at exit.""" - eot_payload: EOTPayloadDict = {"command_type": command_type, "eot": True} - send_post_request(eot_payload, test_run_pipe) - - class CustomDiscoveryTestRunner(DiscoverRunner): """Custom test runner for Django to handle test DISCOVERY and building the test tree.""" @@ -53,10 +45,6 @@ def run_tests(self, test_labels, **kwargs): ) print(error_msg, file=sys.stderr) raise VSCodeUnittestError(error_msg) - # register atexit to ensure EOT is sent in all scenarios. - atexit.register( - send_post_request_on_exit, command_type="discovery", test_run_pipe=test_run_pipe - ) try: top_level_dir: pathlib.Path = pathlib.Path.cwd() @@ -77,6 +65,8 @@ def run_tests(self, test_labels, **kwargs): # Send discovery payload. send_post_request(payload, test_run_pipe) # Send EOT token. + eot_payload: EOTPayloadDict = {"command_type": "discovery", "eot": True} + send_post_request(eot_payload, test_run_pipe) return 0 # Skip actual test execution, return 0 as no tests were run. except Exception as e: error_msg = ( @@ -102,10 +92,6 @@ def get_test_runner_kwargs(self): ) print(error_msg, file=sys.stderr) raise VSCodeUnittestError(error_msg) - # register atexit to ensure EOT is sent in all scenarios. - atexit.register( - send_post_request_on_exit, command_type="execution", test_run_pipe=test_run_pipe - ) # Get existing kwargs kwargs = super().get_test_runner_kwargs() # Add custom resultclass, same resultclass as used in unittest. diff --git a/python_files/unittestadapter/execution.py b/python_files/unittestadapter/execution.py index ce1356a4d604..b2351ff2c96e 100644 --- a/python_files/unittestadapter/execution.py +++ b/python_files/unittestadapter/execution.py @@ -58,6 +58,25 @@ def __init__(self, *args, **kwargs): def startTest(self, test: unittest.TestCase): # noqa: N802 super().startTest(test) + def stopTestRun(self): # noqa: N802 + super().stopTestRun() + # After stopping the test run, send EOT + test_run_pipe = os.getenv("TEST_RUN_PIPE") + manage_py_path = os.getenv("MANAGE_PY_PATH") + if manage_py_path: + # only send this if it is a Django run + if not test_run_pipe: + print( + "UNITTEST ERROR: TEST_RUN_PIPE is not set at the time of unittest trying to send data. " + f"TEST_RUN_PIPE = {test_run_pipe}\n", + file=sys.stderr, + ) + raise VSCodeUnittestError( + "UNITTEST ERROR: TEST_RUN_PIPE is not set at the time of unittest trying to send data. " + ) + eot_payload: EOTPayloadDict = {"command_type": "execution", "eot": True} + send_post_request(eot_payload, test_run_pipe) + def addError( # noqa: N802 self, test: unittest.TestCase, From 181e5ebbb6e8266ee953b8784fecebded72397af Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Mon, 12 Aug 2024 14:19:20 -0700 Subject: [PATCH 15/17] updates to simplify test scenario --- python_files/tests/pytestadapter/helpers.py | 13 +++++++--- .../.data/simple_django/manage.py | 3 ++- .../.data/simple_django/mysite/asgi.py | 9 ------- .../.data/simple_django/mysite/settings.py | 26 ------------------- .../.data/simple_django/mysite/urls.py | 15 ----------- .../.data/simple_django/mysite/wsgi.py | 11 -------- .../.data/simple_django/polls/urls.py | 6 ----- .../.data/simple_django/polls/views.py | 12 --------- .../django_test_execution_script.py | 1 + .../unittestadapter/django_test_runner.py | 4 --- 10 files changed, 13 insertions(+), 87 deletions(-) diff --git a/python_files/tests/pytestadapter/helpers.py b/python_files/tests/pytestadapter/helpers.py index 5fdcce11689a..7cfca83acd66 100644 --- a/python_files/tests/pytestadapter/helpers.py +++ b/python_files/tests/pytestadapter/helpers.py @@ -193,23 +193,28 @@ def _run_test_code(proc_args: List[str], proc_env, proc_cwd: str, completed: thr def runner(args: List[str]) -> Optional[List[Dict[str, Any]]]: - """Run the pytest discovery and return the JSON data from the server.""" + """Run a subprocess and a named-pipe to listen for messages at the same time with threading.""" print("\n Running python test subprocess with cwd set to: ", TEST_DATA_PATH) return runner_with_cwd(args, TEST_DATA_PATH) def runner_with_cwd(args: List[str], path: pathlib.Path) -> Optional[List[Dict[str, Any]]]: - """Run the pytest discovery and return the JSON data from the server.""" + """Run a subprocess and a named-pipe to listen for messages at the same time with threading.""" return runner_with_cwd_env(args, path, {}) def runner_with_cwd_env( args: List[str], path: pathlib.Path, env_add: Dict[str, str] ) -> Optional[List[Dict[str, Any]]]: - """Run the pytest discovery and return the JSON data from the server.""" + """ + Run a subprocess and a named-pipe to listen for messages at the same time with threading. + + Includes environment variables to add to the test environment. + """ process_args: List[str] pipe_name: str if "MANAGE_PY_PATH" in env_add: + # if we are running Django, generate unittest specific pipe name process_args = [sys.executable, *args] pipe_name = generate_random_pipe_name("unittest-discovery-test") else: @@ -229,6 +234,7 @@ def runner_with_cwd_env( "PYTHONPATH": os.fspath(pathlib.Path(__file__).parent.parent.parent), } ) + # if additional environment variables are passed, add them to the environment if env_add: env.update(env_add) @@ -259,6 +265,7 @@ def runner_with_cwd_env( "PYTHONPATH": os.fspath(pathlib.Path(__file__).parent.parent.parent), } ) + # if additional environment variables are passed, add them to the environment if env_add: env.update(env_add) server = UnixPipeServer(pipe_name) diff --git a/python_files/tests/unittestadapter/.data/simple_django/manage.py b/python_files/tests/unittestadapter/.data/simple_django/manage.py index a7da6671aab8..c5734a6babee 100755 --- a/python_files/tests/unittestadapter/.data/simple_django/manage.py +++ b/python_files/tests/unittestadapter/.data/simple_django/manage.py @@ -1,4 +1,5 @@ -#!/usr/bin/env python +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. """Django's command-line utility for administrative tasks.""" import os import sys diff --git a/python_files/tests/unittestadapter/.data/simple_django/mysite/asgi.py b/python_files/tests/unittestadapter/.data/simple_django/mysite/asgi.py index 709745cd8b68..bb01f607934c 100644 --- a/python_files/tests/unittestadapter/.data/simple_django/mysite/asgi.py +++ b/python_files/tests/unittestadapter/.data/simple_django/mysite/asgi.py @@ -1,14 +1,5 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. -""" -ASGI config for mysite project. - -It exposes the ASGI callable as a module-level variable named ``application``. - -For more information on this file, see -https://docs.djangoproject.com/en/3.2/howto/deployment/asgi/ -""" - import os from django.core.asgi import get_asgi_application diff --git a/python_files/tests/unittestadapter/.data/simple_django/mysite/settings.py b/python_files/tests/unittestadapter/.data/simple_django/mysite/settings.py index 52795d7ec108..3120fb4e829f 100644 --- a/python_files/tests/unittestadapter/.data/simple_django/mysite/settings.py +++ b/python_files/tests/unittestadapter/.data/simple_django/mysite/settings.py @@ -18,15 +18,6 @@ BASE_DIR = Path(__file__).resolve().parent.parent -# Quick-start development settings - unsuitable for production -# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/ - -# SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = 'django-insecure-f2*4t6_!)%2d@91f(*ybqura=uvf5h4@2(qe+(zogq=0q=1iy0' - -# SECURITY WARNING: don't run with debug turned on in production! -DEBUG = True - ALLOWED_HOSTS = [] @@ -84,23 +75,6 @@ } -# Password validation -# https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators - -AUTH_PASSWORD_VALIDATORS = [ - { - 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', - }, - { - 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', - }, - { - 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', - }, - { - 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', - }, -] # Internationalization diff --git a/python_files/tests/unittestadapter/.data/simple_django/mysite/urls.py b/python_files/tests/unittestadapter/.data/simple_django/mysite/urls.py index 34723c537af5..02e76f125c72 100644 --- a/python_files/tests/unittestadapter/.data/simple_django/mysite/urls.py +++ b/python_files/tests/unittestadapter/.data/simple_django/mysite/urls.py @@ -1,20 +1,5 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. -"""mysite URL Configuration - -The `urlpatterns` list routes URLs to views. For more information please see: - https://docs.djangoproject.com/en/3.2/topics/http/urls/ -Examples: -Function views - 1. Add an import: from my_app import views - 2. Add a URL to urlpatterns: path('', views.home, name='home') -Class-based views - 1. Add an import: from other_app.views import Home - 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') -Including another URLconf - 1. Import the include() function: from django.urls import include, path - 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) -""" from django.contrib import admin from django.urls import include, path diff --git a/python_files/tests/unittestadapter/.data/simple_django/mysite/wsgi.py b/python_files/tests/unittestadapter/.data/simple_django/mysite/wsgi.py index 41572422f0b0..e932bff6649e 100644 --- a/python_files/tests/unittestadapter/.data/simple_django/mysite/wsgi.py +++ b/python_files/tests/unittestadapter/.data/simple_django/mysite/wsgi.py @@ -1,18 +1,7 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. -""" -WSGI config for mysite project. - -It exposes the WSGI callable as a module-level variable named ``application``. - -For more information on this file, see -https://docs.djangoproject.com/en/3.2/howto/deployment/wsgi/ -""" - import os from django.core.wsgi import get_wsgi_application -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings') - application = get_wsgi_application() diff --git a/python_files/tests/unittestadapter/.data/simple_django/polls/urls.py b/python_files/tests/unittestadapter/.data/simple_django/polls/urls.py index 25c1d536ccfb..5756c7daa847 100644 --- a/python_files/tests/unittestadapter/.data/simple_django/polls/urls.py +++ b/python_files/tests/unittestadapter/.data/simple_django/polls/urls.py @@ -8,10 +8,4 @@ urlpatterns = [ # ex: /polls/ path("", views.index, name="index"), - # ex: /polls/5/ - path("/", views.detail, name="detail"), - # ex: /polls/5/results/ - path("/results/", views.results, name="results"), - # ex: /polls/5/vote/ - path("/vote/", views.vote, name="vote"), ] diff --git a/python_files/tests/unittestadapter/.data/simple_django/polls/views.py b/python_files/tests/unittestadapter/.data/simple_django/polls/views.py index 2ca4f66ddfe1..cccb6b3b0685 100644 --- a/python_files/tests/unittestadapter/.data/simple_django/polls/views.py +++ b/python_files/tests/unittestadapter/.data/simple_django/polls/views.py @@ -5,15 +5,3 @@ def index(request): return HttpResponse("Hello, world. You're at the polls index.") - -def detail(request, question_id): - return HttpResponse("You're looking at question %s." % question_id) - - -def results(request, question_id): - response = "You're looking at the results of question %s." - return HttpResponse(response % question_id) - - -def vote(request, question_id): - return HttpResponse("You're voting on question %s." % question_id) diff --git a/python_files/tests/unittestadapter/django_test_execution_script.py b/python_files/tests/unittestadapter/django_test_execution_script.py index 4c7cf8d89939..21dd945224ea 100644 --- a/python_files/tests/unittestadapter/django_test_execution_script.py +++ b/python_files/tests/unittestadapter/django_test_execution_script.py @@ -13,4 +13,5 @@ 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, []) diff --git a/python_files/unittestadapter/django_test_runner.py b/python_files/unittestadapter/django_test_runner.py index caf312e5cb2e..4225e2c8fa65 100644 --- a/python_files/unittestadapter/django_test_runner.py +++ b/python_files/unittestadapter/django_test_runner.py @@ -97,7 +97,3 @@ def get_test_runner_kwargs(self): # Add custom resultclass, same resultclass as used in unittest. kwargs["resultclass"] = UnittestTestResult return kwargs - - def suite_result(self, suite, result, **kwargs): - # call super to finish the suite result as Django intends. - return super().suite_result(suite, result, **kwargs) From 228fe2cbe842569f8da047e7d8f0e54686e9309f Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Mon, 12 Aug 2024 14:21:46 -0700 Subject: [PATCH 16/17] remove unneeded edits --- src/testMultiRootWkspc/workspace5/djangoApp/home/views.py | 7 ++++--- src/testMultiRootWkspc/workspace5/djangoApp/manage.py | 2 +- src/testMultiRootWkspc/workspace5/djangoApp/mysite/urls.py | 6 +++--- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/testMultiRootWkspc/workspace5/djangoApp/home/views.py b/src/testMultiRootWkspc/workspace5/djangoApp/home/views.py index a75f16597da1..0494f868dc6f 100644 --- a/src/testMultiRootWkspc/workspace5/djangoApp/home/views.py +++ b/src/testMultiRootWkspc/workspace5/djangoApp/home/views.py @@ -1,9 +1,10 @@ from django.shortcuts import render +from django.template import loader def index(request): context = { - "value_from_server": "this_is_a_value_from_server", - "another_value_from_server": "this_is_another_value_from_server", + 'value_from_server':'this_is_a_value_from_server', + 'another_value_from_server':'this_is_another_value_from_server' } - return render(request, "index.html", context) + return render(request, 'index.html', context) diff --git a/src/testMultiRootWkspc/workspace5/djangoApp/manage.py b/src/testMultiRootWkspc/workspace5/djangoApp/manage.py index 3cb1277efa92..afbc784aafd8 100644 --- a/src/testMultiRootWkspc/workspace5/djangoApp/manage.py +++ b/src/testMultiRootWkspc/workspace5/djangoApp/manage.py @@ -11,7 +11,7 @@ # issue is really that Django is missing to avoid masking other # exceptions on Python 2. try: - import django # noqa: F401 + import django except ImportError: raise ImportError( "Couldn't import Django. Are you sure it's installed and " diff --git a/src/testMultiRootWkspc/workspace5/djangoApp/mysite/urls.py b/src/testMultiRootWkspc/workspace5/djangoApp/mysite/urls.py index 97077eaebdb6..9db383365e3e 100644 --- a/src/testMultiRootWkspc/workspace5/djangoApp/mysite/urls.py +++ b/src/testMultiRootWkspc/workspace5/djangoApp/mysite/urls.py @@ -13,11 +13,11 @@ 1. Import the include() function: from django.conf.urls import url, include 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) """ - from django.conf.urls import url, include +from django.contrib import admin from django.views.generic import RedirectView urlpatterns = [ - url(r"^home/", include("home.urls")), - url("", RedirectView.as_view(url="/home/")), + url(r'^home/', include('home.urls')), + url('', RedirectView.as_view(url='/home/')), ] From 9658eba536a05e3d6e058a0bbc6921350748cd43 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Wed, 14 Aug 2024 08:30:26 -0700 Subject: [PATCH 17/17] address comments --- python_files/tests/pytestadapter/helpers.py | 2 +- .../tests/unittestadapter/test_discovery.py | 15 +++++++-------- .../tests/unittestadapter/test_execution.py | 13 ++++++------- python_files/unittestadapter/discovery.py | 3 +-- python_files/unittestadapter/execution.py | 7 ++----- 5 files changed, 17 insertions(+), 23 deletions(-) diff --git a/python_files/tests/pytestadapter/helpers.py b/python_files/tests/pytestadapter/helpers.py index 7cfca83acd66..4f6631a44c00 100644 --- a/python_files/tests/pytestadapter/helpers.py +++ b/python_files/tests/pytestadapter/helpers.py @@ -214,7 +214,7 @@ def runner_with_cwd_env( process_args: List[str] pipe_name: str if "MANAGE_PY_PATH" in env_add: - # if we are running Django, generate unittest specific pipe name + # If we are running Django, generate a unittest-specific pipe name. process_args = [sys.executable, *args] pipe_name = generate_random_pipe_name("unittest-discovery-test") else: diff --git a/python_files/tests/unittestadapter/test_discovery.py b/python_files/tests/unittestadapter/test_discovery.py index 36f73a4f68ec..972556de999b 100644 --- a/python_files/tests/unittestadapter/test_discovery.py +++ b/python_files/tests/unittestadapter/test_discovery.py @@ -292,18 +292,16 @@ def test_complex_tree() -> None: ) -TEST_DATA_PATH: pathlib.Path = pathlib.Path(__file__).parent / ".data" -PYTHON_FILES_PATH: pathlib.Path = pathlib.Path(__file__).parent.parent.parent -DISCOVERY_SCRIPT: str = os.fsdecode(PYTHON_FILES_PATH / "unittestadapter" / "discovery.py") - - def test_simple_django_collect(): - data_path: pathlib.Path = pathlib.Path(TEST_DATA_PATH, "simple_django") + 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, + discovery_script_path, "--udiscovery", ], data_path, @@ -312,6 +310,7 @@ def test_simple_django_collect(): 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")) @@ -321,7 +320,7 @@ def test_simple_django_collect(): 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") + pathlib.PurePath(test_data_path, "simple_django", "polls", "tests.py") ) assert ( len(actual_item["tests"]["children"][0]["children"][0]["children"][0]["children"]) == 3 diff --git a/python_files/tests/unittestadapter/test_execution.py b/python_files/tests/unittestadapter/test_execution.py index 65f5b5149bd2..f369c6d770b0 100644 --- a/python_files/tests/unittestadapter/test_execution.py +++ b/python_files/tests/unittestadapter/test_execution.py @@ -304,8 +304,8 @@ def test_incorrect_path(): def test_basic_run_django(): """This test runs on a simple django project with three tests, two of which pass and one that fails.""" - data_path: pathlib.Path = pathlib.Path(TEST_DATA_PATH, "simple_django") - manage_py_path: str = os.fsdecode(pathlib.PurePath(data_path, "manage.py")) + 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" ) @@ -325,11 +325,10 @@ def test_basic_run_django(): actual_list: List[Dict[str, Dict[str, Any]]] = actual actual_result_dict = {} assert len(actual_list) == 3 - if actual_list is not None: - for actual_item in actual_list: - assert all(item in actual_item for item in ("status", "cwd", "result")) - assert actual_item.get("cwd") == os.fspath(data_path) - actual_result_dict.update(actual_item["result"]) + for actual_item in actual_list: + assert all(item in actual_item for item in ("status", "cwd", "result")) + assert actual_item.get("cwd") == os.fspath(data_path) + actual_result_dict.update(actual_item["result"]) for test_id in test_ids: assert test_id in actual_result_dict id_result = actual_result_dict[test_id] diff --git a/python_files/unittestadapter/discovery.py b/python_files/unittestadapter/discovery.py index 49eefc0eae91..660dda0b292c 100644 --- a/python_files/unittestadapter/discovery.py +++ b/python_files/unittestadapter/discovery.py @@ -120,8 +120,7 @@ def discover_tests( print(error_msg, file=sys.stderr) raise VSCodeUnittestError(error_msg) - manage_py_path = os.environ.get("MANAGE_PY_PATH") - if manage_py_path: + 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}" diff --git a/python_files/unittestadapter/execution.py b/python_files/unittestadapter/execution.py index b2351ff2c96e..4bc668bf71b6 100644 --- a/python_files/unittestadapter/execution.py +++ b/python_files/unittestadapter/execution.py @@ -62,8 +62,7 @@ def stopTestRun(self): # noqa: N802 super().stopTestRun() # After stopping the test run, send EOT test_run_pipe = os.getenv("TEST_RUN_PIPE") - manage_py_path = os.getenv("MANAGE_PY_PATH") - if manage_py_path: + if os.getenv("MANAGE_PY_PATH"): # only send this if it is a Django run if not test_run_pipe: print( @@ -340,9 +339,7 @@ def send_run_data(raw_data, test_run_pipe): if raw_json and "params" in raw_json and raw_json["params"]: test_ids_from_buffer = raw_json["params"] # Check to see if we are running django tests. - manage_py_path = os.environ.get("MANAGE_PY_PATH") - if manage_py_path: - # run django runner + if manage_py_path := os.environ.get("MANAGE_PY_PATH"): args = argv[index + 1 :] or [] django_execution_runner(manage_py_path, test_ids_from_buffer, args) # the django run subprocesses sends the eot payload.