pyWebLayout/tests/test_alignment_handlers.py
2025-06-08 15:11:35 +02:00

292 lines
13 KiB
Python

#!/usr/bin/env python3
"""
Unit tests for the alignment handler system.
Tests the various alignment handlers (Left, Center, Right, Justify) and their integration with Line objects.
"""
import unittest
import numpy as np
from unittest.mock import Mock
from pyWebLayout.concrete.text import Line, Text, LeftAlignmentHandler, CenterRightAlignmentHandler, JustifyAlignmentHandler
from pyWebLayout.style.layout import Alignment
from pyWebLayout.style import Font
class TestAlignmentHandlers(unittest.TestCase):
"""Test cases for the alignment handler system"""
def setUp(self):
"""Set up test fixtures"""
self.font = Font()
self.test_words = ["This", "is", "a", "test", "sentence"]
self.line_width = 300
self.line_height = 30
self.spacing = (5, 20) # min_spacing, max_spacing
self.origin = (0, 0)
self.size = (self.line_width, self.line_height)
def test_left_alignment_handler_assignment(self):
"""Test that Line correctly assigns LeftAlignmentHandler for LEFT alignment"""
left_line = Line(self.spacing, self.origin, self.size, self.font, halign=Alignment.LEFT)
self.assertIsInstance(left_line._alignment_handler, LeftAlignmentHandler)
def test_center_alignment_handler_assignment(self):
"""Test that Line correctly assigns CenterRightAlignmentHandler for CENTER alignment"""
center_line = Line(self.spacing, self.origin, self.size, self.font, halign=Alignment.CENTER)
self.assertIsInstance(center_line._alignment_handler, CenterRightAlignmentHandler)
# Check that it's configured for CENTER alignment
self.assertEqual(center_line._alignment_handler._alignment, Alignment.CENTER)
def test_right_alignment_handler_assignment(self):
"""Test that Line correctly assigns CenterRightAlignmentHandler for RIGHT alignment"""
right_line = Line(self.spacing, self.origin, self.size, self.font, halign=Alignment.RIGHT)
self.assertIsInstance(right_line._alignment_handler, CenterRightAlignmentHandler)
# Check that it's configured for RIGHT alignment
self.assertEqual(right_line._alignment_handler._alignment, Alignment.RIGHT)
def test_justify_alignment_handler_assignment(self):
"""Test that Line correctly assigns JustifyAlignmentHandler for JUSTIFY alignment"""
justify_line = Line(self.spacing, self.origin, self.size, self.font, halign=Alignment.JUSTIFY)
self.assertIsInstance(justify_line._alignment_handler, JustifyAlignmentHandler)
def test_left_alignment_word_addition(self):
"""Test adding words to a left-aligned line"""
left_line = Line(self.spacing, self.origin, self.size, self.font, halign=Alignment.LEFT)
# Add words until line is full or we run out
words_added = 0
for word in self.test_words:
result = left_line.add_word(word)
if result:
# Word didn't fit, should return the word
self.assertEqual(result, word)
break
else:
words_added += 1
# Should have added at least some words
self.assertGreater(words_added, 0)
self.assertEqual(len(left_line.text_objects), words_added)
def test_center_alignment_word_addition(self):
"""Test adding words to a center-aligned line"""
center_line = Line(self.spacing, self.origin, self.size, self.font, halign=Alignment.CENTER)
# Add words until line is full or we run out
words_added = 0
for word in self.test_words:
result = center_line.add_word(word)
if result:
# Word didn't fit, should return the word
self.assertEqual(result, word)
break
else:
words_added += 1
# Should have added at least some words
self.assertGreater(words_added, 0)
self.assertEqual(len(center_line.text_objects), words_added)
def test_right_alignment_word_addition(self):
"""Test adding words to a right-aligned line"""
right_line = Line(self.spacing, self.origin, self.size, self.font, halign=Alignment.RIGHT)
# Add words until line is full or we run out
words_added = 0
for word in self.test_words:
result = right_line.add_word(word)
if result:
# Word didn't fit, should return the word
self.assertEqual(result, word)
break
else:
words_added += 1
# Should have added at least some words
self.assertGreater(words_added, 0)
self.assertEqual(len(right_line.text_objects), words_added)
def test_justify_alignment_word_addition(self):
"""Test adding words to a justified line"""
justify_line = Line(self.spacing, self.origin, self.size, self.font, halign=Alignment.JUSTIFY)
# Add words until line is full or we run out
words_added = 0
for word in self.test_words:
result = justify_line.add_word(word)
if result:
# Word didn't fit, should return the word
self.assertEqual(result, word)
break
else:
words_added += 1
# Should have added at least some words
self.assertGreater(words_added, 0)
self.assertEqual(len(justify_line.text_objects), words_added)
def test_handler_spacing_and_position_calculations(self):
"""Test spacing and position calculations for different alignment handlers"""
# Create sample text objects
text_objects = [Text(word, self.font) for word in ["Hello", "World"]]
# Test each handler type
handlers = [
("Left", LeftAlignmentHandler()),
("Center", CenterRightAlignmentHandler(Alignment.CENTER)),
("Right", CenterRightAlignmentHandler(Alignment.RIGHT)),
("Justify", JustifyAlignmentHandler())
]
for name, handler in handlers:
with self.subTest(handler=name):
spacing_calc, position = handler.calculate_spacing_and_position(
text_objects, self.line_width, self.spacing[0], self.spacing[1])
# Check that spacing is a valid number
self.assertIsInstance(spacing_calc, (int, float))
self.assertGreaterEqual(spacing_calc, 0)
# Check that position is a valid number
self.assertIsInstance(position, (int, float))
self.assertGreaterEqual(position, 0)
# Position should be within line width
self.assertLessEqual(position, self.line_width)
def test_left_handler_spacing_calculation(self):
"""Test specific spacing calculation for left alignment"""
handler = LeftAlignmentHandler()
text_objects = [Text(word, self.font) for word in ["Hello", "World"]]
spacing_calc, position = handler.calculate_spacing_and_position(
text_objects, self.line_width, self.spacing[0], self.spacing[1])
# Left alignment should have position at 0
self.assertEqual(position, 0)
# Spacing should be minimum spacing for left alignment
self.assertEqual(spacing_calc, self.spacing[0])
def test_center_handler_spacing_calculation(self):
"""Test specific spacing calculation for center alignment"""
handler = CenterRightAlignmentHandler(Alignment.CENTER)
text_objects = [Text(word, self.font) for word in ["Hello", "World"]]
spacing_calc, position = handler.calculate_spacing_and_position(
text_objects, self.line_width, self.spacing[0], self.spacing[1])
# Center alignment should have position > 0 (centered)
self.assertGreater(position, 0)
# Spacing should be minimum spacing for center alignment
self.assertEqual(spacing_calc, self.spacing[0])
def test_right_handler_spacing_calculation(self):
"""Test specific spacing calculation for right alignment"""
handler = CenterRightAlignmentHandler(Alignment.RIGHT)
text_objects = [Text(word, self.font) for word in ["Hello", "World"]]
spacing_calc, position = handler.calculate_spacing_and_position(
text_objects, self.line_width, self.spacing[0], self.spacing[1])
# Right alignment should have position at the right edge minus content width
self.assertGreater(position, 0)
# Spacing should be minimum spacing for right alignment
self.assertEqual(spacing_calc, self.spacing[0])
def test_justify_handler_spacing_calculation(self):
"""Test specific spacing calculation for justify alignment"""
handler = JustifyAlignmentHandler()
text_objects = [Text(word, self.font) for word in ["Hello", "World"]]
spacing_calc, position = handler.calculate_spacing_and_position(
text_objects, self.line_width, self.spacing[0], self.spacing[1])
# Justify alignment should have position at 0
self.assertEqual(position, 0)
# Spacing should be calculated to fill the line (between min and max)
self.assertGreaterEqual(spacing_calc, self.spacing[0])
self.assertLessEqual(spacing_calc, self.spacing[1])
def test_hyphenation_decisions(self):
"""Test hyphenation decisions for different alignment handlers"""
text_objects = [Text(word, self.font) for word in ["Hello", "World"]]
test_word_width = 50
available_width = 40 # Word doesn't fit
handlers = [
("Left", LeftAlignmentHandler()),
("Center", CenterRightAlignmentHandler(Alignment.CENTER)),
("Right", CenterRightAlignmentHandler(Alignment.RIGHT)),
("Justify", JustifyAlignmentHandler())
]
for name, handler in handlers:
with self.subTest(handler=name):
should_hyphenate = handler.should_try_hyphenation(
text_objects, test_word_width, available_width, self.spacing[0], self.font)
# Should return a boolean
self.assertIsInstance(should_hyphenate, bool)
def test_hyphenation_decision_logic(self):
"""Test specific hyphenation decision logic"""
text_objects = [Text(word, self.font) for word in ["Hello"]]
# Test with word that doesn't fit
handler = LeftAlignmentHandler()
should_hyphenate_large = handler.should_try_hyphenation(
text_objects, 100, 50, self.spacing[0], self.font)
# Test with word that fits
should_hyphenate_small = handler.should_try_hyphenation(
text_objects, 30, 50, self.spacing[0], self.font)
# Large word should suggest hyphenation, small word should not
self.assertIsInstance(should_hyphenate_large, bool)
self.assertIsInstance(should_hyphenate_small, bool)
def test_empty_line_alignment_handlers(self):
"""Test alignment handlers with empty lines"""
alignments = [Alignment.LEFT, Alignment.CENTER, Alignment.RIGHT, Alignment.JUSTIFY]
for alignment in alignments:
with self.subTest(alignment=alignment):
line = Line(self.spacing, self.origin, self.size, self.font, halign=alignment)
# Empty line should still have a handler
self.assertIsNotNone(line._alignment_handler)
# Should be able to render empty line
result = line.render()
self.assertIsNotNone(result)
def test_single_word_line_alignment(self):
"""Test alignment handlers with single word lines"""
alignments = [Alignment.LEFT, Alignment.CENTER, Alignment.RIGHT, Alignment.JUSTIFY]
for alignment in alignments:
with self.subTest(alignment=alignment):
line = Line(self.spacing, self.origin, self.size, self.font, halign=alignment)
# Add a single word
result = line.add_word("test")
self.assertIsNone(result) # Should fit
# Should be able to render single word line
rendered = line.render()
self.assertIsNotNone(rendered)
self.assertEqual(len(line.text_objects), 1)
if __name__ == '__main__':
unittest.main()