""" Tests for LayoutManager. Tests layout creation, management, rendering, and element positioning. """ import pytest import json from unittest.mock import MagicMock from src.layout_manager import LayoutManager class TestLayoutManager: """Test LayoutManager functionality.""" @pytest.fixture def tmp_layout_file(self, tmp_path): """Create a temporary layout file.""" layout_file = tmp_path / "custom_layouts.json" return str(layout_file) @pytest.fixture def mock_display_manager(self): """Create a mock display manager.""" dm = MagicMock() dm.clear = MagicMock() dm.update_display = MagicMock() dm.draw_text = MagicMock() dm.draw_weather_icon = MagicMock() dm.small_font = MagicMock() dm.regular_font = MagicMock() return dm @pytest.fixture def layout_manager(self, tmp_layout_file, mock_display_manager): """Create a LayoutManager instance.""" return LayoutManager( display_manager=mock_display_manager, config_path=tmp_layout_file ) def test_init(self, tmp_layout_file, mock_display_manager): """Test LayoutManager initialization.""" lm = LayoutManager( display_manager=mock_display_manager, config_path=tmp_layout_file ) assert lm.display_manager == mock_display_manager assert lm.config_path == tmp_layout_file assert lm.layouts == {} assert lm.current_layout is None def test_load_layouts_file_exists(self, tmp_path, mock_display_manager): """Test loading layouts from existing file.""" layout_file = tmp_path / "custom_layouts.json" layout_data = { "test_layout": { "elements": [{"type": "text", "x": 0, "y": 0}], "description": "Test layout" } } with open(layout_file, 'w') as f: json.dump(layout_data, f) lm = LayoutManager( display_manager=mock_display_manager, config_path=str(layout_file) ) assert "test_layout" in lm.layouts assert lm.layouts["test_layout"]["description"] == "Test layout" def test_load_layouts_file_not_exists(self, tmp_layout_file, mock_display_manager): """Test loading layouts when file doesn't exist.""" lm = LayoutManager( display_manager=mock_display_manager, config_path=tmp_layout_file ) assert lm.layouts == {} def test_create_layout(self, layout_manager): """Test creating a new layout.""" elements = [{"type": "text", "x": 10, "y": 20, "properties": {"text": "Hello"}}] result = layout_manager.create_layout("test_layout", elements, "Test description") assert result is True assert "test_layout" in layout_manager.layouts assert layout_manager.layouts["test_layout"]["elements"] == elements assert layout_manager.layouts["test_layout"]["description"] == "Test description" assert "created" in layout_manager.layouts["test_layout"] assert "modified" in layout_manager.layouts["test_layout"] def test_update_layout(self, layout_manager): """Test updating an existing layout.""" # Create a layout first elements1 = [{"type": "text", "x": 0, "y": 0}] layout_manager.create_layout("test_layout", elements1, "Original") # Update it elements2 = [{"type": "text", "x": 10, "y": 20}] result = layout_manager.update_layout("test_layout", elements2, "Updated") assert result is True assert layout_manager.layouts["test_layout"]["elements"] == elements2 assert layout_manager.layouts["test_layout"]["description"] == "Updated" assert "modified" in layout_manager.layouts["test_layout"] def test_update_layout_not_exists(self, layout_manager): """Test updating a non-existent layout.""" elements = [{"type": "text", "x": 0, "y": 0}] result = layout_manager.update_layout("nonexistent", elements) assert result is False def test_delete_layout(self, layout_manager): """Test deleting a layout.""" elements = [{"type": "text", "x": 0, "y": 0}] layout_manager.create_layout("test_layout", elements) result = layout_manager.delete_layout("test_layout") assert result is True assert "test_layout" not in layout_manager.layouts def test_delete_layout_not_exists(self, layout_manager): """Test deleting a non-existent layout.""" result = layout_manager.delete_layout("nonexistent") assert result is False def test_get_layout(self, layout_manager): """Test getting a specific layout.""" elements = [{"type": "text", "x": 0, "y": 0}] layout_manager.create_layout("test_layout", elements) layout = layout_manager.get_layout("test_layout") assert layout is not None assert layout["elements"] == elements def test_get_layout_not_exists(self, layout_manager): """Test getting a non-existent layout.""" layout = layout_manager.get_layout("nonexistent") assert layout == {} def test_list_layouts(self, layout_manager): """Test listing all layouts.""" layout_manager.create_layout("layout1", []) layout_manager.create_layout("layout2", []) layout_manager.create_layout("layout3", []) layouts = layout_manager.list_layouts() assert len(layouts) == 3 assert "layout1" in layouts assert "layout2" in layouts assert "layout3" in layouts def test_set_current_layout(self, layout_manager): """Test setting the current layout.""" layout_manager.create_layout("test_layout", []) result = layout_manager.set_current_layout("test_layout") assert result is True assert layout_manager.current_layout == "test_layout" def test_set_current_layout_not_exists(self, layout_manager): """Test setting a non-existent layout as current.""" result = layout_manager.set_current_layout("nonexistent") assert result is False assert layout_manager.current_layout is None def test_render_layout(self, layout_manager, mock_display_manager): """Test rendering a layout.""" elements = [ {"type": "text", "x": 0, "y": 0, "properties": {"text": "Hello"}}, {"type": "text", "x": 10, "y": 10, "properties": {"text": "World"}} ] layout_manager.create_layout("test_layout", elements) result = layout_manager.render_layout("test_layout") assert result is True mock_display_manager.clear.assert_called_once() mock_display_manager.update_display.assert_called_once() assert mock_display_manager.draw_text.call_count == 2 def test_render_layout_no_display_manager(self, tmp_layout_file): """Test rendering without display manager.""" lm = LayoutManager(display_manager=None, config_path=tmp_layout_file) lm.create_layout("test_layout", []) result = lm.render_layout("test_layout") assert result is False def test_render_layout_not_exists(self, layout_manager): """Test rendering a non-existent layout.""" result = layout_manager.render_layout("nonexistent") assert result is False def test_render_element_text(self, layout_manager, mock_display_manager): """Test rendering a text element.""" element = { "type": "text", "x": 10, "y": 20, "properties": { "text": "Hello", "color": [255, 0, 0], "font_size": "small" } } layout_manager.render_element(element, {}) mock_display_manager.draw_text.assert_called_once() call_args = mock_display_manager.draw_text.call_args assert call_args[0][0] == "Hello" # text assert call_args[0][1] == 10 # x assert call_args[0][2] == 20 # y def test_render_element_weather_icon(self, layout_manager, mock_display_manager): """Test rendering a weather icon element.""" element = { "type": "weather_icon", "x": 10, "y": 20, "properties": { "condition": "sunny", "size": 16 } } layout_manager.render_element(element, {}) mock_display_manager.draw_weather_icon.assert_called_once_with("sunny", 10, 20, 16) def test_render_element_weather_icon_from_context(self, layout_manager, mock_display_manager): """Test rendering weather icon with data from context.""" element = { "type": "weather_icon", "x": 10, "y": 20, "properties": {"size": 16} } data_context = { "weather": { "condition": "cloudy" } } layout_manager.render_element(element, data_context) mock_display_manager.draw_weather_icon.assert_called_once_with("cloudy", 10, 20, 16) def test_render_element_rectangle(self, layout_manager, mock_display_manager): """Test rendering a rectangle element.""" element = { "type": "rectangle", "x": 10, "y": 20, "properties": { "width": 50, "height": 30, "color": [255, 0, 0], "filled": True } } # Mock the draw object and rectangle method mock_draw = MagicMock() mock_display_manager.draw = mock_draw layout_manager.render_element(element, {}) # Verify rectangle was drawn mock_draw.rectangle.assert_called_once() def test_render_element_unknown_type(self, layout_manager): """Test rendering an unknown element type.""" element = { "type": "unknown_type", "x": 0, "y": 0, "properties": {} } # Should not raise an exception layout_manager.render_element(element, {}) def test_process_template_text(self, layout_manager): """Test template text processing.""" text = "Hello {name}, temperature is {temp}°F" data_context = { "name": "World", "temp": 72 } result = layout_manager._process_template_text(text, data_context) assert result == "Hello World, temperature is 72°F" def test_process_template_text_no_context(self, layout_manager): """Test template text with missing context.""" text = "Hello {name}" data_context = {} result = layout_manager._process_template_text(text, data_context) # Should leave template as-is or handle gracefully assert "{name}" in result or result == "Hello " def test_save_layouts_error_handling(self, layout_manager): """Test error handling when saving layouts.""" # Create a layout layout_manager.create_layout("test", []) # Make save fail by using invalid path layout_manager.config_path = "/nonexistent/directory/layouts.json" result = layout_manager.save_layouts() # Should handle error gracefully assert result is False def test_render_element_line(self, layout_manager, mock_display_manager): """Test rendering a line element.""" element = { "type": "line", "x": 10, "y": 20, "properties": { "x2": 50, "y2": 30, "color": [255, 0, 0], "width": 2 } } mock_draw = MagicMock() mock_display_manager.draw = mock_draw layout_manager.render_element(element, {}) mock_draw.line.assert_called_once() def test_render_element_clock(self, layout_manager, mock_display_manager): """Test rendering a clock element.""" element = { "type": "clock", "x": 10, "y": 20, "properties": { "format": "%H:%M", "color": [255, 255, 255] } } layout_manager.render_element(element, {}) mock_display_manager.draw_text.assert_called_once() def test_render_element_data_text(self, layout_manager, mock_display_manager): """Test rendering a data text element.""" element = { "type": "data_text", "x": 10, "y": 20, "properties": { "data_key": "weather.temperature", "format": "Temp: {value}°F", "color": [255, 255, 255], "default": "N/A" } } data_context = { "weather": { "temperature": 72 } } layout_manager.render_element(element, data_context) mock_display_manager.draw_text.assert_called_once()