clean up
This commit is contained in:
@@ -0,0 +1,191 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Unit tests for the new external pagination system.
|
||||
|
||||
Tests the BlockPaginator and handler architecture to ensure it works correctly
|
||||
with different block types using the unittest framework.
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from pyWebLayout.abstract.block import Paragraph, Heading, HeadingLevel
|
||||
from pyWebLayout.abstract.inline import Word
|
||||
from pyWebLayout.concrete.page import Page
|
||||
from pyWebLayout.style.fonts import Font
|
||||
from pyWebLayout.typesetting.block_pagination import BlockPaginator, PaginationResult
|
||||
|
||||
|
||||
class TestNewPaginationSystem(unittest.TestCase):
|
||||
"""Test cases for the new pagination system."""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test fixtures."""
|
||||
self.font = Font(font_size=16)
|
||||
self.heading_font = Font(font_size=20)
|
||||
|
||||
def create_test_paragraph(self, text: str, font: Font = None) -> Paragraph:
|
||||
"""Create a test paragraph with the given text."""
|
||||
if font is None:
|
||||
font = self.font
|
||||
|
||||
paragraph = Paragraph(font)
|
||||
words = text.split()
|
||||
|
||||
for word_text in words:
|
||||
word = Word(word_text, font)
|
||||
paragraph.add_word(word)
|
||||
|
||||
return paragraph
|
||||
|
||||
def create_test_heading(self, text: str, level: HeadingLevel = HeadingLevel.H1) -> Heading:
|
||||
"""Create a test heading with the given text."""
|
||||
heading = Heading(level, self.heading_font)
|
||||
|
||||
words = text.split()
|
||||
for word_text in words:
|
||||
word = Word(word_text, self.heading_font)
|
||||
heading.add_word(word)
|
||||
|
||||
return heading
|
||||
|
||||
def test_paragraph_pagination(self):
|
||||
"""Test paragraph pagination with line breaking."""
|
||||
# Create a long paragraph
|
||||
long_text = " ".join(["This is a very long paragraph that should be broken across multiple lines."] * 10)
|
||||
paragraph = self.create_test_paragraph(long_text)
|
||||
|
||||
# Create a page with limited height
|
||||
page = Page(size=(400, 200)) # Small page
|
||||
|
||||
# Test the pagination handler
|
||||
paginator = BlockPaginator()
|
||||
result = paginator.paginate_block(paragraph, page, available_height=100)
|
||||
|
||||
# Assertions
|
||||
self.assertIsInstance(result, PaginationResult)
|
||||
self.assertIsInstance(result.success, bool)
|
||||
self.assertIsInstance(result.height_used, (int, float))
|
||||
self.assertGreaterEqual(result.height_used, 0)
|
||||
|
||||
def test_page_filling(self):
|
||||
"""Test filling a page with multiple blocks."""
|
||||
# Create test blocks
|
||||
blocks = [
|
||||
self.create_test_heading("Chapter 1: Introduction"),
|
||||
self.create_test_paragraph("This is the first paragraph of the chapter. It contains some introductory text."),
|
||||
self.create_test_paragraph("This is the second paragraph. It has more content and should flow nicely."),
|
||||
self.create_test_heading("Section 1.1: Overview", HeadingLevel.H2),
|
||||
self.create_test_paragraph("This is a paragraph under the section. It has even more content that might not fit on the same page."),
|
||||
self.create_test_paragraph("This is another long paragraph that definitely won't fit. " * 20),
|
||||
]
|
||||
|
||||
# Create a page
|
||||
page = Page(size=(600, 400))
|
||||
|
||||
# Fill the page
|
||||
next_index, remainder_blocks = page.fill_with_blocks(blocks)
|
||||
|
||||
# Assertions
|
||||
self.assertIsInstance(next_index, int)
|
||||
self.assertGreaterEqual(next_index, 0)
|
||||
self.assertLessEqual(next_index, len(blocks))
|
||||
self.assertIsInstance(remainder_blocks, list)
|
||||
self.assertGreaterEqual(len(page._children), 0)
|
||||
|
||||
# Try to render the page
|
||||
try:
|
||||
page_image = page.render()
|
||||
self.assertIsNotNone(page_image)
|
||||
self.assertEqual(len(page_image.size), 2) # Should have width and height
|
||||
except Exception as e:
|
||||
self.fail(f"Page rendering failed: {e}")
|
||||
|
||||
def test_multi_page_creation(self):
|
||||
"""Test creating multiple pages from a list of blocks."""
|
||||
# Create many test blocks
|
||||
blocks = []
|
||||
for i in range(5): # Reduced for faster testing
|
||||
blocks.append(self.create_test_heading(f"Chapter {i+1}"))
|
||||
for j in range(2): # Reduced for faster testing
|
||||
long_text = f"This is paragraph {j+1} of chapter {i+1}. " * 10
|
||||
blocks.append(self.create_test_paragraph(long_text))
|
||||
|
||||
self.assertGreater(len(blocks), 0)
|
||||
|
||||
# Create pages until all blocks are processed
|
||||
pages = []
|
||||
remaining_blocks = blocks
|
||||
page_count = 0
|
||||
|
||||
while remaining_blocks and page_count < 10: # Safety limit
|
||||
page = Page(size=(600, 400))
|
||||
next_index, remainder_blocks = page.fill_with_blocks(remaining_blocks)
|
||||
|
||||
if page._children:
|
||||
pages.append(page)
|
||||
page_count += 1
|
||||
|
||||
# Update remaining blocks
|
||||
if remainder_blocks:
|
||||
remaining_blocks = remainder_blocks
|
||||
elif next_index < len(remaining_blocks):
|
||||
remaining_blocks = remaining_blocks[next_index:]
|
||||
else:
|
||||
remaining_blocks = []
|
||||
|
||||
# Safety check to prevent infinite loops
|
||||
if not page._children and remaining_blocks:
|
||||
break
|
||||
|
||||
# Assertions
|
||||
self.assertGreater(len(pages), 0, "Should create at least one page")
|
||||
self.assertLessEqual(page_count, 10, "Should not exceed safety limit")
|
||||
|
||||
# Try to render a few pages
|
||||
rendered_count = 0
|
||||
for page in pages[:2]: # Test first 2 pages
|
||||
try:
|
||||
page_image = page.render()
|
||||
rendered_count += 1
|
||||
self.assertIsNotNone(page_image)
|
||||
except Exception as e:
|
||||
self.fail(f"Page rendering failed: {e}")
|
||||
|
||||
self.assertGreater(rendered_count, 0, "Should render at least one page")
|
||||
|
||||
def test_empty_blocks_list(self):
|
||||
"""Test handling of empty blocks list."""
|
||||
page = Page(size=(600, 400))
|
||||
next_index, remainder_blocks = page.fill_with_blocks([])
|
||||
|
||||
self.assertEqual(next_index, 0)
|
||||
self.assertEqual(len(remainder_blocks), 0)
|
||||
self.assertEqual(len(page._children), 0)
|
||||
|
||||
def test_single_block(self):
|
||||
"""Test handling of single block."""
|
||||
blocks = [self.create_test_paragraph("Single paragraph test.")]
|
||||
page = Page(size=(600, 400))
|
||||
|
||||
next_index, remainder_blocks = page.fill_with_blocks(blocks)
|
||||
|
||||
self.assertEqual(next_index, 1)
|
||||
self.assertEqual(len(remainder_blocks), 0)
|
||||
self.assertGreater(len(page._children), 0)
|
||||
|
||||
def test_pagination_result_properties(self):
|
||||
"""Test PaginationResult object properties."""
|
||||
paragraph = self.create_test_paragraph("Test paragraph for pagination result.")
|
||||
page = Page(size=(400, 200))
|
||||
|
||||
paginator = BlockPaginator()
|
||||
result = paginator.paginate_block(paragraph, page, available_height=100)
|
||||
|
||||
# Test that result has expected properties
|
||||
self.assertTrue(hasattr(result, 'success'))
|
||||
self.assertTrue(hasattr(result, 'height_used'))
|
||||
self.assertTrue(hasattr(result, 'remainder'))
|
||||
self.assertTrue(hasattr(result, 'can_continue'))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -0,0 +1,232 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Unit tests for simple pagination logic without EPUB dependencies.
|
||||
Tests basic pagination functionality using the unittest framework.
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from pyWebLayout.concrete.page import Page
|
||||
from pyWebLayout.concrete.text import Text
|
||||
from pyWebLayout.style.fonts import Font
|
||||
from pyWebLayout.abstract.block import Paragraph
|
||||
from pyWebLayout.abstract.inline import Word
|
||||
|
||||
|
||||
class TestSimplePagination(unittest.TestCase):
|
||||
"""Test cases for simple pagination functionality."""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test fixtures."""
|
||||
self.font = Font(font_size=16)
|
||||
self.page_size = (700, 550)
|
||||
self.max_page_height = 510 # Leave room for padding
|
||||
|
||||
def create_test_paragraph(self, text_content: str) -> Paragraph:
|
||||
"""Create a test paragraph with the given text."""
|
||||
paragraph = Paragraph()
|
||||
words = text_content.split()
|
||||
|
||||
for word_text in words:
|
||||
word = Word(word_text, self.font)
|
||||
paragraph.add_word(word)
|
||||
|
||||
return paragraph
|
||||
|
||||
def test_single_paragraph_pagination(self):
|
||||
"""Test pagination with a single paragraph."""
|
||||
text = "This is a simple paragraph for testing pagination functionality."
|
||||
paragraph = self.create_test_paragraph(text)
|
||||
|
||||
page = Page(size=self.page_size)
|
||||
|
||||
# Convert block to renderable
|
||||
renderable = page._convert_block_to_renderable(paragraph)
|
||||
self.assertIsNotNone(renderable, "Should convert paragraph to renderable")
|
||||
|
||||
# Add to page
|
||||
page.add_child(renderable)
|
||||
self.assertEqual(len(page._children), 1)
|
||||
|
||||
# Layout should work
|
||||
try:
|
||||
page.layout()
|
||||
except Exception as e:
|
||||
self.fail(f"Layout failed: {e}")
|
||||
|
||||
# Render should work
|
||||
try:
|
||||
rendered_image = page.render()
|
||||
self.assertIsNotNone(rendered_image)
|
||||
self.assertEqual(rendered_image.size, self.page_size)
|
||||
except Exception as e:
|
||||
self.fail(f"Render failed: {e}")
|
||||
|
||||
def test_multiple_paragraphs_same_page(self):
|
||||
"""Test adding multiple small paragraphs to the same page."""
|
||||
paragraphs = [
|
||||
"First short paragraph.",
|
||||
"Second short paragraph.",
|
||||
"Third short paragraph."
|
||||
]
|
||||
|
||||
page = Page(size=self.page_size)
|
||||
|
||||
for i, text in enumerate(paragraphs):
|
||||
paragraph = self.create_test_paragraph(text)
|
||||
renderable = page._convert_block_to_renderable(paragraph)
|
||||
self.assertIsNotNone(renderable, f"Should convert paragraph {i+1}")
|
||||
|
||||
page.add_child(renderable)
|
||||
|
||||
self.assertEqual(len(page._children), len(paragraphs))
|
||||
|
||||
# Layout should work with multiple children
|
||||
try:
|
||||
page.layout()
|
||||
except Exception as e:
|
||||
self.fail(f"Layout with multiple paragraphs failed: {e}")
|
||||
|
||||
# Calculate page height
|
||||
max_bottom = self.calculate_page_height(page)
|
||||
self.assertLessEqual(max_bottom, self.max_page_height, "Page should not exceed height limit")
|
||||
|
||||
def test_page_overflow_detection(self):
|
||||
"""Test detection of page overflow."""
|
||||
# Create a very long paragraph that should cause overflow
|
||||
long_text = " ".join(["This is a very long paragraph with many words."] * 20)
|
||||
paragraph = self.create_test_paragraph(long_text)
|
||||
|
||||
page = Page(size=self.page_size)
|
||||
renderable = page._convert_block_to_renderable(paragraph)
|
||||
page.add_child(renderable)
|
||||
|
||||
try:
|
||||
page.layout()
|
||||
max_bottom = self.calculate_page_height(page)
|
||||
|
||||
# Very long content might exceed page height
|
||||
# This is expected behavior for testing overflow detection
|
||||
self.assertIsInstance(max_bottom, (int, float))
|
||||
|
||||
except Exception as e:
|
||||
# Layout might fail with very long content, which is acceptable
|
||||
self.assertIsInstance(e, Exception)
|
||||
|
||||
def test_page_height_calculation(self):
|
||||
"""Test page height calculation method."""
|
||||
page = Page(size=self.page_size)
|
||||
|
||||
# Empty page should have height 0
|
||||
height = self.calculate_page_height(page)
|
||||
self.assertEqual(height, 0)
|
||||
|
||||
# Add content and check height increases
|
||||
paragraph = self.create_test_paragraph("Test content for height calculation.")
|
||||
renderable = page._convert_block_to_renderable(paragraph)
|
||||
page.add_child(renderable)
|
||||
page.layout()
|
||||
|
||||
height_with_content = self.calculate_page_height(page)
|
||||
self.assertGreater(height_with_content, 0)
|
||||
|
||||
def test_multi_page_scenario(self):
|
||||
"""Test creating multiple pages from content."""
|
||||
# Create test content
|
||||
test_paragraphs = [
|
||||
"This is the first paragraph with some content.",
|
||||
"Here is a second paragraph with different content.",
|
||||
"The third paragraph continues with more text.",
|
||||
"Fourth paragraph here with additional content.",
|
||||
"Fifth paragraph with even more content for testing."
|
||||
]
|
||||
|
||||
pages = []
|
||||
current_page = Page(size=self.page_size)
|
||||
|
||||
for i, text in enumerate(test_paragraphs):
|
||||
paragraph = self.create_test_paragraph(text)
|
||||
renderable = current_page._convert_block_to_renderable(paragraph)
|
||||
|
||||
if renderable:
|
||||
# Store current state for potential rollback
|
||||
children_backup = current_page._children.copy()
|
||||
|
||||
# Add to current page
|
||||
current_page.add_child(renderable)
|
||||
|
||||
try:
|
||||
current_page.layout()
|
||||
max_bottom = self.calculate_page_height(current_page)
|
||||
|
||||
# Check if page is too full
|
||||
if max_bottom > self.max_page_height and len(current_page._children) > 1:
|
||||
# Rollback and start new page
|
||||
current_page._children = children_backup
|
||||
pages.append(current_page)
|
||||
|
||||
# Start new page with current content
|
||||
current_page = Page(size=self.page_size)
|
||||
current_page.add_child(renderable)
|
||||
current_page.layout()
|
||||
|
||||
except Exception:
|
||||
# Layout failed, rollback
|
||||
current_page._children = children_backup
|
||||
|
||||
# Add final page if it has content
|
||||
if current_page._children:
|
||||
pages.append(current_page)
|
||||
|
||||
# Assertions
|
||||
self.assertGreater(len(pages), 0, "Should create at least one page")
|
||||
|
||||
# Test rendering all pages
|
||||
for i, page in enumerate(pages):
|
||||
with self.subTest(page=i+1):
|
||||
self.assertGreater(len(page._children), 0, f"Page {i+1} should have content")
|
||||
|
||||
try:
|
||||
rendered_image = page.render()
|
||||
self.assertIsNotNone(rendered_image)
|
||||
self.assertEqual(rendered_image.size, self.page_size)
|
||||
except Exception as e:
|
||||
self.fail(f"Page {i+1} render failed: {e}")
|
||||
|
||||
def test_empty_paragraph_handling(self):
|
||||
"""Test handling of empty paragraphs."""
|
||||
empty_paragraph = self.create_test_paragraph("")
|
||||
page = Page(size=self.page_size)
|
||||
|
||||
# Empty paragraph should still be convertible
|
||||
renderable = page._convert_block_to_renderable(empty_paragraph)
|
||||
|
||||
if renderable: # Some implementations might return None for empty content
|
||||
page.add_child(renderable)
|
||||
try:
|
||||
page.layout()
|
||||
rendered_image = page.render()
|
||||
self.assertIsNotNone(rendered_image)
|
||||
except Exception as e:
|
||||
self.fail(f"Empty paragraph handling failed: {e}")
|
||||
|
||||
def test_conversion_error_handling(self):
|
||||
"""Test handling of blocks that can't be converted."""
|
||||
paragraph = self.create_test_paragraph("Test content")
|
||||
page = Page(size=self.page_size)
|
||||
|
||||
# This should normally work
|
||||
renderable = page._convert_block_to_renderable(paragraph)
|
||||
self.assertIsNotNone(renderable, "Normal paragraph should convert successfully")
|
||||
|
||||
def calculate_page_height(self, page):
|
||||
"""Helper method to calculate current page height."""
|
||||
max_bottom = 0
|
||||
for child in page._children:
|
||||
if hasattr(child, '_origin') and hasattr(child, '_size'):
|
||||
child_bottom = child._origin[1] + child._size[1]
|
||||
max_bottom = max(max_bottom, child_bottom)
|
||||
return max_bottom
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user