testing.pytest_plugin

Pytest plugin for the raccoon sim test harness.

This module is registered as a pytest11 entry point in the raccoon wheel, so once raccoon is installed in a project’s test environment the fixtures below are available with no conftest boilerplate.

The three fixtures that matter:

  • robot: a fully wired instance of the project’s generated src.hardware.robot.Robot class, backed by the mock HAL.

  • scene: a factory for entering a raccoon.testing.sim.use_scene() context. Automatically resolves scene names against the project’s own scenes/ dir first, then raccoon’s bundled scenes.

  • run_step: a sync callable that awaits step.run_step(robot) with a timeout. Tests stay sync; no pytest-asyncio dependency.

Typical usage:

from raccoon.step.motion.drive_dsl import drive_forward
from raccoon.testing.sim import pose

def test_drives_30cm(robot, scene, run_step):
    scene("empty_table.ftmap", start=(20, 50, 0))
    run_step(drive_forward(cm=30), robot)
    assert pose().x > 45

Attributes

ROBOT_IMPORT_PATH

ROBOT_CLASS_NAME

Functions

project_info(→ testing._project.ProjectInfo)

The discovered raccoon project (root, raw yml data, derived SimRobotConfig).

robot_sim_config(→ testing.sim.SimRobotConfig)

A fresh SimRobotConfig derived from the project's yml.

robot(→ Any)

Instantiate the project's generated Robot class.

scene(→ Callable[Ellipsis, None])

Factory that enters a use_scene context for the current test.

run_step(→ Callable[Ellipsis, Any])

Sync wrapper that awaits step.run_step(robot) with a timeout.

pytest_sessionfinish(→ None)

Bypass interpreter shutdown when the mock HAL was exercised.

Module Contents

testing.pytest_plugin.ROBOT_IMPORT_PATH = 'src.hardware.robot'
testing.pytest_plugin.ROBOT_CLASS_NAME = 'Robot'
testing.pytest_plugin.project_info() testing._project.ProjectInfo

The discovered raccoon project (root, raw yml data, derived SimRobotConfig).

Session-scoped because the project layout doesn’t change during a test run. If your tests aren’t inside a raccoon project, requesting this fixture fails with a clear error.

testing.pytest_plugin.robot_sim_config(project_info: testing._project.ProjectInfo) testing.sim.SimRobotConfig

A fresh SimRobotConfig derived from the project’s yml.

Function-scoped so individual tests can mutate it (e.g. add line_sensors) without leaking state across tests. Use this as an override hook when a test needs non-default sim geometry.

testing.pytest_plugin.robot(project_info: testing._project.ProjectInfo) Any

Instantiate the project’s generated Robot class.

Skips with a helpful message if the installed raccoon wheel wasn’t built with the mock driver bundle. Uses a fresh instance per test so any mutable state on the Robot (motion history, calibration caches) doesn’t leak between tests.

testing.pytest_plugin.scene(project_info: testing._project.ProjectInfo, robot_sim_config: testing.sim.SimRobotConfig) Callable[Ellipsis, None]

Factory that enters a use_scene context for the current test.

Call it once per test:

def test_foo(robot, scene, run_step):
    scene("empty_table.ftmap", start=(20, 50, 0))
    run_step(my_step, robot)

The scene is detached automatically at the end of the test. Calling scene twice in one test replaces the previous scene.

testing.pytest_plugin.run_step() Callable[Ellipsis, Any]

Sync wrapper that awaits step.run_step(robot) with a timeout.

Tests stay sync so this plugin doesn’t drag in pytest-asyncio. If you need multiple awaits sharing an event loop, write the test as async def and use pytest-asyncio yourself — run_step is the simple path for the common case.

testing.pytest_plugin.pytest_sessionfinish(session: Any, exitstatus: int) None

Bypass interpreter shutdown when the mock HAL was exercised.

The pybind11-bound MockPlatform singleton has a destruction-order race with the motor/IMU wrapper destructors that can segfault at interpreter shutdown. The library’s own end-to-end tests work around this by running the mission in a subprocess that ``os._exit``s before any destructors run. The plugin does the same thing here, transparently.

Only fires when: - The robot fixture was actually used (so we know the mock HAL

has state to tear down).

  • All tests passed — on failure we want pytest’s normal exit path so CI gets an accurate error code and any debugger post-mortem runs.

  • The user hasn’t set RACCOON_TESTING_NO_EXIT_SHORTCUT=1 to debug.