pyWebLayout/tests/concrete/test_dynamic_page.py
Duncan Tourolle 3bcd1bffb5
All checks were successful
Python CI / test (3.10) (push) Successful in 2m22s
Python CI / test (3.12) (push) Successful in 2m13s
Python CI / test (3.13) (push) Successful in 2m9s
Tables now use ""dynamic page" allowing the contents to be anything that can be rendered ion a page.
2025-11-11 18:10:47 +01:00

314 lines
9.0 KiB
Python

"""
Unit tests for DynamicPage class.
"""
import pytest
from PIL import Image
from pyWebLayout.concrete.dynamic_page import DynamicPage, SizeConstraints
from pyWebLayout.concrete.text import Line, Text
from pyWebLayout.style.fonts import Font
from pyWebLayout.style.page_style import PageStyle
from pyWebLayout.style import Alignment
class TestSizeConstraints:
"""Test SizeConstraints dataclass."""
def test_default_constraints(self):
"""Test default constraint values."""
constraints = SizeConstraints()
assert constraints.min_width is None
assert constraints.max_width is None
assert constraints.min_height is None
assert constraints.max_height is None
def test_custom_constraints(self):
"""Test custom constraint values."""
constraints = SizeConstraints(
min_width=100,
max_width=500,
min_height=50,
max_height=1000
)
assert constraints.min_width == 100
assert constraints.max_width == 500
assert constraints.min_height == 50
assert constraints.max_height == 1000
class TestDynamicPage:
"""Test DynamicPage class."""
def test_initialization(self):
"""Test DynamicPage initialization."""
page = DynamicPage()
assert page.size == (0, 0) # Starts with zero size
assert not page._is_measured
assert not page._is_laid_out
assert page._render_offset == 0
assert page.constraints is not None
def test_initialization_with_constraints(self):
"""Test initialization with custom constraints."""
constraints = SizeConstraints(min_width=200, max_width=800)
page = DynamicPage(constraints=constraints)
assert page.constraints.min_width == 200
assert page.constraints.max_width == 800
def test_initialization_with_style(self):
"""Test initialization with custom style."""
style = PageStyle(border_width=2, padding=(10, 20, 10, 20))
page = DynamicPage(style=style)
assert page.style.border_width == 2
assert page.style.padding_top == 10
def test_measure_empty_page(self):
"""Test measuring an empty page."""
page = DynamicPage()
width, height = page.measure()
# Empty page should have minimal size (just padding/borders)
assert width > 0 # At least padding/borders
assert height > 0
assert page._is_measured
def test_measure_with_constraints(self):
"""Test measuring respects constraints."""
constraints = SizeConstraints(min_width=300, min_height=200)
page = DynamicPage(constraints=constraints)
width, height = page.measure()
assert width >= 300
assert height >= 200
def test_measure_caching(self):
"""Test that measurement is cached."""
page = DynamicPage()
# First measurement
size1 = page.measure()
# Second measurement should return cached value
size2 = page.measure()
assert size1 == size2
assert page._is_measured
def test_get_min_width(self):
"""Test get_min_width."""
page = DynamicPage()
min_width = page.get_min_width()
assert min_width > 0
assert isinstance(min_width, int)
def test_get_preferred_width(self):
"""Test get_preferred_width."""
page = DynamicPage()
pref_width = page.get_preferred_width()
assert pref_width > 0
assert isinstance(pref_width, int)
def test_measure_content_height(self):
"""Test measure_content_height."""
page = DynamicPage()
content_height = page.measure_content_height()
assert content_height > 0
assert isinstance(content_height, int)
def test_layout(self):
"""Test layout method."""
page = DynamicPage()
target_size = (400, 600)
page.layout(target_size)
assert page.size == target_size
assert page._is_laid_out
assert page._dirty # Should be marked for re-render
def test_render_without_layout(self):
"""Test rendering without explicit layout (auto-sizing)."""
page = DynamicPage()
image = page.render()
assert isinstance(image, Image.Image)
assert image.size[0] > 0
assert image.size[1] > 0
def test_render_with_layout(self):
"""Test rendering after explicit layout."""
page = DynamicPage()
page.layout((500, 700))
image = page.render()
assert isinstance(image, Image.Image)
assert image.size == (500, 700)
def test_add_child_invalidates_cache(self):
"""Test that adding a child invalidates measurement caches."""
page = DynamicPage()
# Measure to populate cache
page.measure()
assert page._is_measured
# Add a child (mock renderable)
class MockRenderable:
def __init__(self):
self.size = (100, 50)
self._origin = (0, 0)
@property
def origin(self):
return self._origin
def render(self):
pass
page.add_child(MockRenderable())
# Caches should be invalidated
assert not page._is_measured
assert page._intrinsic_size is None
def test_clear_children_invalidates_cache(self):
"""Test that clearing children invalidates caches."""
page = DynamicPage()
# Measure to populate cache
page.measure()
assert page._is_measured
# Clear children
page.clear_children()
# Caches should be invalidated
assert not page._is_measured
def test_pagination_reset(self):
"""Test pagination reset."""
page = DynamicPage()
page._render_offset = 100
page.reset_pagination()
assert page._render_offset == 0
def test_has_more_content_false(self):
"""Test has_more_content when all content is rendered."""
page = DynamicPage()
# Set render offset to total height
total_height = page.measure_content_height()
page._render_offset = total_height
assert not page.has_more_content()
def test_has_more_content_true(self):
"""Test has_more_content when content remains."""
page = DynamicPage()
# Offset is less than total
page._render_offset = 0
assert page.has_more_content()
def test_min_width_measurement(self):
"""Test min width measures longest word."""
page = DynamicPage()
# Min width should be at least padding/borders
min_width = page.get_min_width()
assert min_width > 0
def test_invalidate_caches(self):
"""Test cache invalidation."""
page = DynamicPage()
# Populate caches
page.measure()
page.get_min_width()
page.get_preferred_width()
page.measure_content_height()
assert page._is_measured
assert page._intrinsic_size is not None
assert page._min_width_cache is not None
assert page._preferred_width_cache is not None
assert page._content_height_cache is not None
# Invalidate
page.invalidate_caches()
assert not page._is_measured
assert page._intrinsic_size is None
assert page._min_width_cache is None
assert page._preferred_width_cache is None
assert page._content_height_cache is None
assert not page._is_laid_out
def test_measure_with_available_width(self):
"""Test measurement with available_width constraint."""
page = DynamicPage()
width, height = page.measure(available_width=300)
# Width should respect available_width
assert width <= 300
def test_constraints_override_available_width(self):
"""Test that constraints override available_width."""
constraints = SizeConstraints(min_width=400)
page = DynamicPage(constraints=constraints)
width, height = page.measure(available_width=300)
# Should use min_width constraint, not available_width
assert width >= 400
def test_render_partial_empty_page(self):
"""Test partial rendering on empty page."""
page = DynamicPage()
rendered = page.render_partial(available_height=100)
assert rendered >= 0
assert isinstance(rendered, int)
def test_method_chaining_add_child(self):
"""Test that add_child returns self for chaining."""
page = DynamicPage()
class MockRenderable:
def __init__(self):
self.size = (50, 50)
self._origin = (0, 0)
@property
def origin(self):
return self._origin
result = page.add_child(MockRenderable())
assert result is page
def test_method_chaining_clear_children(self):
"""Test that clear_children returns self for chaining."""
page = DynamicPage()
result = page.clear_children()
assert result is page
if __name__ == '__main__':
pytest.main([__file__, '-v'])