Skip to content
This repository was archived by the owner on Jun 1, 2026. It is now read-only.

Commit cee9fb2

Browse files
authored
Merge pull request #254 from robotpy/init-timeout
Make initialization timeout configurable
2 parents 642865b + 95b496d commit cee9fb2

5 files changed

Lines changed: 62 additions & 9 deletions

File tree

pyfrc/mains/cli_test.py

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,12 @@ def __init__(self, parser=None):
7575
default=-1,
7676
help="Maximum isolated robot processes (default: max CPUs - 1)",
7777
)
78+
parser.add_argument(
79+
"--init-timeout",
80+
type=float,
81+
default=None,
82+
help="Seconds to wait for robot to start (can be set in `tool.robotpy.pyfrc.init_timeout` also)",
83+
)
7884

7985
def run(
8086
self,
@@ -87,6 +93,7 @@ def run(
8793
verbose: bool,
8894
pytest_args: typing.List[str],
8995
jobs: int,
96+
init_timeout: typing.Optional[float],
9097
):
9198
if isolated is None:
9299
pyproject_path = project_path / "pyproject.toml"
@@ -106,6 +113,26 @@ def run(
106113

107114
isolated = v
108115

116+
try:
117+
v = d["tool"]["robotpy"]["pyfrc"]["init_timeout"]
118+
except KeyError:
119+
pass
120+
else:
121+
if not isinstance(v, (int, float)):
122+
raise ValueError(
123+
f"tool.robotpy.pyfrc.init_timeout must be a number (got {v})"
124+
)
125+
elif not (v > 0):
126+
raise ValueError(
127+
f"tool.robotpy.pyfrc.init_timeout must be a positive number (got {v})"
128+
)
129+
130+
if init_timeout is None:
131+
init_timeout = float(v)
132+
133+
if init_timeout is None:
134+
init_timeout = 2.0
135+
109136
if isolated is None:
110137
isolated = True
111138

@@ -120,6 +147,7 @@ def run(
120147
verbose,
121148
pytest_args,
122149
jobs,
150+
init_timeout,
123151
)
124152
except _TryAgain:
125153
return self._run_test(
@@ -132,6 +160,7 @@ def run(
132160
verbose,
133161
pytest_args,
134162
jobs,
163+
init_timeout,
135164
)
136165

137166
def _run_test(
@@ -145,6 +174,7 @@ def _run_test(
145174
verbose: bool,
146175
pytest_args: typing.List[str],
147176
jobs: int,
177+
init_timeout: float,
148178
):
149179
# find test directory, change current directory so pytest can find the tests
150180
# -> assume that tests reside in tests or ../tests
@@ -180,14 +210,18 @@ def _run_test(
180210
pytest_args,
181211
plugins=[
182212
pytest_isolated_tests_plugin.IsolatedTestsPlugin(
183-
robot_class, main_file, builtin, verbose, jobs
213+
robot_class, main_file, builtin, verbose, jobs, init_timeout
184214
)
185215
],
186216
)
187217
else:
188218
retv = pytest.main(
189219
pytest_args,
190-
plugins=[pytest_plugin.PyFrcPlugin(robot_class, main_file, False)],
220+
plugins=[
221+
pytest_plugin.PyFrcPlugin(
222+
robot_class, main_file, False, init_timeout
223+
)
224+
],
191225
)
192226
finally:
193227
os.chdir(curdir)

pyfrc/test_support/controller.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,12 @@ class TestController:
1212
Use this object to control the robot's state during tests
1313
"""
1414

15-
def __init__(self, reraise, robot: wpilib.RobotBase):
15+
def __init__(self, reraise, robot: wpilib.RobotBase, init_timeout: float = 2.0):
1616
self._reraise = reraise
1717

1818
self._thread: typing.Optional[threading.Thread] = None
1919
self._robot = robot
20+
self._init_timeout = init_timeout
2021

2122
self._cond = threading.Condition()
2223
self._robot_started = False
@@ -72,7 +73,12 @@ def run_robot(self):
7273

7374
# If your robotInit is taking more than 2 seconds in simulation, you're
7475
# probably doing something wrong... but if not, please report a bug!
75-
assert self._cond.wait_for(lambda: self._robot_initialized, timeout=2)
76+
#
77+
# To avoid this altogether, `--init-timeout` can be passed to `robotpy test`
78+
# or you can add `tools.robotpy.pyfrc.init_timeout` instead
79+
assert self._cond.wait_for(
80+
lambda: self._robot_initialized, timeout=self._init_timeout
81+
)
7682

7783
try:
7884
# in this block you should tell the sim to do sim things

pyfrc/test_support/pytest_isolated_tests_plugin.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,14 @@ def pytest_runtest_logreport(self, report: pytest.TestReport):
128128

129129

130130
def _run_test(
131-
item_nodeid, config_args, robot_class, robot_file, verbose, pipe, root_path
131+
item_nodeid,
132+
config_args,
133+
robot_class,
134+
robot_file,
135+
verbose,
136+
pipe,
137+
root_path,
138+
init_timeout,
132139
):
133140
"""This function runs in a subprocess"""
134141
logging.root.addHandler(logging.NullHandler())
@@ -143,7 +150,7 @@ def _run_test(
143150

144151
# keep the plugins around because it has a reference to the robot
145152
# and we don't want it to die and deadlock
146-
plugin = PyFrcPlugin(robot_class, robot_file, True)
153+
plugin = PyFrcPlugin(robot_class, robot_file, True, init_timeout)
147154
worker_plugin = WorkerPlugin(pipe)
148155

149156
ec = pytest.main(
@@ -195,11 +202,13 @@ def __init__(
195202
builtin_tests: bool,
196203
verbose: bool,
197204
parallelism: int,
205+
init_timeout: float,
198206
):
199207
self._robot_class = robot_class
200208
self._robot_file = robot_file
201209
self._builtin_tests = builtin_tests
202210
self._verbose = verbose
211+
self._init_timeout = init_timeout
203212

204213
if parallelism < 1:
205214
try:
@@ -287,6 +296,7 @@ def _start_isolated_test(self, item: pytest.Function) -> IsolatedTestJob:
287296
self._verbose,
288297
cconn,
289298
self._config.rootpath,
299+
self._init_timeout,
290300
),
291301
)
292302
process.start()

pyfrc/test_support/pytest_plugin.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ def __init__(
3636
robot_class: Type[wpilib.RobotBase],
3737
robot_file: pathlib.Path,
3838
isolated: bool,
39+
init_timeout: float,
3940
):
4041
self.isolated = isolated
4142

@@ -61,6 +62,8 @@ def robotInit(self):
6162
self._robot_file = robot_file
6263
self._robot_class = TestRobot
6364

65+
self._init_timeout = init_timeout
66+
6467
self._physics = physics
6568

6669
if physics:
@@ -164,7 +167,7 @@ def control(self, reraise, robot: wpilib.RobotBase) -> TestController:
164167
"""
165168
A pytest fixture that provides control over your robot
166169
"""
167-
return TestController(reraise, robot)
170+
return TestController(reraise, robot, self._init_timeout)
168171

169172
@pytest.fixture()
170173
def robot_file(self) -> pathlib.Path:

tests/test_pytest_plugins.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ def _configure_pyfrc_plugin(pytester, robot_class="DummyRobot"):
7575
7676
def pytest_configure(config):
7777
robot_file = pathlib.Path(__file__).resolve()
78-
config.pluginmanager.register(PyFrcPlugin({robot_class}, robot_file, False))
78+
config.pluginmanager.register(PyFrcPlugin({robot_class}, robot_file, False, 2.0))
7979
""")
8080

8181

@@ -92,7 +92,7 @@ def pytest_configure(config):
9292
return
9393
robot_file = pathlib.Path(__file__).resolve()
9494
config.pluginmanager.register(
95-
IsolatedTestsPlugin({robot_class}, robot_file, False, False, {parallelism})
95+
IsolatedTestsPlugin({robot_class}, robot_file, False, False, {parallelism}, 2.0)
9696
)
9797
""")
9898

0 commit comments

Comments
 (0)