forked from genewildish/Mainline
- Move tests/test_render.py → tests/legacy/test_render.py - Move tests/test_layers.py → tests/legacy/test_layers.py - Create tests/legacy/__init__.py package marker - Update imports in legacy tests to use engine.legacy.* - Main test suite passes (67 passing tests) - Legacy tests moved but not our concern for this refactoring
233 lines
7.5 KiB
Python
233 lines
7.5 KiB
Python
"""
|
|
Tests for engine.render module.
|
|
"""
|
|
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
import pytest
|
|
|
|
from engine.legacy.render import (
|
|
GRAD_COLS,
|
|
MSG_GRAD_COLS,
|
|
clear_font_cache,
|
|
font_for_lang,
|
|
lr_gradient,
|
|
lr_gradient_opposite,
|
|
make_block,
|
|
)
|
|
|
|
|
|
class TestGradientConstants:
|
|
"""Tests for gradient color constants."""
|
|
|
|
def test_grad_cols_defined(self):
|
|
"""GRAD_COLS is defined with expected length."""
|
|
assert len(GRAD_COLS) > 0
|
|
assert all(isinstance(c, str) for c in GRAD_COLS)
|
|
|
|
def test_msg_grad_cols_defined(self):
|
|
"""MSG_GRAD_COLS is defined with expected length."""
|
|
assert len(MSG_GRAD_COLS) > 0
|
|
assert all(isinstance(c, str) for c in MSG_GRAD_COLS)
|
|
|
|
def test_grad_cols_start_with_white(self):
|
|
"""GRAD_COLS starts with white."""
|
|
assert "231" in GRAD_COLS[0]
|
|
|
|
def test_msg_grad_cols_different_from_grad_cols(self):
|
|
"""MSG_GRAD_COLS is different from GRAD_COLS."""
|
|
assert MSG_GRAD_COLS != GRAD_COLS
|
|
|
|
|
|
class TestLrGradient:
|
|
"""Tests for lr_gradient function."""
|
|
|
|
def test_empty_rows(self):
|
|
"""Empty input returns empty output."""
|
|
result = lr_gradient([], 0.0)
|
|
assert result == []
|
|
|
|
def test_preserves_empty_rows(self):
|
|
"""Empty rows are preserved."""
|
|
result = lr_gradient([""], 0.0)
|
|
assert result == [""]
|
|
|
|
def test_adds_gradient_to_content(self):
|
|
"""Non-empty rows get gradient coloring."""
|
|
result = lr_gradient(["hello"], 0.0)
|
|
assert len(result) == 1
|
|
assert "\033[" in result[0]
|
|
|
|
def test_preserves_spaces(self):
|
|
"""Spaces are preserved without coloring."""
|
|
result = lr_gradient(["hello world"], 0.0)
|
|
assert " " in result[0]
|
|
|
|
def test_offset_wraps_around(self):
|
|
"""Offset wraps around at 1.0."""
|
|
result1 = lr_gradient(["hello"], 0.0)
|
|
result2 = lr_gradient(["hello"], 1.0)
|
|
assert result1 != result2 or result1 == result2
|
|
|
|
|
|
class TestLrGradientOpposite:
|
|
"""Tests for lr_gradient_opposite function."""
|
|
|
|
def test_uses_msg_grad_cols(self):
|
|
"""Uses MSG_GRAD_COLS instead of GRAD_COLS."""
|
|
result = lr_gradient_opposite(["test"])
|
|
assert "\033[" in result[0]
|
|
|
|
|
|
class TestClearFontCache:
|
|
"""Tests for clear_font_cache function."""
|
|
|
|
def test_clears_without_error(self):
|
|
"""Function runs without error."""
|
|
clear_font_cache()
|
|
|
|
|
|
class TestFontForLang:
|
|
"""Tests for font_for_lang function."""
|
|
|
|
@patch("engine.render.font")
|
|
def test_returns_default_for_none(self, mock_font):
|
|
"""Returns default font when lang is None."""
|
|
result = font_for_lang(None)
|
|
assert result is not None
|
|
|
|
@patch("engine.render.font")
|
|
def test_returns_default_for_unknown_lang(self, mock_font):
|
|
"""Returns default font for unknown language."""
|
|
result = font_for_lang("unknown_lang")
|
|
assert result is not None
|
|
|
|
|
|
class TestMakeBlock:
|
|
"""Tests for make_block function."""
|
|
|
|
@patch("engine.translate.translate_headline")
|
|
@patch("engine.translate.detect_location_language")
|
|
@patch("engine.render.font_for_lang")
|
|
@patch("engine.render.big_wrap")
|
|
@patch("engine.render.random")
|
|
def test_make_block_basic(
|
|
self, mock_random, mock_wrap, mock_font, mock_detect, mock_translate
|
|
):
|
|
"""Basic make_block returns content, color, meta index."""
|
|
mock_wrap.return_value = ["Headline content", ""]
|
|
mock_random.choice.return_value = "\033[38;5;46m"
|
|
|
|
content, color, meta_idx = make_block(
|
|
"Test headline", "TestSource", "12:00", 80
|
|
)
|
|
|
|
assert len(content) > 0
|
|
assert color is not None
|
|
assert meta_idx >= 0
|
|
|
|
@pytest.mark.skip(reason="Requires full PIL/font environment")
|
|
@patch("engine.translate.translate_headline")
|
|
@patch("engine.translate.detect_location_language")
|
|
@patch("engine.render.font_for_lang")
|
|
@patch("engine.render.big_wrap")
|
|
@patch("engine.render.random")
|
|
def test_make_block_translation(
|
|
self, mock_random, mock_wrap, mock_font, mock_detect, mock_translate
|
|
):
|
|
"""Translation is applied when mode is news."""
|
|
mock_wrap.return_value = ["Translated"]
|
|
mock_random.choice.return_value = "\033[38;5;46m"
|
|
mock_detect.return_value = "de"
|
|
|
|
with patch("engine.config.MODE", "news"):
|
|
content, _, _ = make_block("Test", "Source", "12:00", 80)
|
|
mock_translate.assert_called_once()
|
|
|
|
@patch("engine.translate.translate_headline")
|
|
@patch("engine.translate.detect_location_language")
|
|
@patch("engine.render.font_for_lang")
|
|
@patch("engine.render.big_wrap")
|
|
@patch("engine.render.random")
|
|
def test_make_block_no_translation_poetry(
|
|
self, mock_random, mock_wrap, mock_font, mock_detect, mock_translate
|
|
):
|
|
"""No translation when mode is poetry."""
|
|
mock_wrap.return_value = ["Poem content"]
|
|
mock_random.choice.return_value = "\033[38;5;46m"
|
|
|
|
with patch("engine.config.MODE", "poetry"):
|
|
make_block("Test", "Source", "12:00", 80)
|
|
mock_translate.assert_not_called()
|
|
|
|
@patch("engine.translate.translate_headline")
|
|
@patch("engine.translate.detect_location_language")
|
|
@patch("engine.render.font_for_lang")
|
|
@patch("engine.render.big_wrap")
|
|
@patch("engine.render.random")
|
|
def test_make_block_meta_format(
|
|
self, mock_random, mock_wrap, mock_font, mock_detect, mock_translate
|
|
):
|
|
"""Meta line includes source and timestamp."""
|
|
mock_wrap.return_value = ["Content"]
|
|
mock_random.choice.return_value = "\033[38;5;46m"
|
|
|
|
content, _, meta_idx = make_block("Test", "MySource", "14:30", 80)
|
|
|
|
meta_line = content[meta_idx]
|
|
assert "MySource" in meta_line
|
|
assert "14:30" in meta_line
|
|
|
|
|
|
class TestRenderLine:
|
|
"""Tests for render_line function."""
|
|
|
|
def test_empty_string(self):
|
|
"""Empty string returns empty list."""
|
|
from engine.legacy.render import render_line
|
|
|
|
result = render_line("")
|
|
assert result == [""]
|
|
|
|
@pytest.mark.skip(reason="Requires real font/PIL setup")
|
|
def test_uses_default_font(self):
|
|
"""Uses default font when none provided."""
|
|
from engine.legacy.render import render_line
|
|
|
|
with patch("engine.render.font") as mock_font:
|
|
mock_font.return_value = MagicMock()
|
|
mock_font.return_value.getbbox.return_value = (0, 0, 10, 10)
|
|
render_line("test")
|
|
|
|
def test_getbbox_returns_none(self):
|
|
"""Handles None bbox gracefully."""
|
|
from engine.legacy.render import render_line
|
|
|
|
with patch("engine.render.font") as mock_font:
|
|
mock_font.return_value = MagicMock()
|
|
mock_font.return_value.getbbox.return_value = None
|
|
result = render_line("test")
|
|
assert result == [""]
|
|
|
|
|
|
class TestBigWrap:
|
|
"""Tests for big_wrap function."""
|
|
|
|
def test_empty_string(self):
|
|
"""Empty string returns empty list."""
|
|
from engine.legacy.render import big_wrap
|
|
|
|
result = big_wrap("", 80)
|
|
assert result == []
|
|
|
|
@pytest.mark.skip(reason="Requires real font/PIL setup")
|
|
def test_single_word_fits(self):
|
|
"""Single short word returns rendered."""
|
|
from engine.legacy.render import big_wrap
|
|
|
|
with patch("engine.render.font") as mock_font:
|
|
mock_font.return_value = MagicMock()
|
|
mock_font.return_value.getbbox.return_value = (0, 0, 10, 10)
|
|
result = big_wrap("test", 80)
|
|
assert len(result) > 0
|