forked from genewildish/Mainline
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
This commit is contained in:
123
tests/test_sixel.py
Normal file
123
tests/test_sixel.py
Normal file
@@ -0,0 +1,123 @@
|
||||
"""
|
||||
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 == ""
|
||||
Reference in New Issue
Block a user