From 9417b1f392987cec4537388bbbc61b80da564a4d Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Wed, 29 Jul 2026 05:08:49 +0000 Subject: [PATCH] Fix operating directory in isolated tests - Fixes #297 --- .../tests/test_pytest_plugins.py | 51 +++++++++++++++++-- .../testing/pytest_isolated_tests_plugin.py | 9 ++-- 2 files changed, 54 insertions(+), 6 deletions(-) diff --git a/subprojects/robotpy-wpilib/tests/test_pytest_plugins.py b/subprojects/robotpy-wpilib/tests/test_pytest_plugins.py index 0110696a2..40326e880 100644 --- a/subprojects/robotpy-wpilib/tests/test_pytest_plugins.py +++ b/subprojects/robotpy-wpilib/tests/test_pytest_plugins.py @@ -68,18 +68,29 @@ def pytest_configure(config): """) -def _configure_isolated_plugin(pytester, parallelism=1, robot_class="DummyRobot"): +def _configure_isolated_plugin( + pytester, + parallelism=1, + robot_class="DummyRobot", + robot_module="robot_module", + robot_file_name=None, +): + if robot_file_name is None: + robot_file = "pathlib.Path(__file__).resolve()" + else: + robot_file = f"pathlib.Path(__file__).parent / {robot_file_name!r}" + pytester.makeconftest(f""" import pathlib from wpilib.testing.pytest_isolated_tests_plugin import IsolatedTestsPlugin -from robot_module import {robot_class} +from {robot_module} import {robot_class} def pytest_configure(config): if "--no-header" in config.invocation_params.args: return - robot_file = pathlib.Path(__file__).resolve() + robot_file = {robot_file} config.pluginmanager.register( IsolatedTestsPlugin({robot_class}, robot_file, False, False, {parallelism}) ) @@ -166,6 +177,40 @@ def test_robot_failure_output(robot): assert robot_pid_one != robot_pid_two +def test_isolated_plugin_uses_robot_directory_during_robot_import(pytester): + robot_dir = pytester.path / "robot_project" + robot_dir.mkdir() + robot_dir.joinpath("__init__.py").touch() + robot_dir.joinpath("robot.py").write_text(""" +import wpilib + +IMPORT_OPERATING_DIRECTORY = wpilib.get_operating_directory() + + +class ImportDirectoryRobot(wpilib.TimedRobot): + pass +""") + _configure_isolated_plugin( + pytester, + robot_class="ImportDirectoryRobot", + robot_module="robot_project.robot", + robot_file_name="robot_project/robot.py", + ) + pytester.makepyfile(test_isolated=""" +import pathlib + +from robot_project.robot import IMPORT_OPERATING_DIRECTORY + + +def test_operating_directory(robot, robot_file): + assert pathlib.Path(IMPORT_OPERATING_DIRECTORY) == robot_file.parent.absolute() +""") + + result = pytester.runpytest_subprocess("-vv") + + result.assert_outcomes(passed=1) + + def test_isolated_plugin_assertion_rendering(pytester): _make_robot_module(pytester) _configure_isolated_plugin(pytester) diff --git a/subprojects/robotpy-wpilib/wpilib/testing/pytest_isolated_tests_plugin.py b/subprojects/robotpy-wpilib/wpilib/testing/pytest_isolated_tests_plugin.py index 90a7ad607..4114cc4d4 100644 --- a/subprojects/robotpy-wpilib/wpilib/testing/pytest_isolated_tests_plugin.py +++ b/subprojects/robotpy-wpilib/wpilib/testing/pytest_isolated_tests_plugin.py @@ -4,6 +4,7 @@ import multiprocessing.connection import os import pathlib +import pickle import signal import sys import time @@ -127,7 +128,7 @@ def pytest_runtest_logreport(self, report: pytest.TestReport): def _run_test( - item_nodeid, config_args, robot_class, robot_file, verbose, pipe, root_path + item_nodeid, config_args, robot_class_data, robot_file, verbose, pipe, root_path ): """This function runs in a subprocess""" logging.root.addHandler(logging.NullHandler()) @@ -135,8 +136,10 @@ def _run_test( _enable_faulthandler() - # This is used by getDeployDirectory, so make sure it gets fixed + # This is used by the operating and deploy directory lookups, so set it + # before importing the robot module. robotpy.main.robot_py_path = robot_file + robot_class = pickle.loads(robot_class_data) os.chdir(root_path) @@ -281,7 +284,7 @@ def _start_isolated_test(self, item: pytest.Function) -> IsolatedTestJob: args=( nodeid, config_args, - self._robot_class, + pickle.dumps(self._robot_class), self._robot_file, self._verbose, cconn,