This commit is contained in:
@@ -1,832 +0,0 @@
|
||||
"""
|
||||
Comprehensive tests for the EbookReader application interface.
|
||||
|
||||
Tests cover:
|
||||
- EPUB loading and initialization
|
||||
- Navigation (forward, backward, boundaries)
|
||||
- Font scaling and styling
|
||||
- Chapter navigation
|
||||
- Position management (bookmarks)
|
||||
- Information retrieval
|
||||
- File operations
|
||||
- Error handling
|
||||
- Context manager
|
||||
- Integration scenarios
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import tempfile
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
import os
|
||||
|
||||
from pyWebLayout.layout.ereader_application import EbookReader, create_ebook_reader
|
||||
|
||||
|
||||
class TestEbookReaderInitialization(unittest.TestCase):
|
||||
"""Test EbookReader creation and EPUB loading"""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test environment"""
|
||||
self.temp_dir = tempfile.mkdtemp()
|
||||
self.epub_path = "tests/data/test.epub"
|
||||
|
||||
if not Path(self.epub_path).exists():
|
||||
self.skipTest(f"Test EPUB not found at {self.epub_path}")
|
||||
|
||||
def tearDown(self):
|
||||
"""Clean up test environment"""
|
||||
shutil.rmtree(self.temp_dir, ignore_errors=True)
|
||||
|
||||
def test_create_reader_with_defaults(self):
|
||||
"""Test creating reader with default settings"""
|
||||
reader = EbookReader(bookmarks_dir=self.temp_dir)
|
||||
|
||||
self.assertEqual(reader.page_size, (800, 1000))
|
||||
self.assertEqual(reader.base_font_scale, 1.0)
|
||||
self.assertIsNone(reader.manager)
|
||||
self.assertFalse(reader.is_loaded())
|
||||
|
||||
reader.close()
|
||||
|
||||
def test_create_reader_with_custom_settings(self):
|
||||
"""Test creating reader with custom settings"""
|
||||
reader = EbookReader(
|
||||
page_size=(600, 800),
|
||||
margin=50,
|
||||
background_color=(240, 240, 240),
|
||||
line_spacing=10,
|
||||
inter_block_spacing=20,
|
||||
bookmarks_dir=self.temp_dir,
|
||||
buffer_size=3
|
||||
)
|
||||
|
||||
self.assertEqual(reader.page_size, (600, 800))
|
||||
self.assertEqual(reader.page_style.line_spacing, 10)
|
||||
self.assertEqual(reader.page_style.inter_block_spacing, 20)
|
||||
self.assertEqual(reader.buffer_size, 3)
|
||||
|
||||
reader.close()
|
||||
|
||||
def test_load_valid_epub(self):
|
||||
"""Test loading a valid EPUB file"""
|
||||
reader = EbookReader(bookmarks_dir=self.temp_dir)
|
||||
|
||||
success = reader.load_epub(self.epub_path)
|
||||
|
||||
self.assertTrue(success)
|
||||
self.assertTrue(reader.is_loaded())
|
||||
self.assertIsNotNone(reader.manager)
|
||||
self.assertIsNotNone(reader.blocks)
|
||||
self.assertIsNotNone(reader.document_id)
|
||||
self.assertIsNotNone(reader.book_title)
|
||||
self.assertIsNotNone(reader.book_author)
|
||||
|
||||
reader.close()
|
||||
|
||||
def test_load_nonexistent_epub(self):
|
||||
"""Test loading a non-existent EPUB file"""
|
||||
reader = EbookReader(bookmarks_dir=self.temp_dir)
|
||||
|
||||
success = reader.load_epub("nonexistent.epub")
|
||||
|
||||
self.assertFalse(success)
|
||||
self.assertFalse(reader.is_loaded())
|
||||
|
||||
reader.close()
|
||||
|
||||
def test_load_invalid_epub(self):
|
||||
"""Test loading an invalid file as EPUB"""
|
||||
# Create a temporary invalid file
|
||||
invalid_path = os.path.join(self.temp_dir, "invalid.epub")
|
||||
with open(invalid_path, 'w') as f:
|
||||
f.write("This is not a valid EPUB file")
|
||||
|
||||
reader = EbookReader(bookmarks_dir=self.temp_dir)
|
||||
|
||||
success = reader.load_epub(invalid_path)
|
||||
|
||||
self.assertFalse(success)
|
||||
self.assertFalse(reader.is_loaded())
|
||||
|
||||
reader.close()
|
||||
|
||||
def test_convenience_function(self):
|
||||
"""Test create_ebook_reader convenience function"""
|
||||
reader = create_ebook_reader(
|
||||
page_size=(700, 900),
|
||||
bookmarks_dir=self.temp_dir
|
||||
)
|
||||
|
||||
self.assertIsInstance(reader, EbookReader)
|
||||
self.assertEqual(reader.page_size, (700, 900))
|
||||
|
||||
reader.close()
|
||||
|
||||
|
||||
class TestEbookReaderFontScaling(unittest.TestCase):
|
||||
"""Test font size control"""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test environment"""
|
||||
self.temp_dir = tempfile.mkdtemp()
|
||||
self.epub_path = "tests/data/test.epub"
|
||||
|
||||
if not Path(self.epub_path).exists():
|
||||
self.skipTest(f"Test EPUB not found at {self.epub_path}")
|
||||
|
||||
self.reader = EbookReader(
|
||||
bookmarks_dir=self.temp_dir,
|
||||
buffer_size=0 # Disable buffering for tests
|
||||
)
|
||||
self.reader.load_epub(self.epub_path)
|
||||
|
||||
def tearDown(self):
|
||||
"""Clean up test environment"""
|
||||
self.reader.close()
|
||||
shutil.rmtree(self.temp_dir, ignore_errors=True)
|
||||
|
||||
def test_set_font_size(self):
|
||||
"""Test setting font size with arbitrary scale"""
|
||||
page = self.reader.set_font_size(1.5)
|
||||
|
||||
self.assertIsNotNone(page)
|
||||
self.assertEqual(self.reader.get_font_size(), 1.5)
|
||||
|
||||
def test_increase_font_size(self):
|
||||
"""Test increasing font size by one step"""
|
||||
initial_size = self.reader.get_font_size()
|
||||
|
||||
page = self.reader.increase_font_size()
|
||||
|
||||
self.assertIsNotNone(page)
|
||||
self.assertEqual(self.reader.get_font_size(), initial_size + 0.1)
|
||||
|
||||
def test_decrease_font_size(self):
|
||||
"""Test decreasing font size by one step"""
|
||||
self.reader.set_font_size(1.5)
|
||||
|
||||
page = self.reader.decrease_font_size()
|
||||
|
||||
self.assertIsNotNone(page)
|
||||
self.assertAlmostEqual(self.reader.get_font_size(), 1.4, places=5)
|
||||
|
||||
def test_font_size_bounds_clamping(self):
|
||||
"""Test that font size is clamped between 0.5x and 3.0x"""
|
||||
# Test upper bound
|
||||
self.reader.set_font_size(5.0)
|
||||
self.assertEqual(self.reader.get_font_size(), 3.0)
|
||||
|
||||
# Test lower bound
|
||||
self.reader.set_font_size(0.1)
|
||||
self.assertEqual(self.reader.get_font_size(), 0.5)
|
||||
|
||||
def test_get_font_size(self):
|
||||
"""Test getting current font size"""
|
||||
self.assertEqual(self.reader.get_font_size(), 1.0)
|
||||
|
||||
self.reader.set_font_size(2.0)
|
||||
self.assertEqual(self.reader.get_font_size(), 2.0)
|
||||
|
||||
def test_font_scale_with_navigation(self):
|
||||
"""Test that font scale persists across page navigation"""
|
||||
self.reader.set_font_size(1.5)
|
||||
initial_font_size = self.reader.get_font_size()
|
||||
|
||||
# Navigate forward
|
||||
self.reader.next_page()
|
||||
|
||||
# Font size should be preserved
|
||||
self.assertEqual(self.reader.get_font_size(), initial_font_size)
|
||||
|
||||
|
||||
class TestEbookReaderSpacing(unittest.TestCase):
|
||||
"""Test line and block spacing"""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test environment"""
|
||||
self.temp_dir = tempfile.mkdtemp()
|
||||
self.epub_path = "tests/data/test.epub"
|
||||
|
||||
if not Path(self.epub_path).exists():
|
||||
self.skipTest(f"Test EPUB not found at {self.epub_path}")
|
||||
|
||||
self.reader = EbookReader(
|
||||
bookmarks_dir=self.temp_dir,
|
||||
buffer_size=0
|
||||
)
|
||||
self.reader.load_epub(self.epub_path)
|
||||
|
||||
def tearDown(self):
|
||||
"""Clean up test environment"""
|
||||
self.reader.close()
|
||||
shutil.rmtree(self.temp_dir, ignore_errors=True)
|
||||
|
||||
def test_set_line_spacing(self):
|
||||
"""Test setting line spacing"""
|
||||
page = self.reader.set_line_spacing(10)
|
||||
|
||||
self.assertIsNotNone(page)
|
||||
self.assertEqual(self.reader.page_style.line_spacing, 10)
|
||||
|
||||
def test_set_inter_block_spacing(self):
|
||||
"""Test setting inter-block spacing"""
|
||||
page = self.reader.set_inter_block_spacing(25)
|
||||
|
||||
self.assertIsNotNone(page)
|
||||
self.assertEqual(self.reader.page_style.inter_block_spacing, 25)
|
||||
|
||||
def test_spacing_with_navigation(self):
|
||||
"""Test that spacing changes affect rendering after navigation"""
|
||||
self.reader.set_line_spacing(15)
|
||||
|
||||
page = self.reader.next_page()
|
||||
|
||||
self.assertIsNotNone(page)
|
||||
self.assertEqual(self.reader.page_style.line_spacing, 15)
|
||||
|
||||
def test_spacing_position_preservation(self):
|
||||
"""Test that changing spacing preserves reading position"""
|
||||
# Navigate to a specific position
|
||||
for _ in range(3):
|
||||
self.reader.next_page()
|
||||
|
||||
position_before = self.reader.manager.current_position.copy()
|
||||
|
||||
# Change spacing
|
||||
self.reader.set_line_spacing(12)
|
||||
|
||||
position_after = self.reader.manager.current_position
|
||||
|
||||
# Position should be preserved
|
||||
self.assertEqual(position_before.chapter_index, position_after.chapter_index)
|
||||
self.assertEqual(position_before.block_index, position_after.block_index)
|
||||
|
||||
|
||||
class TestEbookReaderChapterNavigation(unittest.TestCase):
|
||||
"""Test chapter navigation features"""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test environment"""
|
||||
self.temp_dir = tempfile.mkdtemp()
|
||||
self.epub_path = "tests/data/test.epub"
|
||||
|
||||
if not Path(self.epub_path).exists():
|
||||
self.skipTest(f"Test EPUB not found at {self.epub_path}")
|
||||
|
||||
self.reader = EbookReader(
|
||||
bookmarks_dir=self.temp_dir,
|
||||
buffer_size=0
|
||||
)
|
||||
self.reader.load_epub(self.epub_path)
|
||||
|
||||
def tearDown(self):
|
||||
"""Clean up test environment"""
|
||||
self.reader.close()
|
||||
shutil.rmtree(self.temp_dir, ignore_errors=True)
|
||||
|
||||
def test_get_chapters(self):
|
||||
"""Test getting list of chapters"""
|
||||
chapters = self.reader.get_chapters()
|
||||
|
||||
self.assertIsInstance(chapters, list)
|
||||
if len(chapters) > 0:
|
||||
# Each chapter should be a tuple (title, index)
|
||||
self.assertIsInstance(chapters[0], tuple)
|
||||
self.assertEqual(len(chapters[0]), 2)
|
||||
|
||||
def test_get_chapter_positions(self):
|
||||
"""Test getting chapter positions"""
|
||||
positions = self.reader.get_chapter_positions()
|
||||
|
||||
self.assertIsInstance(positions, list)
|
||||
if len(positions) > 0:
|
||||
# Each item should be (title, RenderingPosition)
|
||||
self.assertIsInstance(positions[0], tuple)
|
||||
self.assertEqual(len(positions[0]), 2)
|
||||
|
||||
def test_jump_to_chapter_by_index(self):
|
||||
"""Test jumping to chapter by index"""
|
||||
chapters = self.reader.get_chapters()
|
||||
|
||||
if len(chapters) > 0:
|
||||
page = self.reader.jump_to_chapter(0)
|
||||
self.assertIsNotNone(page)
|
||||
|
||||
def test_jump_to_chapter_by_name(self):
|
||||
"""Test jumping to chapter by name"""
|
||||
chapters = self.reader.get_chapters()
|
||||
|
||||
if len(chapters) > 0:
|
||||
chapter_title = chapters[0][0]
|
||||
page = self.reader.jump_to_chapter(chapter_title)
|
||||
self.assertIsNotNone(page)
|
||||
|
||||
def test_jump_to_invalid_chapter_index(self):
|
||||
"""Test jumping to invalid chapter index"""
|
||||
page = self.reader.jump_to_chapter(9999)
|
||||
|
||||
self.assertIsNone(page)
|
||||
|
||||
def test_jump_to_invalid_chapter_name(self):
|
||||
"""Test jumping to non-existent chapter name"""
|
||||
page = self.reader.jump_to_chapter("Non-Existent Chapter")
|
||||
|
||||
self.assertIsNone(page)
|
||||
|
||||
|
||||
class TestEbookReaderInformation(unittest.TestCase):
|
||||
"""Test information retrieval methods"""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test environment"""
|
||||
self.temp_dir = tempfile.mkdtemp()
|
||||
self.epub_path = "tests/data/test.epub"
|
||||
|
||||
if not Path(self.epub_path).exists():
|
||||
self.skipTest(f"Test EPUB not found at {self.epub_path}")
|
||||
|
||||
self.reader = EbookReader(
|
||||
bookmarks_dir=self.temp_dir,
|
||||
buffer_size=0
|
||||
)
|
||||
self.reader.load_epub(self.epub_path)
|
||||
|
||||
def tearDown(self):
|
||||
"""Clean up test environment"""
|
||||
self.reader.close()
|
||||
shutil.rmtree(self.temp_dir, ignore_errors=True)
|
||||
|
||||
def test_get_position_info(self):
|
||||
"""Test getting detailed position information"""
|
||||
info = self.reader.get_position_info()
|
||||
|
||||
self.assertIsInstance(info, dict)
|
||||
self.assertIn('position', info)
|
||||
self.assertIn('chapter', info)
|
||||
self.assertIn('progress', info)
|
||||
self.assertIn('font_scale', info)
|
||||
self.assertIn('book_title', info)
|
||||
self.assertIn('book_author', info)
|
||||
|
||||
def test_get_reading_progress(self):
|
||||
"""Test getting reading progress as percentage"""
|
||||
progress = self.reader.get_reading_progress()
|
||||
|
||||
self.assertIsInstance(progress, float)
|
||||
self.assertGreaterEqual(progress, 0.0)
|
||||
self.assertLessEqual(progress, 1.0)
|
||||
|
||||
# Progress should increase after navigation
|
||||
initial_progress = progress
|
||||
for _ in range(5):
|
||||
self.reader.next_page()
|
||||
|
||||
new_progress = self.reader.get_reading_progress()
|
||||
self.assertGreater(new_progress, initial_progress)
|
||||
|
||||
def test_get_current_chapter_info(self):
|
||||
"""Test getting current chapter information"""
|
||||
info = self.reader.get_current_chapter_info()
|
||||
|
||||
# May be None if no chapters
|
||||
if info is not None:
|
||||
self.assertIsInstance(info, dict)
|
||||
self.assertIn('title', info)
|
||||
self.assertIn('level', info)
|
||||
self.assertIn('block_index', info)
|
||||
|
||||
def test_get_book_info_complete(self):
|
||||
"""Test getting complete book information"""
|
||||
info = self.reader.get_book_info()
|
||||
|
||||
self.assertIsInstance(info, dict)
|
||||
self.assertIn('title', info)
|
||||
self.assertIn('author', info)
|
||||
self.assertIn('document_id', info)
|
||||
self.assertIn('total_blocks', info)
|
||||
self.assertIn('total_chapters', info)
|
||||
self.assertIn('page_size', info)
|
||||
self.assertIn('font_scale', info)
|
||||
|
||||
self.assertGreater(info['total_blocks'], 0)
|
||||
self.assertEqual(info['page_size'], self.reader.page_size)
|
||||
|
||||
|
||||
class TestEbookReaderFileOperations(unittest.TestCase):
|
||||
"""Test file I/O operations"""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test environment"""
|
||||
self.temp_dir = tempfile.mkdtemp()
|
||||
self.epub_path = "tests/data/test.epub"
|
||||
|
||||
if not Path(self.epub_path).exists():
|
||||
self.skipTest(f"Test EPUB not found at {self.epub_path}")
|
||||
|
||||
self.reader = EbookReader(
|
||||
bookmarks_dir=self.temp_dir,
|
||||
buffer_size=0
|
||||
)
|
||||
self.reader.load_epub(self.epub_path)
|
||||
|
||||
def tearDown(self):
|
||||
"""Clean up test environment"""
|
||||
self.reader.close()
|
||||
shutil.rmtree(self.temp_dir, ignore_errors=True)
|
||||
|
||||
def test_render_to_file_png(self):
|
||||
"""Test saving current page as PNG"""
|
||||
output_path = os.path.join(self.temp_dir, "page.png")
|
||||
|
||||
success = self.reader.render_to_file(output_path)
|
||||
|
||||
self.assertTrue(success)
|
||||
self.assertTrue(os.path.exists(output_path))
|
||||
|
||||
# Verify it's a valid image
|
||||
img = Image.open(output_path)
|
||||
self.assertEqual(img.size, self.reader.page_size)
|
||||
|
||||
def test_render_to_file_jpg(self):
|
||||
"""Test saving current page as JPEG"""
|
||||
output_path = os.path.join(self.temp_dir, "page.jpg")
|
||||
|
||||
# Get the page image and convert to RGB (JPEG doesn't support RGBA)
|
||||
page_img = self.reader.get_current_page()
|
||||
if page_img.mode == 'RGBA':
|
||||
page_img = page_img.convert('RGB')
|
||||
|
||||
# Save manually since render_to_file might not handle conversion
|
||||
try:
|
||||
page_img.save(output_path)
|
||||
success = True
|
||||
except Exception:
|
||||
success = False
|
||||
|
||||
self.assertTrue(success)
|
||||
self.assertTrue(os.path.exists(output_path))
|
||||
|
||||
def test_render_to_invalid_path(self):
|
||||
"""Test saving to invalid path"""
|
||||
invalid_path = "/nonexistent/directory/page.png"
|
||||
|
||||
success = self.reader.render_to_file(invalid_path)
|
||||
|
||||
self.assertFalse(success)
|
||||
|
||||
|
||||
class TestEbookReaderContextManager(unittest.TestCase):
|
||||
"""Test context manager and cleanup"""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test environment"""
|
||||
self.temp_dir = tempfile.mkdtemp()
|
||||
self.epub_path = "tests/data/test.epub"
|
||||
|
||||
if not Path(self.epub_path).exists():
|
||||
self.skipTest(f"Test EPUB not found at {self.epub_path}")
|
||||
|
||||
def tearDown(self):
|
||||
"""Clean up test environment"""
|
||||
shutil.rmtree(self.temp_dir, ignore_errors=True)
|
||||
|
||||
def test_context_manager_usage(self):
|
||||
"""Test using EbookReader as context manager"""
|
||||
with EbookReader(bookmarks_dir=self.temp_dir) as reader:
|
||||
success = reader.load_epub(self.epub_path)
|
||||
self.assertTrue(success)
|
||||
|
||||
page = reader.get_current_page()
|
||||
self.assertIsNotNone(page)
|
||||
|
||||
# After exiting context, manager should be cleaned up
|
||||
self.assertIsNone(reader.manager)
|
||||
|
||||
def test_close_method(self):
|
||||
"""Test explicit close method"""
|
||||
reader = EbookReader(bookmarks_dir=self.temp_dir)
|
||||
reader.load_epub(self.epub_path)
|
||||
|
||||
self.assertIsNotNone(reader.manager)
|
||||
|
||||
reader.close()
|
||||
|
||||
self.assertIsNone(reader.manager)
|
||||
|
||||
def test_operations_after_close(self):
|
||||
"""Test that operations fail gracefully after close"""
|
||||
reader = EbookReader(bookmarks_dir=self.temp_dir)
|
||||
reader.load_epub(self.epub_path)
|
||||
reader.close()
|
||||
|
||||
# These should all return None or empty
|
||||
self.assertIsNone(reader.get_current_page())
|
||||
self.assertIsNone(reader.next_page())
|
||||
self.assertIsNone(reader.previous_page())
|
||||
self.assertEqual(reader.get_chapters(), [])
|
||||
|
||||
|
||||
class TestEbookReaderErrorHandling(unittest.TestCase):
|
||||
"""Test error handling and edge cases"""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test environment"""
|
||||
self.temp_dir = tempfile.mkdtemp()
|
||||
self.epub_path = "tests/data/test.epub"
|
||||
|
||||
if not Path(self.epub_path).exists():
|
||||
self.skipTest(f"Test EPUB not found at {self.epub_path}")
|
||||
|
||||
def tearDown(self):
|
||||
"""Clean up test environment"""
|
||||
shutil.rmtree(self.temp_dir, ignore_errors=True)
|
||||
|
||||
def test_operations_without_loaded_book(self):
|
||||
"""Test that operations handle unloaded state gracefully"""
|
||||
reader = EbookReader(bookmarks_dir=self.temp_dir)
|
||||
|
||||
# All these should return None or empty/False
|
||||
self.assertIsNone(reader.get_current_page())
|
||||
self.assertIsNone(reader.next_page())
|
||||
self.assertIsNone(reader.previous_page())
|
||||
self.assertFalse(reader.save_position("test"))
|
||||
self.assertIsNone(reader.load_position("test"))
|
||||
self.assertEqual(reader.list_saved_positions(), [])
|
||||
self.assertFalse(reader.delete_position("test"))
|
||||
self.assertEqual(reader.get_chapters(), [])
|
||||
self.assertIsNone(reader.jump_to_chapter(0))
|
||||
self.assertIsNone(reader.set_font_size(1.5))
|
||||
self.assertEqual(reader.get_reading_progress(), 0.0)
|
||||
self.assertIsNone(reader.get_current_chapter_info())
|
||||
|
||||
reader.close()
|
||||
|
||||
def test_is_loaded(self):
|
||||
"""Test is_loaded method"""
|
||||
reader = EbookReader(bookmarks_dir=self.temp_dir)
|
||||
|
||||
self.assertFalse(reader.is_loaded())
|
||||
|
||||
reader.load_epub(self.epub_path)
|
||||
|
||||
self.assertTrue(reader.is_loaded())
|
||||
|
||||
reader.close()
|
||||
|
||||
|
||||
class TestEbookReaderIntegration(unittest.TestCase):
|
||||
"""Test complex integration scenarios"""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test environment"""
|
||||
self.temp_dir = tempfile.mkdtemp()
|
||||
self.epub_path = "tests/data/test.epub"
|
||||
|
||||
if not Path(self.epub_path).exists():
|
||||
self.skipTest(f"Test EPUB not found at {self.epub_path}")
|
||||
|
||||
self.reader = EbookReader(
|
||||
bookmarks_dir=self.temp_dir,
|
||||
buffer_size=0
|
||||
)
|
||||
self.reader.load_epub(self.epub_path)
|
||||
|
||||
def tearDown(self):
|
||||
"""Clean up test environment"""
|
||||
self.reader.close()
|
||||
shutil.rmtree(self.temp_dir, ignore_errors=True)
|
||||
|
||||
def test_font_scaling_preserves_position(self):
|
||||
"""Test that changing font scale preserves reading position"""
|
||||
# Navigate to a specific position
|
||||
for _ in range(3):
|
||||
self.reader.next_page()
|
||||
|
||||
position_before = self.reader.manager.current_position.copy()
|
||||
|
||||
# Change font size
|
||||
self.reader.set_font_size(1.5)
|
||||
|
||||
position_after = self.reader.manager.current_position
|
||||
|
||||
# Position should be preserved
|
||||
self.assertEqual(position_before.chapter_index, position_after.chapter_index)
|
||||
self.assertEqual(position_before.block_index, position_after.block_index)
|
||||
|
||||
def test_styling_with_bookmarks(self):
|
||||
"""Test that bookmarks work correctly across styling changes"""
|
||||
# Navigate and save position
|
||||
for _ in range(5):
|
||||
self.reader.next_page()
|
||||
|
||||
self.reader.save_position("test_bookmark")
|
||||
|
||||
# Change styling
|
||||
self.reader.set_font_size(1.5)
|
||||
self.reader.set_line_spacing(12)
|
||||
|
||||
# Navigate away
|
||||
for _ in range(5):
|
||||
self.reader.next_page()
|
||||
|
||||
# Jump back to bookmark
|
||||
page = self.reader.load_position("test_bookmark")
|
||||
|
||||
self.assertIsNotNone(page)
|
||||
|
||||
# Cleanup
|
||||
self.reader.delete_position("test_bookmark")
|
||||
|
||||
def test_chapter_navigation_after_font_change(self):
|
||||
"""Test chapter navigation after changing font size"""
|
||||
self.reader.set_font_size(2.0)
|
||||
|
||||
chapters = self.reader.get_chapters()
|
||||
|
||||
if len(chapters) > 0:
|
||||
page = self.reader.jump_to_chapter(0)
|
||||
self.assertIsNotNone(page)
|
||||
|
||||
|
||||
class TestEbookReaderNavigation(unittest.TestCase):
|
||||
"""Test EbookReader navigation functionality (existing tests)"""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test environment"""
|
||||
self.temp_dir = tempfile.mkdtemp()
|
||||
self.epub_path = "tests/data/test.epub"
|
||||
|
||||
if not Path(self.epub_path).exists():
|
||||
self.skipTest(f"Test EPUB not found at {self.epub_path}")
|
||||
|
||||
def tearDown(self):
|
||||
"""Clean up test environment"""
|
||||
shutil.rmtree(self.temp_dir, ignore_errors=True)
|
||||
|
||||
def compare_images(self, img1: Image.Image, img2: Image.Image) -> bool:
|
||||
"""
|
||||
Check if two PIL Images are pixel-perfect identical.
|
||||
"""
|
||||
if img1 is None or img2 is None:
|
||||
return False
|
||||
|
||||
if img1.size != img2.size:
|
||||
return False
|
||||
|
||||
arr1 = np.array(img1)
|
||||
arr2 = np.array(img2)
|
||||
|
||||
return np.array_equal(arr1, arr2)
|
||||
|
||||
def test_bidirectional_navigation_20_pages(self):
|
||||
"""
|
||||
Test that navigating forward 20 pages and then backward 20 pages
|
||||
produces identical page renderings for the first page.
|
||||
"""
|
||||
reader = EbookReader(
|
||||
page_size=(800, 1000),
|
||||
bookmarks_dir=self.temp_dir,
|
||||
buffer_size=0
|
||||
)
|
||||
|
||||
success = reader.load_epub(self.epub_path)
|
||||
self.assertTrue(success, "Failed to load test EPUB")
|
||||
self.assertTrue(reader.is_loaded(), "Reader should be loaded")
|
||||
|
||||
initial_page = reader.get_current_page()
|
||||
self.assertIsNotNone(initial_page, "Initial page should not be None")
|
||||
|
||||
initial_position = reader.manager.current_position.copy()
|
||||
|
||||
forward_pages = [initial_page]
|
||||
forward_positions = [initial_position]
|
||||
pages_to_navigate = 20
|
||||
|
||||
for i in range(pages_to_navigate):
|
||||
page = reader.next_page()
|
||||
if page is None:
|
||||
break
|
||||
forward_pages.append(page)
|
||||
forward_positions.append(reader.manager.current_position.copy())
|
||||
|
||||
actual_pages_navigated = len(forward_pages) - 1
|
||||
|
||||
backward_pages = []
|
||||
|
||||
for i in range(len(forward_positions) - 1, -1, -1):
|
||||
position = forward_positions[i]
|
||||
page_obj = reader.manager.jump_to_position(position)
|
||||
page_img = page_obj.render()
|
||||
backward_pages.append(page_img)
|
||||
|
||||
final_page = backward_pages[-1]
|
||||
|
||||
self.assertTrue(
|
||||
self.compare_images(initial_page, final_page),
|
||||
"First page should be identical after forward/backward navigation"
|
||||
)
|
||||
|
||||
reader.close()
|
||||
|
||||
def test_navigation_at_boundaries(self):
|
||||
"""Test navigation behavior at document boundaries."""
|
||||
reader = EbookReader(
|
||||
page_size=(800, 1000),
|
||||
bookmarks_dir=self.temp_dir,
|
||||
buffer_size=0
|
||||
)
|
||||
|
||||
success = reader.load_epub(self.epub_path)
|
||||
self.assertTrue(success, "Failed to load test EPUB")
|
||||
|
||||
# Try to go backward from first page
|
||||
page = reader.previous_page()
|
||||
# Should return None or stay on same page
|
||||
|
||||
# Navigate forward until end
|
||||
pages_forward = 0
|
||||
max_pages = 100
|
||||
while pages_forward < max_pages:
|
||||
page = reader.next_page()
|
||||
if page is None:
|
||||
break
|
||||
pages_forward += 1
|
||||
|
||||
# Try to go forward from last page
|
||||
page = reader.next_page()
|
||||
self.assertIsNone(page, "Should return None at end of document")
|
||||
|
||||
reader.close()
|
||||
|
||||
|
||||
class TestEbookReaderPositionManagement(unittest.TestCase):
|
||||
"""Test position tracking and bookmark features"""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test environment"""
|
||||
self.temp_dir = tempfile.mkdtemp()
|
||||
self.epub_path = "tests/data/test.epub"
|
||||
|
||||
if not Path(self.epub_path).exists():
|
||||
self.skipTest(f"Test EPUB not found at {self.epub_path}")
|
||||
|
||||
self.reader = EbookReader(
|
||||
bookmarks_dir=self.temp_dir,
|
||||
buffer_size=0
|
||||
)
|
||||
self.reader.load_epub(self.epub_path)
|
||||
|
||||
def tearDown(self):
|
||||
"""Clean up test environment"""
|
||||
self.reader.close()
|
||||
shutil.rmtree(self.temp_dir, ignore_errors=True)
|
||||
|
||||
def test_position_save_and_load(self):
|
||||
"""Test saving and loading positions"""
|
||||
# Navigate to a position
|
||||
for _ in range(3):
|
||||
self.reader.next_page()
|
||||
|
||||
# Save position
|
||||
success = self.reader.save_position("test_pos")
|
||||
self.assertTrue(success)
|
||||
|
||||
# Navigate away
|
||||
for _ in range(5):
|
||||
self.reader.next_page()
|
||||
|
||||
# Load saved position
|
||||
page = self.reader.load_position("test_pos")
|
||||
self.assertIsNotNone(page)
|
||||
|
||||
def test_list_saved_positions(self):
|
||||
"""Test listing saved positions"""
|
||||
self.reader.save_position("pos1")
|
||||
self.reader.save_position("pos2")
|
||||
|
||||
positions = self.reader.list_saved_positions()
|
||||
|
||||
self.assertIn("pos1", positions)
|
||||
self.assertIn("pos2", positions)
|
||||
|
||||
def test_delete_position(self):
|
||||
"""Test deleting a saved position"""
|
||||
self.reader.save_position("temp_pos")
|
||||
|
||||
success = self.reader.delete_position("temp_pos")
|
||||
self.assertTrue(success)
|
||||
|
||||
positions = self.reader.list_saved_positions()
|
||||
self.assertNotIn("temp_pos", positions)
|
||||
|
||||
def test_delete_nonexistent_position(self):
|
||||
"""Test deleting a non-existent position"""
|
||||
success = self.reader.delete_position("nonexistent")
|
||||
self.assertFalse(success)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -1,455 +0,0 @@
|
||||
"""
|
||||
Comprehensive tests for the ereader layout system.
|
||||
|
||||
Tests the complete ereader functionality including position tracking,
|
||||
font scaling, chapter navigation, and page buffering.
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import tempfile
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
|
||||
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
|
||||
from pyWebLayout.layout.ereader_layout import RenderingPosition, ChapterNavigator, FontScaler, BidirectionalLayouter
|
||||
from pyWebLayout.layout.ereader_manager import EreaderLayoutManager, BookmarkManager, create_ereader_manager
|
||||
|
||||
|
||||
class TestRenderingPosition(unittest.TestCase):
|
||||
"""Test the RenderingPosition class"""
|
||||
|
||||
def test_position_creation(self):
|
||||
"""Test creating a rendering position"""
|
||||
pos = RenderingPosition(
|
||||
chapter_index=1,
|
||||
block_index=5,
|
||||
word_index=10,
|
||||
table_row=2,
|
||||
table_col=3
|
||||
)
|
||||
|
||||
self.assertEqual(pos.chapter_index, 1)
|
||||
self.assertEqual(pos.block_index, 5)
|
||||
self.assertEqual(pos.word_index, 10)
|
||||
self.assertEqual(pos.table_row, 2)
|
||||
self.assertEqual(pos.table_col, 3)
|
||||
|
||||
def test_position_serialization(self):
|
||||
"""Test position serialization and deserialization"""
|
||||
pos = RenderingPosition(
|
||||
chapter_index=1,
|
||||
block_index=5,
|
||||
word_index=10,
|
||||
remaining_pretext="test"
|
||||
)
|
||||
|
||||
# Serialize to dict
|
||||
pos_dict = pos.to_dict()
|
||||
self.assertIsInstance(pos_dict, dict)
|
||||
self.assertEqual(pos_dict['chapter_index'], 1)
|
||||
self.assertEqual(pos_dict['remaining_pretext'], "test")
|
||||
|
||||
# Deserialize from dict
|
||||
pos2 = RenderingPosition.from_dict(pos_dict)
|
||||
self.assertEqual(pos, pos2)
|
||||
|
||||
def test_position_copy(self):
|
||||
"""Test position copying"""
|
||||
pos = RenderingPosition(chapter_index=1, block_index=5)
|
||||
pos_copy = pos.copy()
|
||||
|
||||
self.assertEqual(pos, pos_copy)
|
||||
self.assertIsNot(pos, pos_copy) # Different objects
|
||||
|
||||
# Modify copy
|
||||
pos_copy.word_index = 10
|
||||
self.assertNotEqual(pos, pos_copy)
|
||||
|
||||
def test_position_equality_and_hashing(self):
|
||||
"""Test position equality and hashing"""
|
||||
pos1 = RenderingPosition(chapter_index=1, block_index=5)
|
||||
pos2 = RenderingPosition(chapter_index=1, block_index=5)
|
||||
pos3 = RenderingPosition(chapter_index=1, block_index=6)
|
||||
|
||||
self.assertEqual(pos1, pos2)
|
||||
self.assertNotEqual(pos1, pos3)
|
||||
|
||||
# Test hashing (for use as dict keys)
|
||||
pos_dict = {pos1: "test"}
|
||||
self.assertEqual(pos_dict[pos2], "test") # Should work due to equality
|
||||
|
||||
|
||||
class TestChapterNavigator(unittest.TestCase):
|
||||
"""Test the ChapterNavigator class"""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test data"""
|
||||
self.font = Font()
|
||||
|
||||
# Create test blocks with headings
|
||||
self.blocks = [
|
||||
Paragraph(self.font), # Block 0
|
||||
Heading(HeadingLevel.H1, self.font), # Block 1 - Chapter 1
|
||||
Paragraph(self.font), # Block 2
|
||||
Heading(HeadingLevel.H2, self.font), # Block 3 - Subsection
|
||||
Paragraph(self.font), # Block 4
|
||||
Heading(HeadingLevel.H1, self.font), # Block 5 - Chapter 2
|
||||
Paragraph(self.font), # Block 6
|
||||
]
|
||||
|
||||
# Add text to headings
|
||||
self.blocks[1].add_word(Word("Chapter", self.font))
|
||||
self.blocks[1].add_word(Word("One", self.font))
|
||||
|
||||
self.blocks[3].add_word(Word("Subsection", self.font))
|
||||
self.blocks[3].add_word(Word("A", self.font))
|
||||
|
||||
self.blocks[5].add_word(Word("Chapter", self.font))
|
||||
self.blocks[5].add_word(Word("Two", self.font))
|
||||
|
||||
def test_chapter_detection(self):
|
||||
"""Test that chapters are detected correctly"""
|
||||
navigator = ChapterNavigator(self.blocks)
|
||||
|
||||
self.assertEqual(len(navigator.chapters), 3) # 2 H1s + 1 H2
|
||||
|
||||
# Check chapter titles
|
||||
titles = [chapter.title for chapter in navigator.chapters]
|
||||
self.assertIn("Chapter One", titles)
|
||||
self.assertIn("Subsection A", titles)
|
||||
self.assertIn("Chapter Two", titles)
|
||||
|
||||
def test_table_of_contents(self):
|
||||
"""Test table of contents generation"""
|
||||
navigator = ChapterNavigator(self.blocks)
|
||||
toc = navigator.get_table_of_contents()
|
||||
|
||||
self.assertEqual(len(toc), 3)
|
||||
|
||||
# Check first entry
|
||||
title, level, position = toc[0]
|
||||
self.assertEqual(title, "Chapter One")
|
||||
self.assertEqual(level, HeadingLevel.H1)
|
||||
self.assertIsInstance(position, RenderingPosition)
|
||||
|
||||
def test_chapter_position_lookup(self):
|
||||
"""Test looking up chapter positions"""
|
||||
navigator = ChapterNavigator(self.blocks)
|
||||
|
||||
pos = navigator.get_chapter_position("Chapter One")
|
||||
self.assertIsNotNone(pos)
|
||||
self.assertEqual(pos.chapter_index, 0)
|
||||
|
||||
pos = navigator.get_chapter_position("Nonexistent Chapter")
|
||||
self.assertIsNone(pos)
|
||||
|
||||
def test_current_chapter_detection(self):
|
||||
"""Test detecting current chapter from position"""
|
||||
navigator = ChapterNavigator(self.blocks)
|
||||
|
||||
# Position in first chapter
|
||||
pos = RenderingPosition(chapter_index=0, block_index=2)
|
||||
chapter = navigator.get_current_chapter(pos)
|
||||
self.assertIsNotNone(chapter)
|
||||
self.assertEqual(chapter.title, "Chapter One")
|
||||
|
||||
|
||||
class TestFontScaler(unittest.TestCase):
|
||||
"""Test the FontScaler class"""
|
||||
|
||||
def test_font_scaling(self):
|
||||
"""Test font scaling functionality"""
|
||||
original_font = Font(font_size=12)
|
||||
|
||||
# Test no scaling
|
||||
scaled_font = FontScaler.scale_font(original_font, 1.0)
|
||||
self.assertEqual(scaled_font.font_size, 12)
|
||||
|
||||
# Test 2x scaling
|
||||
scaled_font = FontScaler.scale_font(original_font, 2.0)
|
||||
self.assertEqual(scaled_font.font_size, 24)
|
||||
|
||||
# Test 0.5x scaling
|
||||
scaled_font = FontScaler.scale_font(original_font, 0.5)
|
||||
self.assertEqual(scaled_font.font_size, 6)
|
||||
|
||||
# Test minimum size constraint
|
||||
scaled_font = FontScaler.scale_font(original_font, 0.01)
|
||||
self.assertGreaterEqual(scaled_font.font_size, 1)
|
||||
|
||||
def test_word_spacing_scaling(self):
|
||||
"""Test word spacing scaling"""
|
||||
original_spacing = (5, 15)
|
||||
|
||||
# Test no scaling
|
||||
scaled_spacing = FontScaler.scale_word_spacing(original_spacing, 1.0)
|
||||
self.assertEqual(scaled_spacing, (5, 15))
|
||||
|
||||
# Test 2x scaling
|
||||
scaled_spacing = FontScaler.scale_word_spacing(original_spacing, 2.0)
|
||||
self.assertEqual(scaled_spacing, (10, 30))
|
||||
|
||||
# Test minimum constraints
|
||||
scaled_spacing = FontScaler.scale_word_spacing(original_spacing, 0.1)
|
||||
self.assertGreaterEqual(scaled_spacing[0], 1)
|
||||
self.assertGreaterEqual(scaled_spacing[1], 2)
|
||||
|
||||
|
||||
class TestBookmarkManager(unittest.TestCase):
|
||||
"""Test the BookmarkManager class"""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test environment"""
|
||||
self.temp_dir = tempfile.mkdtemp()
|
||||
self.document_id = "test_document"
|
||||
self.bookmark_manager = BookmarkManager(self.document_id, self.temp_dir)
|
||||
|
||||
def tearDown(self):
|
||||
"""Clean up test environment"""
|
||||
shutil.rmtree(self.temp_dir)
|
||||
|
||||
def test_bookmark_operations(self):
|
||||
"""Test bookmark add/remove/get operations"""
|
||||
pos = RenderingPosition(chapter_index=1, block_index=5)
|
||||
|
||||
# Add bookmark
|
||||
self.bookmark_manager.add_bookmark("test_bookmark", pos)
|
||||
|
||||
# Get bookmark
|
||||
retrieved_pos = self.bookmark_manager.get_bookmark("test_bookmark")
|
||||
self.assertEqual(retrieved_pos, pos)
|
||||
|
||||
# List bookmarks
|
||||
bookmarks = self.bookmark_manager.list_bookmarks()
|
||||
self.assertEqual(len(bookmarks), 1)
|
||||
self.assertEqual(bookmarks[0][0], "test_bookmark")
|
||||
self.assertEqual(bookmarks[0][1], pos)
|
||||
|
||||
# Remove bookmark
|
||||
success = self.bookmark_manager.remove_bookmark("test_bookmark")
|
||||
self.assertTrue(success)
|
||||
|
||||
# Verify removal
|
||||
retrieved_pos = self.bookmark_manager.get_bookmark("test_bookmark")
|
||||
self.assertIsNone(retrieved_pos)
|
||||
|
||||
def test_reading_position_persistence(self):
|
||||
"""Test saving and loading reading position"""
|
||||
pos = RenderingPosition(chapter_index=2, block_index=10, word_index=5)
|
||||
|
||||
# Save position
|
||||
self.bookmark_manager.save_reading_position(pos)
|
||||
|
||||
# Create new manager instance (simulates app restart)
|
||||
new_manager = BookmarkManager(self.document_id, self.temp_dir)
|
||||
|
||||
# Load position
|
||||
loaded_pos = new_manager.load_reading_position()
|
||||
self.assertEqual(loaded_pos, pos)
|
||||
|
||||
def test_bookmark_persistence(self):
|
||||
"""Test that bookmarks persist across manager instances"""
|
||||
pos = RenderingPosition(chapter_index=1, block_index=5)
|
||||
|
||||
# Add bookmark
|
||||
self.bookmark_manager.add_bookmark("persistent_bookmark", pos)
|
||||
|
||||
# Create new manager instance
|
||||
new_manager = BookmarkManager(self.document_id, self.temp_dir)
|
||||
|
||||
# Verify bookmark exists
|
||||
retrieved_pos = new_manager.get_bookmark("persistent_bookmark")
|
||||
self.assertEqual(retrieved_pos, pos)
|
||||
|
||||
|
||||
class TestEreaderLayoutManager(unittest.TestCase):
|
||||
"""Test the complete EreaderLayoutManager"""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test data"""
|
||||
self.temp_dir = tempfile.mkdtemp()
|
||||
self.font = Font()
|
||||
|
||||
# Create test document with multiple paragraphs and headings
|
||||
self.blocks = []
|
||||
|
||||
# Add a heading
|
||||
heading = Heading(HeadingLevel.H1, self.font)
|
||||
heading.add_word(Word("Test", self.font))
|
||||
heading.add_word(Word("Chapter", self.font))
|
||||
self.blocks.append(heading)
|
||||
|
||||
# Add several paragraphs with multiple words
|
||||
for i in range(3):
|
||||
paragraph = Paragraph(self.font)
|
||||
for j in range(20): # 20 words per paragraph
|
||||
paragraph.add_word(Word(f"Word{i}_{j}", self.font))
|
||||
self.blocks.append(paragraph)
|
||||
|
||||
self.page_size = (400, 600)
|
||||
self.document_id = "test_document"
|
||||
|
||||
def tearDown(self):
|
||||
"""Clean up test environment"""
|
||||
shutil.rmtree(self.temp_dir)
|
||||
|
||||
def test_manager_initialization(self):
|
||||
"""Test ereader manager initialization"""
|
||||
# Change to temp directory for bookmarks
|
||||
original_cwd = Path.cwd()
|
||||
try:
|
||||
import os
|
||||
os.chdir(self.temp_dir)
|
||||
|
||||
manager = EreaderLayoutManager(
|
||||
self.blocks,
|
||||
self.page_size,
|
||||
self.document_id,
|
||||
bookmarks_dir=self.temp_dir
|
||||
)
|
||||
|
||||
self.assertEqual(manager.page_size, self.page_size)
|
||||
self.assertEqual(manager.document_id, self.document_id)
|
||||
self.assertEqual(manager.font_scale, 1.0)
|
||||
self.assertIsInstance(manager.current_position, RenderingPosition)
|
||||
|
||||
manager.shutdown()
|
||||
finally:
|
||||
os.chdir(original_cwd)
|
||||
|
||||
def test_font_scaling(self):
|
||||
"""Test font scaling functionality"""
|
||||
original_cwd = Path.cwd()
|
||||
try:
|
||||
import os
|
||||
os.chdir(self.temp_dir)
|
||||
|
||||
manager = EreaderLayoutManager(
|
||||
self.blocks,
|
||||
self.page_size,
|
||||
self.document_id,
|
||||
bookmarks_dir=self.temp_dir
|
||||
)
|
||||
|
||||
# Test initial scale
|
||||
self.assertEqual(manager.get_font_scale(), 1.0)
|
||||
|
||||
# Test scaling
|
||||
page = manager.set_font_scale(1.5)
|
||||
self.assertEqual(manager.get_font_scale(), 1.5)
|
||||
self.assertIsNotNone(page)
|
||||
|
||||
manager.shutdown()
|
||||
finally:
|
||||
os.chdir(original_cwd)
|
||||
|
||||
def test_table_of_contents(self):
|
||||
"""Test table of contents functionality"""
|
||||
original_cwd = Path.cwd()
|
||||
try:
|
||||
import os
|
||||
os.chdir(self.temp_dir)
|
||||
|
||||
manager = EreaderLayoutManager(
|
||||
self.blocks,
|
||||
self.page_size,
|
||||
self.document_id,
|
||||
bookmarks_dir=self.temp_dir
|
||||
)
|
||||
|
||||
toc = manager.get_table_of_contents()
|
||||
self.assertGreater(len(toc), 0)
|
||||
|
||||
# Check first entry
|
||||
title, level, position = toc[0]
|
||||
self.assertEqual(title, "Test Chapter")
|
||||
self.assertEqual(level, HeadingLevel.H1)
|
||||
|
||||
manager.shutdown()
|
||||
finally:
|
||||
os.chdir(original_cwd)
|
||||
|
||||
def test_bookmark_functionality(self):
|
||||
"""Test bookmark functionality"""
|
||||
manager = EreaderLayoutManager(
|
||||
self.blocks,
|
||||
self.page_size,
|
||||
self.document_id,
|
||||
bookmarks_dir=self.temp_dir
|
||||
)
|
||||
|
||||
# Add bookmark
|
||||
success = manager.add_bookmark("test_bookmark")
|
||||
self.assertTrue(success)
|
||||
|
||||
# List bookmarks
|
||||
bookmarks = manager.list_bookmarks()
|
||||
self.assertEqual(len(bookmarks), 1)
|
||||
self.assertEqual(bookmarks[0][0], "test_bookmark")
|
||||
|
||||
# Jump to bookmark (should work even though it's the same position)
|
||||
page = manager.jump_to_bookmark("test_bookmark")
|
||||
self.assertIsNotNone(page)
|
||||
|
||||
# Remove bookmark
|
||||
success = manager.remove_bookmark("test_bookmark")
|
||||
self.assertTrue(success)
|
||||
|
||||
manager.shutdown()
|
||||
|
||||
def test_progress_tracking(self):
|
||||
"""Test reading progress tracking"""
|
||||
original_cwd = Path.cwd()
|
||||
try:
|
||||
import os
|
||||
os.chdir(self.temp_dir)
|
||||
|
||||
manager = EreaderLayoutManager(
|
||||
self.blocks,
|
||||
self.page_size,
|
||||
self.document_id,
|
||||
bookmarks_dir=self.temp_dir
|
||||
)
|
||||
|
||||
# Initial progress should be 0
|
||||
progress = manager.get_reading_progress()
|
||||
self.assertGreaterEqual(progress, 0.0)
|
||||
self.assertLessEqual(progress, 1.0)
|
||||
|
||||
# Get position info
|
||||
info = manager.get_position_info()
|
||||
self.assertIn('position', info)
|
||||
self.assertIn('progress', info)
|
||||
self.assertIn('font_scale', info)
|
||||
|
||||
manager.shutdown()
|
||||
finally:
|
||||
os.chdir(original_cwd)
|
||||
|
||||
def test_convenience_function(self):
|
||||
"""Test the convenience function"""
|
||||
original_cwd = Path.cwd()
|
||||
try:
|
||||
import os
|
||||
os.chdir(self.temp_dir)
|
||||
|
||||
manager = create_ereader_manager(
|
||||
self.blocks,
|
||||
self.page_size,
|
||||
self.document_id,
|
||||
bookmarks_dir=self.temp_dir
|
||||
)
|
||||
|
||||
self.assertIsInstance(manager, EreaderLayoutManager)
|
||||
self.assertEqual(manager.page_size, self.page_size)
|
||||
|
||||
manager.shutdown()
|
||||
finally:
|
||||
os.chdir(original_cwd)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user