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

This commit is contained in:
2026-07-25 10:26:29 +02:00
parent d6cda77aaf
commit 1f110866f5
170 changed files with 24657 additions and 3 deletions
+141
View File
@@ -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