feat(simulation): add provider-neutral worker profile
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import replace
|
||||
|
||||
import pytest
|
||||
|
||||
from k1link.simulation.contracts import ControlProfile
|
||||
from k1link.simulation.provider_contract import (
|
||||
ProviderRole,
|
||||
SimulationClockDescriptor,
|
||||
SimulationProviderContractError,
|
||||
SimulationProviderDescriptor,
|
||||
SimulationProviderProfile,
|
||||
)
|
||||
from k1link.simulation.stock_rover import STOCK_ROVER_PROVIDER_PROFILE
|
||||
|
||||
|
||||
def _unreal_profile() -> SimulationProviderProfile:
|
||||
return SimulationProviderProfile(
|
||||
profile_id="unreal-native-rover-v1",
|
||||
providers=(
|
||||
SimulationProviderDescriptor(
|
||||
provider_id="unreal-native",
|
||||
roles=(
|
||||
ProviderRole.WORLD,
|
||||
ProviderRole.PHYSICS,
|
||||
ProviderRole.STATE,
|
||||
ProviderRole.SENSOR,
|
||||
),
|
||||
capabilities=(
|
||||
"clock.simulation",
|
||||
"state.vehicle-pose",
|
||||
"truth.ground-truth",
|
||||
"sensor.virtual",
|
||||
),
|
||||
),
|
||||
SimulationProviderDescriptor(
|
||||
provider_id="unreal-direct-control",
|
||||
roles=(ProviderRole.CONTROLLER,),
|
||||
capabilities=("command.rover-speed-steering/v1",),
|
||||
),
|
||||
),
|
||||
clock=SimulationClockDescriptor(
|
||||
provider_id="unreal-native",
|
||||
domain="unreal:fixed-step",
|
||||
),
|
||||
control_profiles=(ControlProfile.ROVER_SPEED_STEERING_V1,),
|
||||
)
|
||||
|
||||
|
||||
def test_stock_rover_provider_profile_round_trips_exactly() -> None:
|
||||
restored = SimulationProviderProfile.from_dict(STOCK_ROVER_PROVIDER_PROFILE.to_dict())
|
||||
|
||||
assert restored == STOCK_ROVER_PROVIDER_PROFILE
|
||||
assert restored.clock.domain == "gazebo:/clock"
|
||||
assert any(ProviderRole.CONTROLLER in provider.roles for provider in restored.providers)
|
||||
|
||||
|
||||
def test_provider_contract_accepts_unreal_without_changing_canonical_frames() -> None:
|
||||
profile = SimulationProviderProfile.from_dict(_unreal_profile().to_dict())
|
||||
|
||||
assert profile.clock.domain == "unreal:fixed-step"
|
||||
assert profile.world_frame == "map_enu"
|
||||
assert profile.body_frame == "base_link_flu"
|
||||
|
||||
|
||||
def test_provider_contract_rejects_missing_clock_capability() -> None:
|
||||
profile = _unreal_profile()
|
||||
world = profile.providers[0]
|
||||
|
||||
with pytest.raises(SimulationProviderContractError, match="clock.simulation"):
|
||||
replace(
|
||||
profile,
|
||||
providers=(
|
||||
replace(
|
||||
world,
|
||||
capabilities=tuple(
|
||||
capability
|
||||
for capability in world.capabilities
|
||||
if capability != "clock.simulation"
|
||||
),
|
||||
),
|
||||
profile.providers[1],
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def test_provider_contract_rejects_undeclared_command_capability() -> None:
|
||||
profile = _unreal_profile()
|
||||
controller = profile.providers[1]
|
||||
|
||||
with pytest.raises(SimulationProviderContractError, match="control profiles"):
|
||||
replace(
|
||||
profile,
|
||||
providers=(
|
||||
profile.providers[0],
|
||||
replace(controller, capabilities=("transport.custom",)),
|
||||
),
|
||||
)
|
||||
Reference in New Issue
Block a user