Compare commits
2 Commits
b65c35d96d
...
13d20c28c5
| Author | SHA1 | Date | |
|---|---|---|---|
| 13d20c28c5 | |||
| f8baf155e9 |
@ -75,6 +75,15 @@ def paragraph_layouter(paragraph: Paragraph, page: Page, start_word: int = 0, pr
|
||||
)
|
||||
text_align = concrete_style.text_align
|
||||
|
||||
# Apply page-level word spacing override if specified
|
||||
if hasattr(page.style, 'word_spacing') and isinstance(page.style.word_spacing, int) and page.style.word_spacing > 0:
|
||||
# Add the page-level word spacing to both min and max constraints
|
||||
min_ws, max_ws = word_spacing_constraints
|
||||
word_spacing_constraints = (
|
||||
min_ws + page.style.word_spacing,
|
||||
max_ws + page.style.word_spacing
|
||||
)
|
||||
|
||||
# Apply alignment override if provided
|
||||
if alignment_override is not None:
|
||||
text_align = alignment_override
|
||||
@ -91,9 +100,14 @@ def paragraph_layouter(paragraph: Paragraph, page: Page, start_word: int = 0, pr
|
||||
background=font.background
|
||||
)
|
||||
|
||||
# Calculate baseline-to-baseline spacing using line spacing multiplier
|
||||
# Calculate baseline-to-baseline spacing: font size + additional line spacing
|
||||
# This is the vertical distance between baselines of consecutive lines
|
||||
baseline_spacing = int(font.font_size * page.style.line_spacing_multiplier)
|
||||
# Formula: baseline_spacing = font_size + line_spacing (absolute pixels)
|
||||
line_spacing_value = getattr(page.style, 'line_spacing', 5)
|
||||
# Ensure line_spacing is an int (could be Mock in tests)
|
||||
if not isinstance(line_spacing_value, int):
|
||||
line_spacing_value = 5
|
||||
baseline_spacing = font.font_size + line_spacing_value
|
||||
|
||||
# Get font metrics for boundary checking
|
||||
ascent, descent = font.font.getmetrics()
|
||||
|
||||
@ -252,6 +252,11 @@ class BidirectionalLayouter:
|
||||
# Block doesn't fit, we're done with this page
|
||||
break
|
||||
|
||||
# Add inter-block spacing after successfully laying out a block
|
||||
# Only add if we're not at the end of the document and there's space
|
||||
if new_pos.block_index < len(self.blocks):
|
||||
page._current_y_offset += self.page_style.inter_block_spacing
|
||||
|
||||
# Ensure new position doesn't go beyond bounds
|
||||
if new_pos.block_index >= len(self.blocks):
|
||||
# We've reached the end of the document
|
||||
|
||||
@ -342,6 +342,96 @@ class EreaderLayoutManager:
|
||||
"""Get the current font scale"""
|
||||
return self.font_scale
|
||||
|
||||
def increase_line_spacing(self, amount: int = 2) -> Page:
|
||||
"""
|
||||
Increase line spacing and re-render current page.
|
||||
|
||||
Args:
|
||||
amount: Pixels to add to line spacing (default: 2)
|
||||
|
||||
Returns:
|
||||
Re-rendered page with increased line spacing
|
||||
"""
|
||||
self.page_style.line_spacing += amount
|
||||
self.renderer.page_style = self.page_style # Update renderer's reference
|
||||
self.renderer.buffer.invalidate_all() # Clear cache to force re-render
|
||||
return self.get_current_page()
|
||||
|
||||
def decrease_line_spacing(self, amount: int = 2) -> Page:
|
||||
"""
|
||||
Decrease line spacing and re-render current page.
|
||||
|
||||
Args:
|
||||
amount: Pixels to remove from line spacing (default: 2)
|
||||
|
||||
Returns:
|
||||
Re-rendered page with decreased line spacing
|
||||
"""
|
||||
self.page_style.line_spacing = max(0, self.page_style.line_spacing - amount)
|
||||
self.renderer.page_style = self.page_style # Update renderer's reference
|
||||
self.renderer.buffer.invalidate_all() # Clear cache to force re-render
|
||||
return self.get_current_page()
|
||||
|
||||
def increase_inter_block_spacing(self, amount: int = 5) -> Page:
|
||||
"""
|
||||
Increase spacing between blocks and re-render current page.
|
||||
|
||||
Args:
|
||||
amount: Pixels to add to inter-block spacing (default: 5)
|
||||
|
||||
Returns:
|
||||
Re-rendered page with increased block spacing
|
||||
"""
|
||||
self.page_style.inter_block_spacing += amount
|
||||
self.renderer.page_style = self.page_style # Update renderer's reference
|
||||
self.renderer.buffer.invalidate_all() # Clear cache to force re-render
|
||||
return self.get_current_page()
|
||||
|
||||
def decrease_inter_block_spacing(self, amount: int = 5) -> Page:
|
||||
"""
|
||||
Decrease spacing between blocks and re-render current page.
|
||||
|
||||
Args:
|
||||
amount: Pixels to remove from inter-block spacing (default: 5)
|
||||
|
||||
Returns:
|
||||
Re-rendered page with decreased block spacing
|
||||
"""
|
||||
self.page_style.inter_block_spacing = max(0, self.page_style.inter_block_spacing - amount)
|
||||
self.renderer.page_style = self.page_style # Update renderer's reference
|
||||
self.renderer.buffer.invalidate_all() # Clear cache to force re-render
|
||||
return self.get_current_page()
|
||||
|
||||
def increase_word_spacing(self, amount: int = 2) -> Page:
|
||||
"""
|
||||
Increase spacing between words and re-render current page.
|
||||
|
||||
Args:
|
||||
amount: Pixels to add to word spacing (default: 2)
|
||||
|
||||
Returns:
|
||||
Re-rendered page with increased word spacing
|
||||
"""
|
||||
self.page_style.word_spacing += amount
|
||||
self.renderer.page_style = self.page_style # Update renderer's reference
|
||||
self.renderer.buffer.invalidate_all() # Clear cache to force re-render
|
||||
return self.get_current_page()
|
||||
|
||||
def decrease_word_spacing(self, amount: int = 2) -> Page:
|
||||
"""
|
||||
Decrease spacing between words and re-render current page.
|
||||
|
||||
Args:
|
||||
amount: Pixels to remove from word spacing (default: 2)
|
||||
|
||||
Returns:
|
||||
Re-rendered page with decreased word spacing
|
||||
"""
|
||||
self.page_style.word_spacing = max(0, self.page_style.word_spacing - amount)
|
||||
self.renderer.page_style = self.page_style # Update renderer's reference
|
||||
self.renderer.buffer.invalidate_all() # Clear cache to force re-render
|
||||
return self.get_current_page()
|
||||
|
||||
def get_table_of_contents(self) -> List[Tuple[str, HeadingLevel, RenderingPosition]]:
|
||||
"""
|
||||
Get the table of contents.
|
||||
|
||||
@ -14,8 +14,9 @@ class PageStyle:
|
||||
border_color: Tuple[int, int, int] = (0, 0, 0)
|
||||
|
||||
# Spacing properties
|
||||
line_spacing: int = 5
|
||||
inter_block_spacing: int = 15
|
||||
line_spacing: int = 5 # Additional pixels between lines (added to font size)
|
||||
inter_block_spacing: int = 15 # Pixels between blocks (paragraphs, headings, etc.)
|
||||
word_spacing: int = 0 # Additional pixels between words (0 = use font defaults)
|
||||
|
||||
# Padding (top, right, bottom, left)
|
||||
padding: Tuple[int, int, int, int] = (20, 20, 20, 20)
|
||||
@ -25,7 +26,6 @@ class PageStyle:
|
||||
|
||||
# Typography properties
|
||||
max_font_size: int = 72 # Maximum font size allowed on a page
|
||||
line_spacing_multiplier: float = 1.2 # Baseline-to-baseline spacing multiplier
|
||||
|
||||
@property
|
||||
def padding_top(self) -> int:
|
||||
|
||||
784
tests/layout/test_ereader_manager.py
Normal file
784
tests/layout/test_ereader_manager.py
Normal file
@ -0,0 +1,784 @@
|
||||
"""
|
||||
Tests for the ereader manager components.
|
||||
|
||||
This module tests:
|
||||
- BookmarkManager: Bookmark and position persistence
|
||||
- EreaderLayoutManager: High-level ereader interface
|
||||
- create_ereader_manager: Convenience function
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import json
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
from unittest.mock import Mock, MagicMock, patch
|
||||
|
||||
from pyWebLayout.layout.ereader_manager import (
|
||||
BookmarkManager,
|
||||
EreaderLayoutManager,
|
||||
create_ereader_manager
|
||||
)
|
||||
from pyWebLayout.layout.ereader_layout import RenderingPosition
|
||||
from pyWebLayout.abstract.block import Paragraph, Heading, HeadingLevel
|
||||
from pyWebLayout.abstract.inline import Word
|
||||
from pyWebLayout.style import Font
|
||||
from pyWebLayout.style.page_style import PageStyle
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Fixtures
|
||||
# ============================================================================
|
||||
|
||||
@pytest.fixture
|
||||
def temp_bookmarks_dir(tmp_path):
|
||||
"""Create a temporary directory for bookmarks."""
|
||||
bookmarks_dir = tmp_path / "bookmarks"
|
||||
bookmarks_dir.mkdir()
|
||||
return str(bookmarks_dir)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def sample_font():
|
||||
"""Create a standard font for testing."""
|
||||
return Font(font_size=12, colour=(0, 0, 0))
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def sample_blocks(sample_font):
|
||||
"""Create sample document blocks."""
|
||||
blocks = []
|
||||
|
||||
# Heading
|
||||
h1 = Heading(HeadingLevel.H1, sample_font)
|
||||
h1.add_word(Word("Chapter", sample_font))
|
||||
h1.add_word(Word("One", sample_font))
|
||||
blocks.append(h1)
|
||||
|
||||
# Paragraphs
|
||||
for i in range(5):
|
||||
p = Paragraph(sample_font)
|
||||
p.add_word(Word(f"Paragraph", sample_font))
|
||||
p.add_word(Word(f"{i}", sample_font))
|
||||
blocks.append(p)
|
||||
|
||||
return blocks
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def sample_position():
|
||||
"""Create a sample rendering position."""
|
||||
return RenderingPosition(
|
||||
chapter_index=1,
|
||||
block_index=5,
|
||||
word_index=10
|
||||
)
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# BookmarkManager Tests
|
||||
# ============================================================================
|
||||
|
||||
class TestBookmarkManager:
|
||||
"""Tests for the BookmarkManager class."""
|
||||
|
||||
def test_initialization(self, temp_bookmarks_dir):
|
||||
"""Test BookmarkManager initialization."""
|
||||
manager = BookmarkManager("test_doc", temp_bookmarks_dir)
|
||||
|
||||
assert manager.document_id == "test_doc"
|
||||
assert manager.bookmarks_dir == Path(temp_bookmarks_dir)
|
||||
assert manager.bookmarks_file.exists() or True # May not exist yet
|
||||
assert isinstance(manager._bookmarks, dict)
|
||||
|
||||
def test_initialization_creates_directory(self, tmp_path):
|
||||
"""Test that initialization creates bookmarks directory if needed."""
|
||||
bookmarks_dir = str(tmp_path / "new_bookmarks")
|
||||
|
||||
manager = BookmarkManager("test_doc", bookmarks_dir)
|
||||
|
||||
assert Path(bookmarks_dir).exists()
|
||||
assert Path(bookmarks_dir).is_dir()
|
||||
|
||||
def test_add_bookmark(self, temp_bookmarks_dir, sample_position):
|
||||
"""Test adding a bookmark."""
|
||||
manager = BookmarkManager("test_doc", temp_bookmarks_dir)
|
||||
|
||||
manager.add_bookmark("Chapter 1", sample_position)
|
||||
|
||||
# Verify bookmark was added
|
||||
bookmark = manager.get_bookmark("Chapter 1")
|
||||
assert bookmark is not None
|
||||
assert bookmark == sample_position
|
||||
|
||||
def test_add_multiple_bookmarks(self, temp_bookmarks_dir):
|
||||
"""Test adding multiple bookmarks."""
|
||||
manager = BookmarkManager("test_doc", temp_bookmarks_dir)
|
||||
|
||||
pos1 = RenderingPosition(chapter_index=1, block_index=5)
|
||||
pos2 = RenderingPosition(chapter_index=2, block_index=10)
|
||||
pos3 = RenderingPosition(chapter_index=3, block_index=15)
|
||||
|
||||
manager.add_bookmark("Bookmark 1", pos1)
|
||||
manager.add_bookmark("Bookmark 2", pos2)
|
||||
manager.add_bookmark("Bookmark 3", pos3)
|
||||
|
||||
assert len(manager._bookmarks) == 3
|
||||
assert manager.get_bookmark("Bookmark 1") == pos1
|
||||
assert manager.get_bookmark("Bookmark 2") == pos2
|
||||
assert manager.get_bookmark("Bookmark 3") == pos3
|
||||
|
||||
def test_remove_bookmark(self, temp_bookmarks_dir, sample_position):
|
||||
"""Test removing a bookmark."""
|
||||
manager = BookmarkManager("test_doc", temp_bookmarks_dir)
|
||||
|
||||
manager.add_bookmark("Test", sample_position)
|
||||
assert manager.get_bookmark("Test") is not None
|
||||
|
||||
result = manager.remove_bookmark("Test")
|
||||
|
||||
assert result is True
|
||||
assert manager.get_bookmark("Test") is None
|
||||
|
||||
def test_remove_nonexistent_bookmark(self, temp_bookmarks_dir):
|
||||
"""Test removing a bookmark that doesn't exist."""
|
||||
manager = BookmarkManager("test_doc", temp_bookmarks_dir)
|
||||
|
||||
result = manager.remove_bookmark("Nonexistent")
|
||||
|
||||
assert result is False
|
||||
|
||||
def test_get_bookmark(self, temp_bookmarks_dir, sample_position):
|
||||
"""Test getting a bookmark."""
|
||||
manager = BookmarkManager("test_doc", temp_bookmarks_dir)
|
||||
|
||||
manager.add_bookmark("Test", sample_position)
|
||||
|
||||
retrieved = manager.get_bookmark("Test")
|
||||
|
||||
assert retrieved == sample_position
|
||||
|
||||
def test_get_nonexistent_bookmark(self, temp_bookmarks_dir):
|
||||
"""Test getting a bookmark that doesn't exist."""
|
||||
manager = BookmarkManager("test_doc", temp_bookmarks_dir)
|
||||
|
||||
result = manager.get_bookmark("Nonexistent")
|
||||
|
||||
assert result is None
|
||||
|
||||
def test_list_bookmarks(self, temp_bookmarks_dir):
|
||||
"""Test listing all bookmarks."""
|
||||
manager = BookmarkManager("test_doc", temp_bookmarks_dir)
|
||||
|
||||
pos1 = RenderingPosition(chapter_index=1, block_index=5)
|
||||
pos2 = RenderingPosition(chapter_index=2, block_index=10)
|
||||
|
||||
manager.add_bookmark("First", pos1)
|
||||
manager.add_bookmark("Second", pos2)
|
||||
|
||||
bookmarks = manager.list_bookmarks()
|
||||
|
||||
assert len(bookmarks) == 2
|
||||
assert ("First", pos1) in bookmarks
|
||||
assert ("Second", pos2) in bookmarks
|
||||
|
||||
def test_list_bookmarks_empty(self, temp_bookmarks_dir):
|
||||
"""Test listing bookmarks when none exist."""
|
||||
manager = BookmarkManager("test_doc", temp_bookmarks_dir)
|
||||
|
||||
bookmarks = manager.list_bookmarks()
|
||||
|
||||
assert bookmarks == []
|
||||
|
||||
def test_save_reading_position(self, temp_bookmarks_dir, sample_position):
|
||||
"""Test saving current reading position."""
|
||||
manager = BookmarkManager("test_doc", temp_bookmarks_dir)
|
||||
|
||||
manager.save_reading_position(sample_position)
|
||||
|
||||
# Verify file was created
|
||||
assert manager.position_file.exists()
|
||||
|
||||
# Verify content
|
||||
with open(manager.position_file, 'r') as f:
|
||||
data = json.load(f)
|
||||
|
||||
assert data['chapter_index'] == sample_position.chapter_index
|
||||
assert data['block_index'] == sample_position.block_index
|
||||
|
||||
def test_load_reading_position(self, temp_bookmarks_dir, sample_position):
|
||||
"""Test loading saved reading position."""
|
||||
manager = BookmarkManager("test_doc", temp_bookmarks_dir)
|
||||
|
||||
manager.save_reading_position(sample_position)
|
||||
|
||||
loaded = manager.load_reading_position()
|
||||
|
||||
assert loaded is not None
|
||||
assert loaded == sample_position
|
||||
|
||||
def test_load_reading_position_nonexistent(self, temp_bookmarks_dir):
|
||||
"""Test loading reading position when file doesn't exist."""
|
||||
manager = BookmarkManager("test_doc", temp_bookmarks_dir)
|
||||
|
||||
loaded = manager.load_reading_position()
|
||||
|
||||
assert loaded is None
|
||||
|
||||
def test_bookmark_persistence(self, temp_bookmarks_dir, sample_position):
|
||||
"""Test that bookmarks persist across manager instances."""
|
||||
# Create first manager and add bookmark
|
||||
manager1 = BookmarkManager("test_doc", temp_bookmarks_dir)
|
||||
manager1.add_bookmark("Persistent", sample_position)
|
||||
|
||||
# Create second manager and verify bookmark exists
|
||||
manager2 = BookmarkManager("test_doc", temp_bookmarks_dir)
|
||||
loaded = manager2.get_bookmark("Persistent")
|
||||
|
||||
assert loaded is not None
|
||||
assert loaded == sample_position
|
||||
|
||||
def test_position_persistence(self, temp_bookmarks_dir, sample_position):
|
||||
"""Test that reading position persists across manager instances."""
|
||||
# Save with first manager
|
||||
manager1 = BookmarkManager("test_doc", temp_bookmarks_dir)
|
||||
manager1.save_reading_position(sample_position)
|
||||
|
||||
# Load with second manager
|
||||
manager2 = BookmarkManager("test_doc", temp_bookmarks_dir)
|
||||
loaded = manager2.load_reading_position()
|
||||
|
||||
assert loaded == sample_position
|
||||
|
||||
def test_corrupt_bookmarks_file(self, temp_bookmarks_dir):
|
||||
"""Test handling of corrupt bookmarks file."""
|
||||
manager = BookmarkManager("test_doc", temp_bookmarks_dir)
|
||||
|
||||
# Write corrupt JSON
|
||||
with open(manager.bookmarks_file, 'w') as f:
|
||||
f.write("{ invalid json }}")
|
||||
|
||||
# Should handle gracefully and load empty bookmarks
|
||||
manager2 = BookmarkManager("test_doc", temp_bookmarks_dir)
|
||||
assert manager2._bookmarks == {}
|
||||
|
||||
def test_corrupt_position_file(self, temp_bookmarks_dir):
|
||||
"""Test handling of corrupt position file."""
|
||||
manager = BookmarkManager("test_doc", temp_bookmarks_dir)
|
||||
|
||||
# Write corrupt JSON
|
||||
with open(manager.position_file, 'w') as f:
|
||||
f.write("{ invalid json }}")
|
||||
|
||||
# Should handle gracefully
|
||||
loaded = manager.load_reading_position()
|
||||
assert loaded is None
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# EreaderLayoutManager Tests
|
||||
# ============================================================================
|
||||
|
||||
class TestEreaderLayoutManager:
|
||||
"""Tests for the EreaderLayoutManager class."""
|
||||
|
||||
def test_initialization(self, sample_blocks, temp_bookmarks_dir):
|
||||
"""Test EreaderLayoutManager initialization."""
|
||||
manager = EreaderLayoutManager(
|
||||
sample_blocks,
|
||||
page_size=(800, 600),
|
||||
document_id="test_doc",
|
||||
bookmarks_dir=temp_bookmarks_dir
|
||||
)
|
||||
|
||||
assert manager.blocks == sample_blocks
|
||||
assert manager.page_size == (800, 600)
|
||||
assert manager.document_id == "test_doc"
|
||||
assert manager.font_scale == 1.0
|
||||
assert isinstance(manager.current_position, RenderingPosition)
|
||||
|
||||
def test_initialization_with_custom_page_style(self, sample_blocks, temp_bookmarks_dir):
|
||||
"""Test initialization with custom page style."""
|
||||
custom_style = PageStyle()
|
||||
|
||||
manager = EreaderLayoutManager(
|
||||
sample_blocks,
|
||||
page_size=(800, 600),
|
||||
page_style=custom_style,
|
||||
bookmarks_dir=temp_bookmarks_dir
|
||||
)
|
||||
|
||||
assert manager.page_style == custom_style
|
||||
|
||||
def test_initialization_loads_saved_position(self, sample_blocks, temp_bookmarks_dir):
|
||||
"""Test that initialization loads saved reading position."""
|
||||
# Save a position first
|
||||
bookmark_mgr = BookmarkManager("test_doc", temp_bookmarks_dir)
|
||||
saved_pos = RenderingPosition(chapter_index=2, block_index=10)
|
||||
bookmark_mgr.save_reading_position(saved_pos)
|
||||
|
||||
# Create manager - should load saved position
|
||||
manager = EreaderLayoutManager(
|
||||
sample_blocks,
|
||||
page_size=(800, 600),
|
||||
document_id="test_doc",
|
||||
bookmarks_dir=temp_bookmarks_dir
|
||||
)
|
||||
|
||||
assert manager.current_position == saved_pos
|
||||
|
||||
def test_get_current_page(self, sample_blocks, temp_bookmarks_dir):
|
||||
"""Test getting the current page."""
|
||||
manager = EreaderLayoutManager(
|
||||
sample_blocks,
|
||||
page_size=(800, 600),
|
||||
bookmarks_dir=temp_bookmarks_dir
|
||||
)
|
||||
|
||||
page = manager.get_current_page()
|
||||
|
||||
from pyWebLayout.concrete.page import Page
|
||||
assert isinstance(page, Page)
|
||||
|
||||
def test_next_page(self, sample_blocks, temp_bookmarks_dir):
|
||||
"""Test advancing to next page."""
|
||||
manager = EreaderLayoutManager(
|
||||
sample_blocks,
|
||||
page_size=(800, 600),
|
||||
bookmarks_dir=temp_bookmarks_dir
|
||||
)
|
||||
|
||||
initial_pos = manager.current_position.copy()
|
||||
|
||||
next_page = manager.next_page()
|
||||
|
||||
# Position should have advanced
|
||||
assert manager.current_position != initial_pos or next_page is None
|
||||
|
||||
def test_next_page_at_end(self, sample_blocks, temp_bookmarks_dir):
|
||||
"""Test next_page when at end of document."""
|
||||
manager = EreaderLayoutManager(
|
||||
sample_blocks,
|
||||
page_size=(800, 600),
|
||||
bookmarks_dir=temp_bookmarks_dir
|
||||
)
|
||||
|
||||
# Move to end
|
||||
manager.current_position = RenderingPosition(
|
||||
block_index=len(sample_blocks) + 100
|
||||
)
|
||||
|
||||
result = manager.next_page()
|
||||
|
||||
# Should return None at end
|
||||
assert result is None
|
||||
|
||||
def test_previous_page(self, sample_blocks, temp_bookmarks_dir):
|
||||
"""Test going to previous page."""
|
||||
manager = EreaderLayoutManager(
|
||||
sample_blocks,
|
||||
page_size=(800, 600),
|
||||
bookmarks_dir=temp_bookmarks_dir
|
||||
)
|
||||
|
||||
# Move forward first
|
||||
manager.current_position = RenderingPosition(block_index=3)
|
||||
|
||||
prev_page = manager.previous_page()
|
||||
|
||||
# Should return a page or None if at beginning
|
||||
from pyWebLayout.concrete.page import Page
|
||||
assert prev_page is None or isinstance(prev_page, Page)
|
||||
|
||||
def test_previous_page_at_beginning(self, sample_blocks, temp_bookmarks_dir):
|
||||
"""Test previous_page when at beginning of document."""
|
||||
manager = EreaderLayoutManager(
|
||||
sample_blocks,
|
||||
page_size=(800, 600),
|
||||
bookmarks_dir=temp_bookmarks_dir
|
||||
)
|
||||
|
||||
# At beginning
|
||||
manager.current_position = RenderingPosition()
|
||||
|
||||
result = manager.previous_page()
|
||||
|
||||
assert result is None
|
||||
|
||||
def test_jump_to_position(self, sample_blocks, temp_bookmarks_dir):
|
||||
"""Test jumping to a specific position."""
|
||||
manager = EreaderLayoutManager(
|
||||
sample_blocks,
|
||||
page_size=(800, 600),
|
||||
bookmarks_dir=temp_bookmarks_dir
|
||||
)
|
||||
|
||||
target_pos = RenderingPosition(chapter_index=1, block_index=3)
|
||||
|
||||
page = manager.jump_to_position(target_pos)
|
||||
|
||||
assert manager.current_position == target_pos
|
||||
from pyWebLayout.concrete.page import Page
|
||||
assert isinstance(page, Page)
|
||||
|
||||
def test_jump_to_chapter(self, sample_blocks, temp_bookmarks_dir):
|
||||
"""Test jumping to a chapter by title."""
|
||||
manager = EreaderLayoutManager(
|
||||
sample_blocks,
|
||||
page_size=(800, 600),
|
||||
bookmarks_dir=temp_bookmarks_dir
|
||||
)
|
||||
|
||||
page = manager.jump_to_chapter("Chapter One")
|
||||
|
||||
# May return page or None if chapter not found
|
||||
from pyWebLayout.concrete.page import Page
|
||||
assert page is None or isinstance(page, Page)
|
||||
|
||||
def test_jump_to_chapter_not_found(self, sample_blocks, temp_bookmarks_dir):
|
||||
"""Test jumping to non-existent chapter."""
|
||||
manager = EreaderLayoutManager(
|
||||
sample_blocks,
|
||||
page_size=(800, 600),
|
||||
bookmarks_dir=temp_bookmarks_dir
|
||||
)
|
||||
|
||||
result = manager.jump_to_chapter("Nonexistent Chapter")
|
||||
|
||||
assert result is None
|
||||
|
||||
def test_jump_to_chapter_index(self, sample_blocks, temp_bookmarks_dir):
|
||||
"""Test jumping to chapter by index."""
|
||||
manager = EreaderLayoutManager(
|
||||
sample_blocks,
|
||||
page_size=(800, 600),
|
||||
bookmarks_dir=temp_bookmarks_dir
|
||||
)
|
||||
|
||||
page = manager.jump_to_chapter_index(0)
|
||||
|
||||
from pyWebLayout.concrete.page import Page
|
||||
assert page is None or isinstance(page, Page)
|
||||
|
||||
def test_jump_to_chapter_index_invalid(self, sample_blocks, temp_bookmarks_dir):
|
||||
"""Test jumping to invalid chapter index."""
|
||||
manager = EreaderLayoutManager(
|
||||
sample_blocks,
|
||||
page_size=(800, 600),
|
||||
bookmarks_dir=temp_bookmarks_dir
|
||||
)
|
||||
|
||||
result = manager.jump_to_chapter_index(999)
|
||||
|
||||
assert result is None
|
||||
|
||||
def test_set_font_scale(self, sample_blocks, temp_bookmarks_dir):
|
||||
"""Test changing font scale."""
|
||||
manager = EreaderLayoutManager(
|
||||
sample_blocks,
|
||||
page_size=(800, 600),
|
||||
bookmarks_dir=temp_bookmarks_dir
|
||||
)
|
||||
|
||||
page = manager.set_font_scale(1.5)
|
||||
|
||||
assert manager.font_scale == 1.5
|
||||
from pyWebLayout.concrete.page import Page
|
||||
assert isinstance(page, Page)
|
||||
|
||||
def test_set_font_scale_same(self, sample_blocks, temp_bookmarks_dir):
|
||||
"""Test setting font scale to same value."""
|
||||
manager = EreaderLayoutManager(
|
||||
sample_blocks,
|
||||
page_size=(800, 600),
|
||||
bookmarks_dir=temp_bookmarks_dir
|
||||
)
|
||||
|
||||
page = manager.set_font_scale(1.0)
|
||||
|
||||
assert manager.font_scale == 1.0
|
||||
|
||||
def test_get_font_scale(self, sample_blocks, temp_bookmarks_dir):
|
||||
"""Test getting current font scale."""
|
||||
manager = EreaderLayoutManager(
|
||||
sample_blocks,
|
||||
page_size=(800, 600),
|
||||
bookmarks_dir=temp_bookmarks_dir
|
||||
)
|
||||
|
||||
scale = manager.get_font_scale()
|
||||
|
||||
assert scale == 1.0
|
||||
|
||||
def test_get_table_of_contents(self, sample_blocks, temp_bookmarks_dir):
|
||||
"""Test getting table of contents."""
|
||||
manager = EreaderLayoutManager(
|
||||
sample_blocks,
|
||||
page_size=(800, 600),
|
||||
bookmarks_dir=temp_bookmarks_dir
|
||||
)
|
||||
|
||||
toc = manager.get_table_of_contents()
|
||||
|
||||
assert isinstance(toc, list)
|
||||
|
||||
def test_get_current_chapter(self, sample_blocks, temp_bookmarks_dir):
|
||||
"""Test getting current chapter info."""
|
||||
manager = EreaderLayoutManager(
|
||||
sample_blocks,
|
||||
page_size=(800, 600),
|
||||
bookmarks_dir=temp_bookmarks_dir
|
||||
)
|
||||
|
||||
chapter = manager.get_current_chapter()
|
||||
|
||||
# May be None or ChapterInfo
|
||||
assert chapter is None or hasattr(chapter, 'title')
|
||||
|
||||
def test_add_bookmark(self, sample_blocks, temp_bookmarks_dir):
|
||||
"""Test adding a bookmark at current position."""
|
||||
manager = EreaderLayoutManager(
|
||||
sample_blocks,
|
||||
page_size=(800, 600),
|
||||
bookmarks_dir=temp_bookmarks_dir
|
||||
)
|
||||
|
||||
result = manager.add_bookmark("Test Bookmark")
|
||||
|
||||
assert result is True
|
||||
# Verify bookmark was added
|
||||
bookmark = manager.bookmark_manager.get_bookmark("Test Bookmark")
|
||||
assert bookmark is not None
|
||||
|
||||
def test_remove_bookmark(self, sample_blocks, temp_bookmarks_dir):
|
||||
"""Test removing a bookmark."""
|
||||
manager = EreaderLayoutManager(
|
||||
sample_blocks,
|
||||
page_size=(800, 600),
|
||||
bookmarks_dir=temp_bookmarks_dir
|
||||
)
|
||||
|
||||
manager.add_bookmark("Test")
|
||||
result = manager.remove_bookmark("Test")
|
||||
|
||||
assert result is True
|
||||
|
||||
def test_jump_to_bookmark(self, sample_blocks, temp_bookmarks_dir):
|
||||
"""Test jumping to a bookmark."""
|
||||
manager = EreaderLayoutManager(
|
||||
sample_blocks,
|
||||
page_size=(800, 600),
|
||||
bookmarks_dir=temp_bookmarks_dir
|
||||
)
|
||||
|
||||
# Add bookmark at specific position
|
||||
manager.current_position = RenderingPosition(block_index=3)
|
||||
manager.add_bookmark("Test Position")
|
||||
|
||||
# Move away
|
||||
manager.current_position = RenderingPosition(block_index=0)
|
||||
|
||||
# Jump to bookmark
|
||||
page = manager.jump_to_bookmark("Test Position")
|
||||
|
||||
from pyWebLayout.concrete.page import Page
|
||||
assert isinstance(page, Page)
|
||||
assert manager.current_position.block_index == 3
|
||||
|
||||
def test_jump_to_nonexistent_bookmark(self, sample_blocks, temp_bookmarks_dir):
|
||||
"""Test jumping to non-existent bookmark."""
|
||||
manager = EreaderLayoutManager(
|
||||
sample_blocks,
|
||||
page_size=(800, 600),
|
||||
bookmarks_dir=temp_bookmarks_dir
|
||||
)
|
||||
|
||||
result = manager.jump_to_bookmark("Nonexistent")
|
||||
|
||||
assert result is None
|
||||
|
||||
def test_list_bookmarks(self, sample_blocks, temp_bookmarks_dir):
|
||||
"""Test listing all bookmarks."""
|
||||
manager = EreaderLayoutManager(
|
||||
sample_blocks,
|
||||
page_size=(800, 600),
|
||||
bookmarks_dir=temp_bookmarks_dir
|
||||
)
|
||||
|
||||
manager.add_bookmark("Bookmark 1")
|
||||
manager.add_bookmark("Bookmark 2")
|
||||
|
||||
bookmarks = manager.list_bookmarks()
|
||||
|
||||
assert len(bookmarks) == 2
|
||||
|
||||
def test_get_reading_progress(self, sample_blocks, temp_bookmarks_dir):
|
||||
"""Test getting reading progress percentage."""
|
||||
manager = EreaderLayoutManager(
|
||||
sample_blocks,
|
||||
page_size=(800, 600),
|
||||
bookmarks_dir=temp_bookmarks_dir
|
||||
)
|
||||
|
||||
progress = manager.get_reading_progress()
|
||||
|
||||
assert 0.0 <= progress <= 1.0
|
||||
|
||||
def test_get_reading_progress_at_end(self, sample_blocks, temp_bookmarks_dir):
|
||||
"""Test reading progress at end of document."""
|
||||
manager = EreaderLayoutManager(
|
||||
sample_blocks,
|
||||
page_size=(800, 600),
|
||||
bookmarks_dir=temp_bookmarks_dir
|
||||
)
|
||||
|
||||
manager.current_position = RenderingPosition(
|
||||
block_index=len(sample_blocks) - 1
|
||||
)
|
||||
|
||||
progress = manager.get_reading_progress()
|
||||
|
||||
assert progress == 1.0
|
||||
|
||||
def test_get_reading_progress_empty_document(self, temp_bookmarks_dir):
|
||||
"""Test reading progress with empty document."""
|
||||
manager = EreaderLayoutManager(
|
||||
[],
|
||||
page_size=(800, 600),
|
||||
bookmarks_dir=temp_bookmarks_dir
|
||||
)
|
||||
|
||||
progress = manager.get_reading_progress()
|
||||
|
||||
assert progress == 0.0
|
||||
|
||||
def test_get_position_info(self, sample_blocks, temp_bookmarks_dir):
|
||||
"""Test getting detailed position information."""
|
||||
manager = EreaderLayoutManager(
|
||||
sample_blocks,
|
||||
page_size=(800, 600),
|
||||
bookmarks_dir=temp_bookmarks_dir
|
||||
)
|
||||
|
||||
info = manager.get_position_info()
|
||||
|
||||
assert isinstance(info, dict)
|
||||
assert 'position' in info
|
||||
assert 'chapter' in info
|
||||
assert 'progress' in info
|
||||
assert 'font_scale' in info
|
||||
assert 'page_size' in info
|
||||
|
||||
def test_get_cache_stats(self, sample_blocks, temp_bookmarks_dir):
|
||||
"""Test getting cache statistics."""
|
||||
manager = EreaderLayoutManager(
|
||||
sample_blocks,
|
||||
page_size=(800, 600),
|
||||
bookmarks_dir=temp_bookmarks_dir
|
||||
)
|
||||
|
||||
stats = manager.get_cache_stats()
|
||||
|
||||
assert isinstance(stats, dict)
|
||||
|
||||
def test_position_changed_callback(self, sample_blocks, temp_bookmarks_dir):
|
||||
"""Test position changed callback."""
|
||||
manager = EreaderLayoutManager(
|
||||
sample_blocks,
|
||||
page_size=(800, 600),
|
||||
bookmarks_dir=temp_bookmarks_dir
|
||||
)
|
||||
|
||||
callback_called = []
|
||||
|
||||
def callback(position):
|
||||
callback_called.append(position)
|
||||
|
||||
manager.set_position_changed_callback(callback)
|
||||
manager.jump_to_position(RenderingPosition(block_index=3))
|
||||
|
||||
assert len(callback_called) > 0
|
||||
|
||||
def test_chapter_changed_callback(self, sample_blocks, temp_bookmarks_dir):
|
||||
"""Test chapter changed callback."""
|
||||
manager = EreaderLayoutManager(
|
||||
sample_blocks,
|
||||
page_size=(800, 600),
|
||||
bookmarks_dir=temp_bookmarks_dir
|
||||
)
|
||||
|
||||
callback_called = []
|
||||
|
||||
def callback(chapter):
|
||||
callback_called.append(chapter)
|
||||
|
||||
manager.set_chapter_changed_callback(callback)
|
||||
manager.jump_to_position(RenderingPosition(block_index=3))
|
||||
|
||||
assert len(callback_called) > 0
|
||||
|
||||
def test_shutdown(self, sample_blocks, temp_bookmarks_dir):
|
||||
"""Test shutdown saves position."""
|
||||
manager = EreaderLayoutManager(
|
||||
sample_blocks,
|
||||
page_size=(800, 600),
|
||||
document_id="test_doc",
|
||||
bookmarks_dir=temp_bookmarks_dir
|
||||
)
|
||||
|
||||
test_pos = RenderingPosition(block_index=5)
|
||||
manager.current_position = test_pos
|
||||
|
||||
manager.shutdown()
|
||||
|
||||
# Verify position was saved
|
||||
bookmark_mgr = BookmarkManager("test_doc", temp_bookmarks_dir)
|
||||
loaded = bookmark_mgr.load_reading_position()
|
||||
assert loaded == test_pos
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Convenience Function Tests
|
||||
# ============================================================================
|
||||
|
||||
class TestCreateEreaderManager:
|
||||
"""Tests for the create_ereader_manager convenience function."""
|
||||
|
||||
def test_create_with_defaults(self, sample_blocks):
|
||||
"""Test creating manager with default parameters."""
|
||||
manager = create_ereader_manager(
|
||||
sample_blocks,
|
||||
page_size=(800, 600)
|
||||
)
|
||||
|
||||
assert isinstance(manager, EreaderLayoutManager)
|
||||
assert manager.blocks == sample_blocks
|
||||
assert manager.page_size == (800, 600)
|
||||
assert manager.document_id == "default"
|
||||
|
||||
def test_create_with_custom_document_id(self, sample_blocks):
|
||||
"""Test creating manager with custom document ID."""
|
||||
manager = create_ereader_manager(
|
||||
sample_blocks,
|
||||
page_size=(800, 600),
|
||||
document_id="custom_doc"
|
||||
)
|
||||
|
||||
assert manager.document_id == "custom_doc"
|
||||
|
||||
def test_create_with_kwargs(self, sample_blocks, tmp_path):
|
||||
"""Test creating manager with additional kwargs."""
|
||||
bookmarks_dir = str(tmp_path / "custom_bookmarks")
|
||||
|
||||
manager = create_ereader_manager(
|
||||
sample_blocks,
|
||||
page_size=(800, 600),
|
||||
document_id="test",
|
||||
buffer_size=10,
|
||||
bookmarks_dir=bookmarks_dir
|
||||
)
|
||||
|
||||
assert isinstance(manager, EreaderLayoutManager)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
pytest.main([__file__, "-v"])
|
||||
Loading…
x
Reference in New Issue
Block a user