Update docs and screenshots
CI / backend (pull_request) Canceled after 0s
CI / shell (pull_request) Canceled after 0s
CI / frontend (pull_request) Canceled after 0s
CI / arm64-smoke (pull_request) Canceled after 0s
CI / backend (push) Canceled after 0s
CI / shell (push) Canceled after 0s
CI / frontend (push) Canceled after 0s
CI / arm64-smoke (push) Canceled after 0s
CI / backend (pull_request) Canceled after 0s
CI / shell (pull_request) Canceled after 0s
CI / frontend (pull_request) Canceled after 0s
CI / arm64-smoke (pull_request) Canceled after 0s
CI / backend (push) Canceled after 0s
CI / shell (push) Canceled after 0s
CI / frontend (push) Canceled after 0s
CI / arm64-smoke (push) Canceled after 0s
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
import pytest
|
||||
from app.dmx.backends import FakeOlaClientAdapter, OlaDmxBackend, PythonOlaClientAdapter
|
||||
from app.dmx.frame import DmxFrame
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_ola_backend_sends_full_frame_and_updates_status() -> None:
|
||||
adapter = FakeOlaClientAdapter()
|
||||
backend = OlaDmxBackend(
|
||||
universe=7,
|
||||
output_port="usb-dmx-1",
|
||||
adapter_factory=lambda: adapter,
|
||||
)
|
||||
frame = DmxFrame(universe=7)
|
||||
frame.set_channel(1, 255, "scene")
|
||||
frame.set_channel(512, 64, "scene")
|
||||
|
||||
await backend.send_frame(frame)
|
||||
|
||||
status = backend.get_status()
|
||||
assert status.connected is True
|
||||
assert status.degraded is False
|
||||
assert status.frames_sent == 1
|
||||
assert status.send_errors == 0
|
||||
assert status.last_successful_frame is not None
|
||||
assert status.selected_universe == 7
|
||||
assert status.selected_output_port == "usb-dmx-1"
|
||||
assert adapter.open_calls == 1
|
||||
assert len(adapter.sent_frames) == 1
|
||||
sent_universe, values = adapter.sent_frames[0]
|
||||
assert sent_universe == 7
|
||||
assert len(values) == 512
|
||||
assert values[0] == 255
|
||||
assert values[511] == 64
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_ola_backend_reconnects_after_send_failure() -> None:
|
||||
created_adapters: list[FakeOlaClientAdapter] = []
|
||||
|
||||
def factory() -> FakeOlaClientAdapter:
|
||||
adapter = FakeOlaClientAdapter(
|
||||
send_results=[RuntimeError("olad unavailable")] if not created_adapters else [],
|
||||
)
|
||||
created_adapters.append(adapter)
|
||||
return adapter
|
||||
|
||||
backend = OlaDmxBackend(
|
||||
universe=1,
|
||||
output_port="usb-dmx-1",
|
||||
adapter_factory=factory,
|
||||
)
|
||||
frame = DmxFrame()
|
||||
frame.set_channel(1, 128, "scene")
|
||||
|
||||
with pytest.raises(RuntimeError, match="olad unavailable"):
|
||||
await backend.send_frame(frame)
|
||||
|
||||
failed_status = backend.get_status()
|
||||
assert failed_status.connected is False
|
||||
assert failed_status.degraded is True
|
||||
assert failed_status.send_errors == 1
|
||||
assert failed_status.frames_sent == 0
|
||||
|
||||
await backend.send_frame(frame)
|
||||
|
||||
recovered_status = backend.get_status()
|
||||
assert recovered_status.connected is True
|
||||
assert recovered_status.degraded is False
|
||||
assert recovered_status.reconnect_count == 1
|
||||
assert recovered_status.frames_sent == 1
|
||||
assert recovered_status.send_errors == 1
|
||||
assert len(created_adapters) == 2
|
||||
assert created_adapters[0].close_calls == 1
|
||||
assert created_adapters[1].sent_frames[0][1][0] == 128
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_ola_backend_does_not_send_random_black_frame_on_reconnect() -> None:
|
||||
created_adapters: list[FakeOlaClientAdapter] = []
|
||||
|
||||
def factory() -> FakeOlaClientAdapter:
|
||||
adapter = FakeOlaClientAdapter(
|
||||
send_results=[RuntimeError("send failed")] if not created_adapters else [],
|
||||
)
|
||||
created_adapters.append(adapter)
|
||||
return adapter
|
||||
|
||||
backend = OlaDmxBackend(
|
||||
universe=3,
|
||||
output_port="usb-dmx-2",
|
||||
adapter_factory=factory,
|
||||
)
|
||||
failed_frame = DmxFrame(universe=3)
|
||||
failed_frame.set_channel(1, 10, "effect")
|
||||
recovered_frame = DmxFrame(universe=3)
|
||||
recovered_frame.set_channel(1, 200, "effect")
|
||||
recovered_frame.set_channel(2, 150, "effect")
|
||||
|
||||
with pytest.raises(RuntimeError, match="send failed"):
|
||||
await backend.send_frame(failed_frame)
|
||||
|
||||
await backend.send_frame(recovered_frame)
|
||||
|
||||
assert created_adapters[0].sent_frames[0][1][0] == 10
|
||||
assert created_adapters[1].sent_frames[0][0] == 3
|
||||
assert created_adapters[1].sent_frames[0][1] == recovered_frame.values
|
||||
assert any(value != 0 for value in created_adapters[1].sent_frames[0][1])
|
||||
|
||||
|
||||
def test_python_ola_adapter_uses_buffer_with_tobytes() -> None:
|
||||
captured: dict[str, object] = {}
|
||||
|
||||
class FakeClient:
|
||||
def SendDmx(self, universe: int, payload: object, callback: object) -> None:
|
||||
captured["universe"] = universe
|
||||
captured["payload_type"] = type(payload).__name__
|
||||
captured["has_tobytes"] = hasattr(payload, "tobytes")
|
||||
callback(True)
|
||||
|
||||
class FakeWrapper:
|
||||
def __init__(self) -> None:
|
||||
self._client = FakeClient()
|
||||
|
||||
def Client(self) -> FakeClient:
|
||||
return self._client
|
||||
|
||||
def Run(self) -> None:
|
||||
return
|
||||
|
||||
def Stop(self) -> None:
|
||||
return
|
||||
|
||||
adapter = PythonOlaClientAdapter(timeout_s=0.1)
|
||||
adapter._wrapper_class = FakeWrapper
|
||||
|
||||
adapter.send_frame(1, [0, 255] + [0] * 510)
|
||||
|
||||
assert captured["universe"] == 1
|
||||
assert captured["payload_type"] == "array"
|
||||
assert captured["has_tobytes"] is True
|
||||
Reference in New Issue
Block a user