#!/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 import Alignment from pyWebLayout.style import Font from pyWebLayout.abstract import Word from PIL import Image, ImageFont, ImageDraw class TestAlignmentHandlers(unittest.TestCase): """Test cases for the alignment handler system""" def setUp(self): """Set up test fixtures""" self.font = Font() self.test_words = [Word(text, self.font) for text in ["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) # Create a real PIL image (canvas) for testing self.canvas = Image.new('RGB', (800, 600), color='white') # Create a real ImageDraw object self.draw = ImageDraw.Draw(self.canvas) # Create a real Font object self.style = Font() 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.draw, font=self.style, 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.draw, font=self.style, 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.draw, font=self.style, 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.draw, font=self.style, 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.draw, halign=Alignment.LEFT) # Add words until line is full or we run out words_added = 0 for word in self.test_words: result, part = left_line.add_word(word) if not result: # Word didn't fit 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.draw, font=self.style, halign=Alignment.CENTER) # Add words until line is full or we run out words_added = 0 for word in self.test_words: result, part = center_line.add_word(word) if not result: # Word didn't fit 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.draw, font=self.style, halign=Alignment.RIGHT) # Add words until line is full or we run out words_added = 0 for word in self.test_words: result, part = right_line.add_word(word) if not result: # Word didn't fit 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.draw, font=self.style, halign=Alignment.JUSTIFY) # Add words until line is full or we run out words_added = 0 for word in self.test_words: result, part = justify_line.add_word(word) if not result: # Word didn't fit 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.style, self.draw) 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, overflow = 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) # Check that overflow is a boolean self.assertIsInstance(overflow, bool) # Position should be within line width (unless overflow) if not overflow: 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.style, self.draw) for word in ["Hello", "World"]] spacing_calc, position, overflow = 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) # Should not overflow with reasonable text self.assertFalse(overflow) def test_center_handler_spacing_calculation(self): """Test specific spacing calculation for center alignment""" handler = CenterRightAlignmentHandler(Alignment.CENTER) text_objects = [Text(word, self.style, self.draw) for word in ["Hello", "World"]] spacing_calc, position, overflow = handler.calculate_spacing_and_position( text_objects, self.line_width, self.spacing[0], self.spacing[1]) # Center alignment should have position > 0 (centered) if no overflow if not overflow: self.assertGreaterEqual(position, 0) def test_right_handler_spacing_calculation(self): """Test specific spacing calculation for right alignment""" handler = CenterRightAlignmentHandler(Alignment.RIGHT) text_objects = [Text(word, self.style, self.draw) for word in ["Hello", "World"]] spacing_calc, position, overflow = handler.calculate_spacing_and_position( text_objects, self.line_width, self.spacing[0], self.spacing[1]) # Right alignment should have position >= 0 self.assertGreaterEqual(position, 0) def test_justify_handler_spacing_calculation(self): """Test specific spacing calculation for justify alignment""" handler = JustifyAlignmentHandler() text_objects = [Text(word, self.style, self.draw) for word in ["Hello", "World"]] spacing_calc, position, overflow = 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) # Check spacing is reasonable self.assertGreaterEqual(spacing_calc, 0) 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.draw, font=self.style, halign=alignment) # Empty line should still have a handler self.assertIsNotNone(line._alignment_handler) # Should be able to render empty line line.render() 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.draw, font=self.style, halign=alignment) # Create a test word test_word = Word("test", self.style) # Add a single word result, part = line.add_word(test_word) self.assertTrue(result) # Should fit self.assertIsNone(part) # No overflow part # Should be able to render single word line line.render() self.assertEqual(len(line.text_objects), 1) if __name__ == '__main__': unittest.main()