forked from genewildish/Mainline
- Add EffectPlugin ABC with @abstractmethod decorators for interface enforcement - Add runtime interface checking in discover_plugins() with issubclass() - Add EffectContext factory with sensible defaults - Standardize Display __init__ (remove redundant init in TerminalDisplay) - Document effect behavior when ticker_height=0 - Evaluate legacy effects: document coexistence, no deprecation needed - Research plugin patterns (VST, Python entry points) - Fix pysixel dependency (removed broken dependency) Test coverage improvements: - Add DisplayRegistry tests - Add MultiDisplay tests - Add SixelDisplay tests - Add controller._get_display tests - Add effects controller command handling tests - Add benchmark regression tests (@pytest.mark.benchmark) - Add pytest marker for benchmark tests in pyproject.toml Documentation updates: - Update AGENTS.md with 56% coverage stats and effect plugin docs - Update README.md with Sixel display mode and benchmark commands - Add new modules to architecture section
124 lines
3.8 KiB
Python
124 lines
3.8 KiB
Python
"""
|
|
Tests for engine.display.backends.sixel module.
|
|
"""
|
|
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
|
|
class TestSixelDisplay:
|
|
"""Tests for SixelDisplay class."""
|
|
|
|
def test_init_stores_dimensions(self):
|
|
"""init stores dimensions."""
|
|
from engine.display.backends.sixel import SixelDisplay
|
|
|
|
display = SixelDisplay()
|
|
display.init(80, 24)
|
|
assert display.width == 80
|
|
assert display.height == 24
|
|
|
|
def test_init_custom_cell_size(self):
|
|
"""init accepts custom cell size."""
|
|
from engine.display.backends.sixel import SixelDisplay
|
|
|
|
display = SixelDisplay(cell_width=12, cell_height=18)
|
|
assert display.cell_width == 12
|
|
assert display.cell_height == 18
|
|
|
|
def test_show_handles_empty_buffer(self):
|
|
"""show handles empty buffer gracefully."""
|
|
from engine.display.backends.sixel import SixelDisplay
|
|
|
|
display = SixelDisplay()
|
|
display.init(80, 24)
|
|
|
|
with patch("engine.display.backends.sixel._encode_sixel") as mock_encode:
|
|
mock_encode.return_value = ""
|
|
display.show([])
|
|
|
|
def test_show_handles_pil_import_error(self):
|
|
"""show gracefully handles missing PIL."""
|
|
from engine.display.backends.sixel import SixelDisplay
|
|
|
|
display = SixelDisplay()
|
|
display.init(80, 24)
|
|
|
|
with patch.dict("sys.modules", {"PIL": None}):
|
|
display.show(["test line"])
|
|
|
|
def test_clear_sends_escape_sequence(self):
|
|
"""clear sends clear screen escape sequence."""
|
|
from engine.display.backends.sixel import SixelDisplay
|
|
|
|
display = SixelDisplay()
|
|
|
|
with patch("sys.stdout") as mock_stdout:
|
|
display.clear()
|
|
mock_stdout.buffer.write.assert_called()
|
|
|
|
def test_cleanup_does_nothing(self):
|
|
"""cleanup does nothing."""
|
|
from engine.display.backends.sixel import SixelDisplay
|
|
|
|
display = SixelDisplay()
|
|
display.cleanup()
|
|
|
|
|
|
class TestSixelAnsiParsing:
|
|
"""Tests for ANSI parsing in SixelDisplay."""
|
|
|
|
def test_parse_empty_string(self):
|
|
"""handles empty string."""
|
|
from engine.display.backends.sixel import _parse_ansi
|
|
|
|
result = _parse_ansi("")
|
|
assert len(result) > 0
|
|
|
|
def test_parse_plain_text(self):
|
|
"""parses plain text without ANSI codes."""
|
|
from engine.display.backends.sixel import _parse_ansi
|
|
|
|
result = _parse_ansi("hello world")
|
|
assert len(result) == 1
|
|
text, fg, bg, bold = result[0]
|
|
assert text == "hello world"
|
|
|
|
def test_parse_with_color_codes(self):
|
|
"""parses ANSI color codes."""
|
|
from engine.display.backends.sixel import _parse_ansi
|
|
|
|
result = _parse_ansi("\033[31mred\033[0m")
|
|
assert len(result) == 2
|
|
|
|
def test_parse_with_bold(self):
|
|
"""parses bold codes."""
|
|
from engine.display.backends.sixel import _parse_ansi
|
|
|
|
result = _parse_ansi("\033[1mbold\033[0m")
|
|
assert len(result) == 2
|
|
|
|
def test_parse_256_color(self):
|
|
"""parses 256 color codes."""
|
|
from engine.display.backends.sixel import _parse_ansi
|
|
|
|
result = _parse_ansi("\033[38;5;196mred\033[0m")
|
|
assert len(result) == 2
|
|
|
|
|
|
class TestSixelEncoding:
|
|
"""Tests for Sixel encoding."""
|
|
|
|
def test_encode_empty_image(self):
|
|
"""handles empty image."""
|
|
from engine.display.backends.sixel import _encode_sixel
|
|
|
|
with patch("PIL.Image.Image") as mock_image:
|
|
mock_img_instance = MagicMock()
|
|
mock_img_instance.convert.return_value = mock_img_instance
|
|
mock_img_instance.size = (0, 0)
|
|
mock_img_instance.load.return_value = {}
|
|
mock_image.return_value = mock_img_instance
|
|
|
|
result = _encode_sixel(mock_img_instance)
|
|
assert result == ""
|