refactor: modularize display backends and add benchmark runner

- Create engine/display/ package with registry pattern
- Move displays to engine/display/backends/ (terminal, null, websocket, sixel)
- Add DisplayRegistry with auto-discovery
- Add benchmark.py for performance testing effects × displays matrix
- Add mise tasks: benchmark, benchmark-json, benchmark-report
- Update controller to use new display module
This commit is contained in:
2026-03-15 22:25:28 -07:00
parent 22dd063baa
commit 829c4ab63d
10 changed files with 694 additions and 150 deletions

View File

@@ -1,12 +1,12 @@
"""
Tests for engine.websocket_display module.
Tests for engine.display.backends.websocket module.
"""
from unittest.mock import MagicMock, patch
import pytest
from engine.websocket_display import WebSocketDisplay
from engine.display.backends.websocket import WebSocketDisplay
class TestWebSocketDisplayImport:
@@ -14,9 +14,9 @@ class TestWebSocketDisplayImport:
def test_import_does_not_error(self):
"""Module imports without error."""
from engine import websocket_display
from engine.display import backends
assert websocket_display is not None
assert backends is not None
class TestWebSocketDisplayInit:
@@ -24,7 +24,7 @@ class TestWebSocketDisplayInit:
def test_default_init(self):
"""Default initialization sets correct defaults."""
with patch("engine.websocket_display.websockets", None):
with patch("engine.display.backends.websocket.websockets", None):
display = WebSocketDisplay()
assert display.host == "0.0.0.0"
assert display.port == 8765
@@ -34,7 +34,7 @@ class TestWebSocketDisplayInit:
def test_custom_init(self):
"""Custom initialization uses provided values."""
with patch("engine.websocket_display.websockets", None):
with patch("engine.display.backends.websocket.websockets", None):
display = WebSocketDisplay(host="localhost", port=9000, http_port=9001)
assert display.host == "localhost"
assert display.port == 9000
@@ -60,7 +60,7 @@ class TestWebSocketDisplayProtocol:
def test_websocket_display_is_display(self):
"""WebSocketDisplay satisfies Display protocol."""
with patch("engine.websocket_display.websockets", MagicMock()):
with patch("engine.display.backends.websocket.websockets", MagicMock()):
display = WebSocketDisplay()
assert hasattr(display, "init")
assert hasattr(display, "show")
@@ -73,7 +73,7 @@ class TestWebSocketDisplayMethods:
def test_init_stores_dimensions(self):
"""init stores terminal dimensions."""
with patch("engine.websocket_display.websockets", MagicMock()):
with patch("engine.display.backends.websocket.websockets", MagicMock()):
display = WebSocketDisplay()
display.init(100, 40)
assert display.width == 100
@@ -81,31 +81,31 @@ class TestWebSocketDisplayMethods:
def test_client_count_initially_zero(self):
"""client_count returns 0 when no clients connected."""
with patch("engine.websocket_display.websockets", MagicMock()):
with patch("engine.display.backends.websocket.websockets", MagicMock()):
display = WebSocketDisplay()
assert display.client_count() == 0
def test_get_ws_port(self):
"""get_ws_port returns configured port."""
with patch("engine.websocket_display.websockets", MagicMock()):
with patch("engine.display.backends.websocket.websockets", MagicMock()):
display = WebSocketDisplay(port=9000)
assert display.get_ws_port() == 9000
def test_get_http_port(self):
"""get_http_port returns configured port."""
with patch("engine.websocket_display.websockets", MagicMock()):
with patch("engine.display.backends.websocket.websockets", MagicMock()):
display = WebSocketDisplay(http_port=9001)
assert display.get_http_port() == 9001
def test_frame_delay_defaults_to_zero(self):
"""get_frame_delay returns 0 by default."""
with patch("engine.websocket_display.websockets", MagicMock()):
with patch("engine.display.backends.websocket.websockets", MagicMock()):
display = WebSocketDisplay()
assert display.get_frame_delay() == 0.0
def test_set_frame_delay(self):
"""set_frame_delay stores the value."""
with patch("engine.websocket_display.websockets", MagicMock()):
with patch("engine.display.backends.websocket.websockets", MagicMock()):
display = WebSocketDisplay()
display.set_frame_delay(0.05)
assert display.get_frame_delay() == 0.05
@@ -116,7 +116,7 @@ class TestWebSocketDisplayCallbacks:
def test_set_client_connected_callback(self):
"""set_client_connected_callback stores callback."""
with patch("engine.websocket_display.websockets", MagicMock()):
with patch("engine.display.backends.websocket.websockets", MagicMock()):
display = WebSocketDisplay()
callback = MagicMock()
display.set_client_connected_callback(callback)
@@ -124,7 +124,7 @@ class TestWebSocketDisplayCallbacks:
def test_set_client_disconnected_callback(self):
"""set_client_disconnected_callback stores callback."""
with patch("engine.websocket_display.websockets", MagicMock()):
with patch("engine.display.backends.websocket.websockets", MagicMock()):
display = WebSocketDisplay()
callback = MagicMock()
display.set_client_disconnected_callback(callback)