124 lines
3.7 KiB
Python
124 lines
3.7 KiB
Python
import can
|
|
import pytest
|
|
|
|
from lingkong_motor_service.config import Settings
|
|
from lingkong_motor_service.protocol import build_open_loop_frame
|
|
from lingkong_motor_service.transport import (
|
|
CanTransportError,
|
|
DisabledCanTransport,
|
|
PythonCanTransport,
|
|
)
|
|
|
|
|
|
class StubBus:
|
|
def __init__(self) -> None:
|
|
self.messages: list[can.Message] = []
|
|
self.timeouts: list[float | None] = []
|
|
self.shutdown_count = 0
|
|
self.received_messages: list[can.Message] = []
|
|
|
|
def send(self, message: can.Message, timeout: float | None = None) -> None:
|
|
self.messages.append(message)
|
|
self.timeouts.append(timeout)
|
|
|
|
def shutdown(self) -> None:
|
|
self.shutdown_count += 1
|
|
|
|
def recv(self, timeout: float | None = None) -> can.Message | None:
|
|
del timeout
|
|
if not self.received_messages:
|
|
return None
|
|
return self.received_messages.pop(0)
|
|
|
|
|
|
def test_canalystii_configuration_and_standard_frame(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
captured: dict[str, object] = {}
|
|
bus = StubBus()
|
|
|
|
def open_bus(**kwargs: object) -> StubBus:
|
|
captured.update(kwargs)
|
|
return bus
|
|
|
|
monkeypatch.setattr(can, "Bus", open_bus)
|
|
transport = PythonCanTransport(Settings(hardware_enabled=True, can_channel=1, can_device=2))
|
|
|
|
try:
|
|
transport.send(build_open_loop_frame({1: 400, 2: -400}))
|
|
|
|
assert captured == {
|
|
"interface": "canalystii",
|
|
"channel": 1,
|
|
"device": 2,
|
|
"bitrate": 1_000_000,
|
|
"rx_queue_size": 256,
|
|
"receive_own_messages": False,
|
|
}
|
|
assert bus.timeouts == [None]
|
|
assert len(bus.messages) == 1
|
|
assert bus.messages[0].arbitration_id == 0x280
|
|
assert bus.messages[0].is_extended_id is False
|
|
assert bytes(bus.messages[0].data) == bytes.fromhex("90 01 70 FE 00 00 00 00")
|
|
finally:
|
|
transport.shutdown()
|
|
|
|
|
|
def test_disabled_transport_fails_closed() -> None:
|
|
with pytest.raises(CanTransportError, match="hardware access is disabled"):
|
|
DisabledCanTransport().send(build_open_loop_frame({1: 0}))
|
|
|
|
|
|
def test_canalystii_can_wait_for_the_hardware_tx_queue(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
bus = StubBus()
|
|
monkeypatch.setattr(can, "Bus", lambda **kwargs: bus)
|
|
transport = PythonCanTransport(Settings(hardware_enabled=True))
|
|
|
|
try:
|
|
transport.send(
|
|
build_open_loop_frame({}),
|
|
tx_queue_timeout_s=0.1,
|
|
)
|
|
|
|
assert bus.timeouts == [0.1]
|
|
finally:
|
|
transport.shutdown()
|
|
|
|
|
|
def test_shutdown_is_idempotent_and_send_after_shutdown_fails(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
bus = StubBus()
|
|
monkeypatch.setattr(can, "Bus", lambda **kwargs: bus)
|
|
transport = PythonCanTransport(Settings(hardware_enabled=True))
|
|
|
|
transport.shutdown()
|
|
transport.shutdown()
|
|
|
|
assert transport.is_open is False
|
|
assert bus.shutdown_count == 1
|
|
with pytest.raises(CanTransportError, match="CAN transport is closed"):
|
|
transport.send(build_open_loop_frame({1: 0}))
|
|
|
|
|
|
def test_canalystii_receives_standard_status_frame(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
bus = StubBus()
|
|
bus.received_messages.append(
|
|
can.Message(
|
|
arbitration_id=0x141,
|
|
is_extended_id=False,
|
|
data=bytes.fromhex("9C 4F 64 00 0A 00 34 12"),
|
|
)
|
|
)
|
|
monkeypatch.setattr(can, "Bus", lambda **kwargs: bus)
|
|
transport = PythonCanTransport(Settings(hardware_enabled=True))
|
|
|
|
try:
|
|
frame = transport.receive(timeout_s=0.01)
|
|
|
|
assert frame is not None
|
|
assert frame.arbitration_id == 0x141
|
|
assert frame.data == bytes.fromhex("9C 4F 64 00 0A 00 34 12")
|
|
finally:
|
|
transport.shutdown()
|