Files
sideline/tests/test_sixel.py
David Gwilliam 9e4d54a82e feat(tests): improve coverage to 56%, add benchmark regression tests
- 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
2026-03-15 23:26:10 -07:00

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 == ""