605 lines
26 KiB
Python
605 lines
26 KiB
Python
"""
|
|
Test file for document layouter functionality.
|
|
|
|
This test focuses on verifying that the document layouter properly
|
|
integrates word spacing constraints from the style system.
|
|
"""
|
|
|
|
import pytest
|
|
from unittest.mock import Mock, MagicMock, patch
|
|
from typing import List, Optional
|
|
|
|
from pyWebLayout.layout.document_layouter import paragraph_layouter, DocumentLayouter
|
|
from pyWebLayout.style.abstract_style import AbstractStyle
|
|
from pyWebLayout.style.concrete_style import ConcreteStyle, StyleResolver, RenderingContext
|
|
|
|
|
|
class TestDocumentLayouter:
|
|
"""Test cases for document layouter functionality."""
|
|
|
|
def setup_method(self):
|
|
"""Set up test fixtures before each test method."""
|
|
# Create mock objects
|
|
self.mock_page = Mock()
|
|
self.mock_page.border_size = 20
|
|
self.mock_page._current_y_offset = 50
|
|
self.mock_page.available_width = 400
|
|
self.mock_page.draw = Mock()
|
|
self.mock_page.can_fit_line = Mock(return_value=True)
|
|
self.mock_page.add_child = Mock()
|
|
|
|
# Create mock page style with all required numeric properties
|
|
self.mock_page.style = Mock()
|
|
self.mock_page.style.max_font_size = 72 # Reasonable maximum font size
|
|
self.mock_page.style.line_spacing_multiplier = 1.2 # Standard line spacing
|
|
|
|
# Create mock style resolver
|
|
self.mock_style_resolver = Mock()
|
|
self.mock_page.style_resolver = self.mock_style_resolver
|
|
|
|
# Create mock paragraph
|
|
self.mock_paragraph = Mock()
|
|
self.mock_paragraph.line_height = 20
|
|
self.mock_paragraph.style = AbstractStyle()
|
|
|
|
# Create mock words
|
|
self.mock_words = []
|
|
for i in range(5):
|
|
word = Mock()
|
|
word.text = f"word{i}"
|
|
self.mock_words.append(word)
|
|
self.mock_paragraph.words = self.mock_words
|
|
|
|
# Create mock concrete style with word spacing constraints
|
|
self.mock_concrete_style = Mock()
|
|
self.mock_concrete_style.word_spacing_min = 2.0
|
|
self.mock_concrete_style.word_spacing_max = 8.0
|
|
self.mock_concrete_style.text_align = "left"
|
|
|
|
# Create mock font that returns proper numeric metrics (not Mock objects)
|
|
mock_font = Mock()
|
|
# CRITICAL: getmetrics() must return actual numeric values, not Mock objects
|
|
# This prevents "TypeError: '>' not supported between instances of 'Mock' and 'Mock'"
|
|
mock_font.getmetrics.return_value = (12, 4) # (ascent, descent) as actual integers
|
|
mock_font.font = mock_font # For accessing .font property
|
|
|
|
# Create mock font object that can be used by create_font
|
|
mock_font_instance = Mock()
|
|
mock_font_instance.font = mock_font
|
|
mock_font_instance.font_size = 16
|
|
mock_font_instance.colour = (0, 0, 0)
|
|
mock_font_instance.background = (255, 255, 255, 0)
|
|
self.mock_concrete_style.create_font = Mock(return_value=mock_font_instance)
|
|
|
|
# Update mock words to have proper style with font
|
|
for word in self.mock_words:
|
|
word.style = Mock()
|
|
word.style.font = mock_font
|
|
word.style.font_size = 16
|
|
word.style.colour = (0, 0, 0)
|
|
word.style.background = None
|
|
|
|
@patch('pyWebLayout.layout.document_layouter.StyleResolver')
|
|
@patch('pyWebLayout.layout.document_layouter.ConcreteStyleRegistry')
|
|
@patch('pyWebLayout.layout.document_layouter.Line')
|
|
def test_paragraph_layouter_basic_flow(self, mock_line_class, mock_style_registry_class, mock_style_resolver_class):
|
|
"""Test basic paragraph layouter functionality."""
|
|
# Setup mocks for StyleResolver and ConcreteStyleRegistry
|
|
mock_style_resolver = Mock()
|
|
mock_style_resolver_class.return_value = mock_style_resolver
|
|
|
|
mock_style_registry = Mock()
|
|
mock_style_registry_class.return_value = mock_style_registry
|
|
mock_style_registry.get_concrete_style.return_value = self.mock_concrete_style
|
|
|
|
mock_line = Mock()
|
|
mock_line_class.return_value = mock_line
|
|
mock_line.add_word.return_value = (True, None) # All words fit successfully
|
|
|
|
# Call function
|
|
result = paragraph_layouter(self.mock_paragraph, self.mock_page)
|
|
|
|
# Verify results
|
|
success, failed_word_index, remaining_pretext = result
|
|
assert success is True
|
|
assert failed_word_index is None
|
|
assert remaining_pretext is None
|
|
|
|
# Verify StyleResolver and ConcreteStyleRegistry were created correctly
|
|
mock_style_resolver_class.assert_called_once()
|
|
mock_style_registry_class.assert_called_once_with(mock_style_resolver)
|
|
mock_style_registry.get_concrete_style.assert_called_once_with(self.mock_paragraph.style)
|
|
|
|
# Verify Line was created with correct spacing constraints
|
|
expected_spacing = (2, 8) # From mock_concrete_style
|
|
mock_line_class.assert_called_once()
|
|
call_args = mock_line_class.call_args
|
|
assert call_args[1]['spacing'] == expected_spacing
|
|
|
|
@patch('pyWebLayout.layout.document_layouter.StyleResolver')
|
|
@patch('pyWebLayout.layout.document_layouter.ConcreteStyleRegistry')
|
|
@patch('pyWebLayout.layout.document_layouter.Line')
|
|
def test_paragraph_layouter_word_spacing_constraints_extraction(self, mock_line_class, mock_style_registry_class, mock_style_resolver_class):
|
|
"""Test that word spacing constraints are correctly extracted from style."""
|
|
# Create concrete style with specific constraints
|
|
concrete_style = Mock()
|
|
concrete_style.word_spacing_min = 5.5
|
|
concrete_style.word_spacing_max = 15.2
|
|
concrete_style.text_align = "justify"
|
|
|
|
# Create a mock font that concrete_style.create_font returns
|
|
mock_font = Mock()
|
|
mock_font.font = Mock()
|
|
mock_font.font.getmetrics.return_value = (12, 4)
|
|
mock_font.font_size = 16
|
|
concrete_style.create_font = Mock(return_value=mock_font)
|
|
|
|
# Setup StyleResolver and ConcreteStyleRegistry mocks
|
|
mock_style_resolver = Mock()
|
|
mock_style_resolver_class.return_value = mock_style_resolver
|
|
|
|
mock_style_registry = Mock()
|
|
mock_style_registry_class.return_value = mock_style_registry
|
|
mock_style_registry.get_concrete_style.return_value = concrete_style
|
|
|
|
mock_line = Mock()
|
|
mock_line_class.return_value = mock_line
|
|
mock_line.add_word.return_value = (True, None)
|
|
|
|
# Call function
|
|
paragraph_layouter(self.mock_paragraph, self.mock_page)
|
|
|
|
# Verify spacing constraints were extracted correctly (converted to int)
|
|
expected_spacing = (5, 15) # int() conversion of 5.5 and 15.2
|
|
call_args = mock_line_class.call_args
|
|
assert call_args[1]['spacing'] == expected_spacing
|
|
|
|
@patch('pyWebLayout.layout.document_layouter.ConcreteStyleRegistry')
|
|
@patch('pyWebLayout.layout.document_layouter.Line')
|
|
@patch('pyWebLayout.layout.document_layouter.Text')
|
|
def test_paragraph_layouter_line_overflow(self, mock_text_class, mock_line_class, mock_style_registry_class):
|
|
"""Test handling of line overflow when words don't fit."""
|
|
# Setup mocks
|
|
mock_style_registry = Mock()
|
|
mock_style_registry_class.return_value = mock_style_registry
|
|
mock_style_registry.get_concrete_style.return_value = self.mock_concrete_style
|
|
|
|
# Create two mock lines with proper size attribute
|
|
mock_line1 = Mock()
|
|
mock_line1.size = (400, 20) # (width, height)
|
|
mock_line2 = Mock()
|
|
mock_line2.size = (400, 20) # (width, height)
|
|
mock_line_class.side_effect = [mock_line1, mock_line2]
|
|
|
|
# Mock Text.from_word to return mock text objects with numeric width
|
|
mock_text = Mock()
|
|
mock_text.width = 50 # Reasonable word width
|
|
mock_text_class.from_word.return_value = mock_text
|
|
|
|
# First line: first 2 words fit, third doesn't
|
|
# Second line: remaining words fit
|
|
mock_line1.add_word.side_effect = [
|
|
(True, None), # word0 fits
|
|
(True, None), # word1 fits
|
|
(False, None), # word2 doesn't fit
|
|
]
|
|
mock_line2.add_word.side_effect = [
|
|
(True, None), # word2 fits on new line
|
|
(True, None), # word3 fits
|
|
(True, None), # word4 fits
|
|
]
|
|
|
|
# Call function
|
|
result = paragraph_layouter(self.mock_paragraph, self.mock_page)
|
|
|
|
# Verify results
|
|
success, failed_word_index, remaining_pretext = result
|
|
assert success is True
|
|
assert failed_word_index is None
|
|
assert remaining_pretext is None
|
|
|
|
# Verify two lines were created
|
|
assert mock_line_class.call_count == 2
|
|
assert self.mock_page.add_child.call_count == 2
|
|
|
|
@patch('pyWebLayout.layout.document_layouter.ConcreteStyleRegistry')
|
|
@patch('pyWebLayout.layout.document_layouter.Line')
|
|
def test_paragraph_layouter_page_full(self, mock_line_class, mock_style_registry_class):
|
|
"""Test handling when page runs out of space."""
|
|
# Setup mocks
|
|
mock_style_registry = Mock()
|
|
mock_style_registry_class.return_value = mock_style_registry
|
|
mock_style_registry.get_concrete_style.return_value = self.mock_concrete_style
|
|
|
|
# Page can fit first line but not second
|
|
self.mock_page.can_fit_line.side_effect = [True, False]
|
|
|
|
mock_line = Mock()
|
|
mock_line_class.return_value = mock_line
|
|
mock_line.add_word.side_effect = [
|
|
(True, None), # word0 fits
|
|
(False, None), # word1 doesn't fit, need new line
|
|
]
|
|
|
|
# Call function
|
|
result = paragraph_layouter(self.mock_paragraph, self.mock_page)
|
|
|
|
# Verify results indicate page is full
|
|
success, failed_word_index, remaining_pretext = result
|
|
assert success is False
|
|
assert failed_word_index == 1 # word1 didn't fit
|
|
assert remaining_pretext is None
|
|
|
|
def test_paragraph_layouter_empty_paragraph(self):
|
|
"""Test handling of empty paragraph."""
|
|
empty_paragraph = Mock()
|
|
empty_paragraph.words = []
|
|
|
|
result = paragraph_layouter(empty_paragraph, self.mock_page)
|
|
|
|
success, failed_word_index, remaining_pretext = result
|
|
assert success is True
|
|
assert failed_word_index is None
|
|
assert remaining_pretext is None
|
|
|
|
def test_paragraph_layouter_invalid_start_word(self):
|
|
"""Test handling of invalid start_word index."""
|
|
result = paragraph_layouter(self.mock_paragraph, self.mock_page, start_word=10)
|
|
|
|
success, failed_word_index, remaining_pretext = result
|
|
assert success is True
|
|
assert failed_word_index is None
|
|
assert remaining_pretext is None
|
|
|
|
@patch('pyWebLayout.layout.document_layouter.ConcreteStyleRegistry')
|
|
def test_document_layouter_class(self, mock_style_registry_class):
|
|
"""Test DocumentLayouter class functionality."""
|
|
# Setup mock
|
|
mock_style_registry = Mock()
|
|
mock_style_registry_class.return_value = mock_style_registry
|
|
|
|
# Create layouter
|
|
layouter = DocumentLayouter(self.mock_page)
|
|
|
|
# Verify initialization
|
|
assert layouter.page == self.mock_page
|
|
mock_style_registry_class.assert_called_once_with(self.mock_page.style_resolver)
|
|
|
|
@patch('pyWebLayout.layout.document_layouter.paragraph_layouter')
|
|
def test_document_layouter_layout_paragraph(self, mock_paragraph_layouter):
|
|
"""Test DocumentLayouter.layout_paragraph method."""
|
|
mock_paragraph_layouter.return_value = (True, None, None)
|
|
|
|
with patch('pyWebLayout.layout.document_layouter.ConcreteStyleRegistry'):
|
|
layouter = DocumentLayouter(self.mock_page)
|
|
|
|
result = layouter.layout_paragraph(self.mock_paragraph, start_word=2, pretext="test")
|
|
|
|
# Verify the function was called correctly
|
|
mock_paragraph_layouter.assert_called_once_with(
|
|
self.mock_paragraph, self.mock_page, 2, "test"
|
|
)
|
|
assert result == (True, None, None)
|
|
|
|
def test_document_layouter_layout_document_success(self):
|
|
"""Test DocumentLayouter.layout_document with successful layout."""
|
|
from pyWebLayout.abstract import Paragraph
|
|
|
|
# Create Mock paragraphs that pass isinstance checks
|
|
paragraphs = [
|
|
Mock(spec=Paragraph),
|
|
Mock(spec=Paragraph),
|
|
Mock(spec=Paragraph)
|
|
]
|
|
|
|
with patch('pyWebLayout.layout.document_layouter.ConcreteStyleRegistry'):
|
|
layouter = DocumentLayouter(self.mock_page)
|
|
|
|
# Mock the layout_paragraph method to return success
|
|
layouter.layout_paragraph = Mock(return_value=(True, None, None))
|
|
|
|
result = layouter.layout_document(paragraphs)
|
|
|
|
assert result is True
|
|
assert layouter.layout_paragraph.call_count == 3
|
|
|
|
def test_document_layouter_layout_document_failure(self):
|
|
"""Test DocumentLayouter.layout_document with layout failure."""
|
|
from pyWebLayout.abstract import Paragraph
|
|
|
|
# Create Mock paragraphs that pass isinstance checks
|
|
paragraphs = [
|
|
Mock(spec=Paragraph),
|
|
Mock(spec=Paragraph)
|
|
]
|
|
|
|
with patch('pyWebLayout.layout.document_layouter.ConcreteStyleRegistry'):
|
|
layouter = DocumentLayouter(self.mock_page)
|
|
|
|
# Mock the layout_paragraph method: first succeeds, second fails
|
|
layouter.layout_paragraph = Mock(side_effect=[
|
|
(True, None, None), # First paragraph succeeds
|
|
(False, 3, None), # Second paragraph fails
|
|
])
|
|
|
|
result = layouter.layout_document(paragraphs)
|
|
|
|
assert result is False
|
|
assert layouter.layout_paragraph.call_count == 2
|
|
|
|
def test_real_style_integration(self):
|
|
"""Test integration with real style system."""
|
|
# Create real style objects
|
|
context = RenderingContext(base_font_size=16)
|
|
resolver = StyleResolver(context)
|
|
|
|
abstract_style = AbstractStyle(
|
|
word_spacing=4.0,
|
|
word_spacing_min=2.0,
|
|
word_spacing_max=10.0
|
|
)
|
|
|
|
concrete_style = resolver.resolve_style(abstract_style)
|
|
|
|
# Verify constraints are resolved correctly
|
|
assert concrete_style.word_spacing_min == 2.0
|
|
assert concrete_style.word_spacing_max == 10.0
|
|
|
|
# This demonstrates the integration works end-to-end
|
|
|
|
|
|
class TestWordSpacingConstraintsInLayout:
|
|
"""Specific tests for word spacing constraints in layout context."""
|
|
|
|
def test_different_spacing_scenarios(self):
|
|
"""Test various word spacing constraint scenarios."""
|
|
context = RenderingContext(base_font_size=16)
|
|
resolver = StyleResolver(context)
|
|
|
|
test_cases = [
|
|
# (word_spacing, word_spacing_min, word_spacing_max, expected_min, expected_max)
|
|
(None, None, None, 2.0, 8.0), # Default case
|
|
(5.0, None, None, 5.0, 10.0), # Only base specified
|
|
(4.0, 2.0, 8.0, 2.0, 8.0), # All specified
|
|
(3.0, 1.0, None, 1.0, 3.0), # Min specified, max = max(word_spacing, min*2) = max(3.0, 2.0) = 3.0
|
|
(6.0, None, 12.0, 6.0, 12.0), # Max specified, min from base
|
|
]
|
|
|
|
for word_spacing, min_spacing, max_spacing, expected_min, expected_max in test_cases:
|
|
style_kwargs = {}
|
|
if word_spacing is not None:
|
|
style_kwargs['word_spacing'] = word_spacing
|
|
if min_spacing is not None:
|
|
style_kwargs['word_spacing_min'] = min_spacing
|
|
if max_spacing is not None:
|
|
style_kwargs['word_spacing_max'] = max_spacing
|
|
|
|
abstract_style = AbstractStyle(**style_kwargs)
|
|
concrete_style = resolver.resolve_style(abstract_style)
|
|
|
|
assert concrete_style.word_spacing_min == expected_min, f"Failed for case: {style_kwargs}"
|
|
assert concrete_style.word_spacing_max == expected_max, f"Failed for case: {style_kwargs}"
|
|
|
|
|
|
class TestMultiPageLayout:
|
|
"""Test cases for multi-page document layout scenarios."""
|
|
|
|
def setup_method(self):
|
|
"""Set up test fixtures for multi-page tests."""
|
|
# Create multiple mock pages
|
|
self.mock_pages = []
|
|
for i in range(3):
|
|
page = Mock()
|
|
page.page_number = i + 1
|
|
page.border_size = 20
|
|
page._current_y_offset = 50
|
|
page.available_width = 400
|
|
page.available_height = 600
|
|
page.draw = Mock()
|
|
page.add_child = Mock()
|
|
page.style_resolver = Mock()
|
|
self.mock_pages.append(page)
|
|
|
|
# Create a long paragraph that will span multiple pages
|
|
self.long_paragraph = Mock()
|
|
self.long_paragraph.line_height = 25
|
|
self.long_paragraph.style = AbstractStyle()
|
|
|
|
# Create many words to ensure page overflow
|
|
self.long_paragraph.words = []
|
|
for i in range(50): # 50 words should definitely overflow a page
|
|
word = Mock()
|
|
word.text = f"word_{i:02d}"
|
|
self.long_paragraph.words.append(word)
|
|
|
|
# Create mock concrete style
|
|
self.mock_concrete_style = Mock()
|
|
self.mock_concrete_style.word_spacing_min = 3.0
|
|
self.mock_concrete_style.word_spacing_max = 12.0
|
|
self.mock_concrete_style.text_align = "justify"
|
|
self.mock_concrete_style.create_font = Mock()
|
|
|
|
|
|
@patch('pyWebLayout.layout.document_layouter.ConcreteStyleRegistry')
|
|
def test_document_layouter_multi_page_scenario(self, mock_style_registry_class):
|
|
"""Test DocumentLayouter handling multiple pages with continuation."""
|
|
# Setup style registry
|
|
mock_style_registry = Mock()
|
|
mock_style_registry_class.return_value = mock_style_registry
|
|
mock_style_registry.get_concrete_style.return_value = self.mock_concrete_style
|
|
|
|
# Create a multi-page document layouter
|
|
class MultiPageDocumentLayouter(DocumentLayouter):
|
|
def __init__(self, pages):
|
|
self.pages = pages
|
|
self.current_page_index = 0
|
|
self.page = pages[0]
|
|
self.style_registry = Mock()
|
|
|
|
def get_next_page(self):
|
|
"""Get the next available page."""
|
|
if self.current_page_index + 1 < len(self.pages):
|
|
self.current_page_index += 1
|
|
self.page = self.pages[self.current_page_index]
|
|
return self.page
|
|
return None
|
|
|
|
def layout_document_with_pagination(self, paragraphs):
|
|
"""Layout document with automatic pagination."""
|
|
for paragraph in paragraphs:
|
|
start_word = 0
|
|
pretext = None
|
|
|
|
while start_word < len(paragraph.words):
|
|
complete, next_word, remaining_pretext = self.layout_paragraph(
|
|
paragraph, start_word, pretext
|
|
)
|
|
|
|
if complete:
|
|
# Paragraph finished
|
|
break
|
|
|
|
if next_word is None:
|
|
# Error condition
|
|
return False, f"Failed to layout paragraph at word {start_word}"
|
|
|
|
# Try to get next page
|
|
next_page = self.get_next_page()
|
|
if not next_page:
|
|
return False, f"Ran out of pages at word {next_word}"
|
|
|
|
# Continue with remaining words on next page
|
|
start_word = next_word
|
|
pretext = remaining_pretext
|
|
|
|
return True, "All paragraphs laid out successfully"
|
|
|
|
# Create layouter with multiple pages
|
|
layouter = MultiPageDocumentLayouter(self.mock_pages)
|
|
|
|
# Mock the layout_paragraph method to simulate page filling
|
|
original_layout_paragraph = layouter.layout_paragraph
|
|
call_count = [0]
|
|
|
|
def mock_layout_paragraph(paragraph, start_word=0, pretext=None):
|
|
call_count[0] += 1
|
|
|
|
# Simulate different scenarios based on call count
|
|
if call_count[0] == 1:
|
|
# First page: can fit words 0-19, fails at word 20
|
|
return (False, 20, None)
|
|
elif call_count[0] == 2:
|
|
# Second page: can fit words 20-39, fails at word 40
|
|
return (False, 40, None)
|
|
elif call_count[0] == 3:
|
|
# Third page: can fit remaining words 40-49
|
|
return (True, None, None)
|
|
else:
|
|
return (False, start_word, None)
|
|
|
|
layouter.layout_paragraph = mock_layout_paragraph
|
|
|
|
# Test multi-page layout
|
|
success, message = layouter.layout_document_with_pagination([self.long_paragraph])
|
|
|
|
# Verify results
|
|
assert success is True
|
|
assert "successfully" in message
|
|
assert call_count[0] == 3 # Should have made 3 layout attempts
|
|
assert layouter.current_page_index == 2 # Should end on page 3 (index 2)
|
|
|
|
|
|
def test_realistic_multi_page_scenario(self):
|
|
"""Test a realistic scenario with actual content and page constraints."""
|
|
# Create realistic paragraph with varied content
|
|
realistic_paragraph = Mock()
|
|
realistic_paragraph.line_height = 20
|
|
realistic_paragraph.style = AbstractStyle(
|
|
word_spacing=4.0,
|
|
word_spacing_min=2.0,
|
|
word_spacing_max=8.0,
|
|
text_align="justify"
|
|
)
|
|
|
|
# Create words of varying lengths (realistic text)
|
|
words = [
|
|
"The", "quick", "brown", "fox", "jumps", "over", "the", "lazy", "dog.",
|
|
"This", "sentence", "contains", "words", "of", "varying", "lengths",
|
|
"to", "simulate", "realistic", "text", "content", "that", "would",
|
|
"require", "proper", "word", "spacing", "calculations", "and",
|
|
"potentially", "multiple", "pages", "for", "layout.", "Each",
|
|
"word", "represents", "a", "challenge", "for", "the", "layouter",
|
|
"system", "to", "handle", "appropriately", "with", "the", "given",
|
|
"constraints", "and", "spacing", "requirements."
|
|
]
|
|
|
|
realistic_paragraph.words = []
|
|
for word_text in words:
|
|
word = Mock()
|
|
word.text = word_text
|
|
realistic_paragraph.words.append(word)
|
|
|
|
# Create page with realistic constraints
|
|
realistic_page = Mock()
|
|
realistic_page.border_size = 30
|
|
realistic_page._current_y_offset = 100
|
|
realistic_page.available_width = 350 # Narrower page
|
|
realistic_page.available_height = 500
|
|
realistic_page.draw = Mock()
|
|
realistic_page.add_child = Mock()
|
|
realistic_page.style_resolver = Mock()
|
|
|
|
# Simulate page that can fit approximately 20 lines
|
|
lines_fitted = [0]
|
|
max_lines = 20
|
|
|
|
def realistic_can_fit_line(line_height):
|
|
lines_fitted[0] += 1
|
|
return lines_fitted[0] <= max_lines
|
|
|
|
realistic_page.can_fit_line = realistic_can_fit_line
|
|
|
|
# Test with real style system
|
|
context = RenderingContext(base_font_size=14)
|
|
resolver = StyleResolver(context)
|
|
concrete_style = resolver.resolve_style(realistic_paragraph.style)
|
|
|
|
# Verify realistic constraints were calculated
|
|
assert concrete_style.word_spacing == 4.0
|
|
assert concrete_style.word_spacing_min == 2.0
|
|
assert concrete_style.word_spacing_max == 8.0
|
|
|
|
# This test demonstrates the integration without mocking everything
|
|
# In a real scenario, this would interface with actual Line and Text objects
|
|
print(f"✓ Realistic scenario test completed")
|
|
print(f" - Words to layout: {len(realistic_paragraph.words)}")
|
|
print(f" - Page width: {realistic_page.available_width}px")
|
|
print(f" - Word spacing constraints: {concrete_style.word_spacing_min}-{concrete_style.word_spacing_max}px")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
# Run specific tests for debugging
|
|
test = TestDocumentLayouter()
|
|
test.setup_method()
|
|
|
|
# Run a simple test
|
|
with patch('pyWebLayout.layout.document_layouter.ConcreteStyleRegistry') as mock_registry:
|
|
with patch('pyWebLayout.layout.document_layouter.Line') as mock_line:
|
|
mock_style_registry = Mock()
|
|
mock_registry.return_value = mock_style_registry
|
|
mock_style_registry.get_concrete_style.return_value = test.mock_concrete_style
|
|
|
|
mock_line_instance = Mock()
|
|
mock_line.return_value = mock_line_instance
|
|
mock_line_instance.add_word.return_value = (True, None)
|
|
|
|
result = paragraph_layouter(test.mock_paragraph, test.mock_page)
|
|
print(f"Test result: {result}")
|
|
|
|
# Run multi-page tests
|
|
multi_test = TestMultiPageLayout()
|
|
multi_test.setup_method()
|
|
multi_test.test_realistic_multi_page_scenario()
|
|
|
|
print("Document layouter tests completed!")
|