improved library screen, fixed issues with image rendering and navigation
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
"""
|
||||
Minimal reproduction test for backward navigation bug.
|
||||
|
||||
BUG: Backward navigation cannot reach block_index=0 from block_index=1.
|
||||
|
||||
This is a pyWebLayout issue, not a dreader-application issue.
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import tempfile
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
|
||||
from dreader.application import EbookReader
|
||||
|
||||
|
||||
class TestBackwardNavigationBug(unittest.TestCase):
|
||||
"""Minimal reproduction of backward navigation bug"""
|
||||
|
||||
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_minimal_backward_navigation_bug(self):
|
||||
"""
|
||||
MINIMAL REPRODUCTION:
|
||||
|
||||
1. Start at block_index=0
|
||||
2. Go forward once (to block_index=1)
|
||||
3. Go backward once
|
||||
4. BUG: Lands at block_index=1 instead of block_index=0
|
||||
|
||||
This proves backward navigation cannot reach the first block.
|
||||
"""
|
||||
reader = EbookReader(
|
||||
page_size=(800, 1000),
|
||||
bookmarks_dir=self.temp_dir,
|
||||
buffer_size=0
|
||||
)
|
||||
|
||||
reader.load_epub(self.epub_path)
|
||||
|
||||
# Starting position
|
||||
pos_start = reader.manager.current_position.copy()
|
||||
print(f"\n1. Starting at block_index={pos_start.block_index}")
|
||||
self.assertEqual(pos_start.block_index, 0, "Should start at block 0")
|
||||
|
||||
# Go forward
|
||||
reader.next_page()
|
||||
pos_forward = reader.manager.current_position.copy()
|
||||
print(f"2. After next_page(): block_index={pos_forward.block_index}")
|
||||
self.assertEqual(pos_forward.block_index, 1, "Should be at block 1")
|
||||
|
||||
# Go backward
|
||||
reader.previous_page()
|
||||
pos_final = reader.manager.current_position.copy()
|
||||
print(f"3. After previous_page(): block_index={pos_final.block_index}")
|
||||
|
||||
# THE BUG: This assertion will fail
|
||||
print(f"\nEXPECTED: block_index=0")
|
||||
print(f"ACTUAL: block_index={pos_final.block_index}")
|
||||
|
||||
if pos_final.block_index != 0:
|
||||
print("\n❌ BUG CONFIRMED: Cannot navigate backward to block_index=0")
|
||||
print(" This is a pyWebLayout bug in the previous_page() method.")
|
||||
|
||||
self.assertEqual(
|
||||
pos_final.block_index,
|
||||
0,
|
||||
"BUG: Backward navigation from block 1 should return to block 0"
|
||||
)
|
||||
|
||||
reader.close()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -0,0 +1,239 @@
|
||||
"""
|
||||
Detailed test for backward navigation issues.
|
||||
|
||||
This test explores the backward navigation behavior more thoroughly
|
||||
to understand if the issue is:
|
||||
1. Complete failure (previous_page returns None)
|
||||
2. Imprecise positioning (lands on wrong block)
|
||||
3. Only occurs after resume
|
||||
4. Occurs during continuous navigation
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import tempfile
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
|
||||
from dreader.application import EbookReader
|
||||
|
||||
|
||||
class TestBackwardNavigationDetailed(unittest.TestCase):
|
||||
"""Detailed backward navigation 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 test_continuous_backward_navigation_no_resume(self):
|
||||
"""
|
||||
Test backward navigation without closing/resuming.
|
||||
This checks if the issue is specific to resume or general.
|
||||
"""
|
||||
reader = EbookReader(
|
||||
page_size=(800, 1000),
|
||||
bookmarks_dir=self.temp_dir,
|
||||
buffer_size=0
|
||||
)
|
||||
|
||||
reader.load_epub(self.epub_path)
|
||||
|
||||
print("\n=== Test: Continuous backward navigation (no resume) ===")
|
||||
|
||||
# Record starting position
|
||||
pos0 = reader.manager.current_position.copy()
|
||||
print(f"Starting position: {pos0}")
|
||||
|
||||
# Go forward 5 pages, recording positions
|
||||
forward_positions = [pos0]
|
||||
for i in range(5):
|
||||
page = reader.next_page()
|
||||
if page is None:
|
||||
print(f"Reached end at page {i}")
|
||||
break
|
||||
pos = reader.manager.current_position.copy()
|
||||
forward_positions.append(pos)
|
||||
print(f"Forward page {i+1}: block_index={pos.block_index}")
|
||||
|
||||
num_forward = len(forward_positions) - 1
|
||||
print(f"\nNavigated forward {num_forward} pages")
|
||||
|
||||
# Now go backward the same number of times
|
||||
print("\n--- Going backward ---")
|
||||
backward_positions = []
|
||||
for i in range(num_forward):
|
||||
page = reader.previous_page()
|
||||
|
||||
if page is None:
|
||||
print(f"ERROR: previous_page() returned None at step {i+1}")
|
||||
self.fail(f"Backward navigation failed at step {i+1}")
|
||||
|
||||
pos = reader.manager.current_position.copy()
|
||||
backward_positions.append(pos)
|
||||
print(f"Backward step {i+1}: block_index={pos.block_index}")
|
||||
|
||||
# Check final position
|
||||
final_pos = reader.manager.current_position.copy()
|
||||
print(f"\nFinal position: {final_pos}")
|
||||
print(f"Expected (pos0): {pos0}")
|
||||
|
||||
if final_pos != pos0:
|
||||
print(f"WARNING: Position mismatch!")
|
||||
print(f" Expected block_index: {pos0.block_index}")
|
||||
print(f" Actual block_index: {final_pos.block_index}")
|
||||
print(f" Difference: {final_pos.block_index - pos0.block_index} blocks")
|
||||
|
||||
self.assertEqual(
|
||||
final_pos,
|
||||
pos0,
|
||||
f"After {num_forward} forward and {num_forward} backward, should be at start"
|
||||
)
|
||||
|
||||
reader.close()
|
||||
|
||||
def test_backward_navigation_at_start(self):
|
||||
"""
|
||||
Test that previous_page() behaves correctly when at the start of the book.
|
||||
"""
|
||||
reader = EbookReader(
|
||||
page_size=(800, 1000),
|
||||
bookmarks_dir=self.temp_dir,
|
||||
buffer_size=0
|
||||
)
|
||||
|
||||
reader.load_epub(self.epub_path)
|
||||
|
||||
print("\n=== Test: Backward navigation at start ===")
|
||||
|
||||
pos_start = reader.manager.current_position.copy()
|
||||
print(f"At start: {pos_start}")
|
||||
|
||||
# Try to go back from the very first page
|
||||
page = reader.previous_page()
|
||||
|
||||
print(f"previous_page() returned: {page is not None}")
|
||||
|
||||
pos_after = reader.manager.current_position.copy()
|
||||
print(f"Position after previous_page(): {pos_after}")
|
||||
|
||||
# Should either return None or stay at same position
|
||||
if page is not None:
|
||||
self.assertEqual(
|
||||
pos_after,
|
||||
pos_start,
|
||||
"If previous_page() returns a page at start, position should not change"
|
||||
)
|
||||
|
||||
reader.close()
|
||||
|
||||
def test_alternating_navigation(self):
|
||||
"""
|
||||
Test alternating forward/backward navigation.
|
||||
"""
|
||||
reader = EbookReader(
|
||||
page_size=(800, 1000),
|
||||
bookmarks_dir=self.temp_dir,
|
||||
buffer_size=0
|
||||
)
|
||||
|
||||
reader.load_epub(self.epub_path)
|
||||
|
||||
print("\n=== Test: Alternating forward/backward navigation ===")
|
||||
|
||||
pos0 = reader.manager.current_position.copy()
|
||||
print(f"Start: block_index={pos0.block_index}")
|
||||
|
||||
# Go forward, back, forward, back pattern
|
||||
operations = [
|
||||
("forward", 1),
|
||||
("backward", 1),
|
||||
("forward", 2),
|
||||
("backward", 1),
|
||||
("forward", 1),
|
||||
("backward", 2),
|
||||
]
|
||||
|
||||
for op, count in operations:
|
||||
for i in range(count):
|
||||
if op == "forward":
|
||||
page = reader.next_page()
|
||||
else:
|
||||
page = reader.previous_page()
|
||||
|
||||
self.assertIsNotNone(
|
||||
page,
|
||||
f"{op} navigation failed at iteration {i+1}"
|
||||
)
|
||||
|
||||
pos = reader.manager.current_position.copy()
|
||||
print(f"After {count}x {op}: block_index={pos.block_index}")
|
||||
|
||||
# We should end up at the starting position (net: +5 -4 = +1, then +1 -2 = -1, total = 0)
|
||||
# Actually: +1 -1 +2 -1 +1 -2 = 0
|
||||
final_pos = reader.manager.current_position.copy()
|
||||
print(f"\nFinal: block_index={final_pos.block_index}")
|
||||
print(f"Expected: block_index={pos0.block_index}")
|
||||
|
||||
self.assertEqual(
|
||||
final_pos,
|
||||
pos0,
|
||||
"Alternating navigation should return to start"
|
||||
)
|
||||
|
||||
reader.close()
|
||||
|
||||
def test_backward_then_forward(self):
|
||||
"""
|
||||
Test that forward navigation works correctly after backward navigation.
|
||||
"""
|
||||
reader = EbookReader(
|
||||
page_size=(800, 1000),
|
||||
bookmarks_dir=self.temp_dir,
|
||||
buffer_size=0
|
||||
)
|
||||
|
||||
reader.load_epub(self.epub_path)
|
||||
|
||||
print("\n=== Test: Backward then forward ===")
|
||||
|
||||
# Go forward 3 pages
|
||||
positions = [reader.manager.current_position.copy()]
|
||||
for i in range(3):
|
||||
reader.next_page()
|
||||
positions.append(reader.manager.current_position.copy())
|
||||
|
||||
print(f"Forward positions: {[p.block_index for p in positions]}")
|
||||
|
||||
# Go back 3 pages
|
||||
for i in range(3):
|
||||
reader.previous_page()
|
||||
|
||||
pos_after_back = reader.manager.current_position.copy()
|
||||
print(f"After going back: block_index={pos_after_back.block_index}")
|
||||
|
||||
# Now go forward 3 pages again
|
||||
for i in range(3):
|
||||
reader.next_page()
|
||||
|
||||
final_pos = reader.manager.current_position.copy()
|
||||
print(f"After going forward again: block_index={final_pos.block_index}")
|
||||
print(f"Expected: block_index={positions[3].block_index}")
|
||||
|
||||
self.assertEqual(
|
||||
final_pos,
|
||||
positions[3],
|
||||
"Forward after backward should reach same position"
|
||||
)
|
||||
|
||||
reader.close()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -0,0 +1,230 @@
|
||||
"""
|
||||
Test backward navigation after resuming from a saved position.
|
||||
|
||||
This test specifically checks if backward navigation works correctly
|
||||
after opening an epub, navigating forward, closing it, then resuming
|
||||
and attempting to navigate backward.
|
||||
|
||||
This may reveal issues with pyWebLayout's backward navigation handling.
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import tempfile
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
|
||||
from dreader.application import EbookReader
|
||||
|
||||
|
||||
class TestBackwardNavigationAfterResume(unittest.TestCase):
|
||||
"""Test backward navigation behavior after resume"""
|
||||
|
||||
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_backward_navigation_after_resume(self):
|
||||
"""
|
||||
Test that backward navigation works after closing and resuming.
|
||||
|
||||
Steps:
|
||||
1. Open EPUB
|
||||
2. Navigate forward 3 pages
|
||||
3. Save positions and pages
|
||||
4. Close reader
|
||||
5. Open new reader (resume)
|
||||
6. Try to navigate backward
|
||||
7. Verify we can reach previous pages
|
||||
"""
|
||||
# Phase 1: Initial session - navigate forward
|
||||
reader1 = EbookReader(
|
||||
page_size=(800, 1000),
|
||||
bookmarks_dir=self.temp_dir,
|
||||
buffer_size=0 # Disable buffering for consistent testing
|
||||
)
|
||||
|
||||
success = reader1.load_epub(self.epub_path)
|
||||
self.assertTrue(success, "Failed to load test EPUB")
|
||||
|
||||
# Capture initial page
|
||||
page0 = reader1.get_current_page()
|
||||
self.assertIsNotNone(page0, "Initial page should not be None")
|
||||
pos0 = reader1.manager.current_position.copy()
|
||||
|
||||
print(f"\nInitial position: {pos0}")
|
||||
|
||||
# Navigate forward 3 pages, capturing each page
|
||||
pages = [page0]
|
||||
positions = [pos0]
|
||||
|
||||
for i in range(3):
|
||||
page = reader1.next_page()
|
||||
self.assertIsNotNone(page, f"Page {i+1} should not be None")
|
||||
pages.append(page)
|
||||
positions.append(reader1.manager.current_position.copy())
|
||||
print(f"Forward page {i+1} position: {positions[-1]}")
|
||||
|
||||
# We should now be at page 3 (0-indexed)
|
||||
self.assertEqual(len(pages), 4, "Should have 4 pages total (0-3)")
|
||||
|
||||
# Save the current position before closing
|
||||
final_position = reader1.manager.current_position.copy()
|
||||
print(f"Final position before close: {final_position}")
|
||||
|
||||
# Close reader (this should save the position)
|
||||
reader1.close()
|
||||
|
||||
# Phase 2: Resume session - navigate backward
|
||||
reader2 = EbookReader(
|
||||
page_size=(800, 1000),
|
||||
bookmarks_dir=self.temp_dir,
|
||||
buffer_size=0
|
||||
)
|
||||
|
||||
success = reader2.load_epub(self.epub_path)
|
||||
self.assertTrue(success, "Failed to load test EPUB on resume")
|
||||
|
||||
# Verify we resumed at the correct position
|
||||
resumed_position = reader2.manager.current_position.copy()
|
||||
print(f"Resumed at position: {resumed_position}")
|
||||
self.assertEqual(
|
||||
resumed_position,
|
||||
final_position,
|
||||
"Should resume at the last saved position"
|
||||
)
|
||||
|
||||
# Get the current page (should match page 3)
|
||||
resumed_page = reader2.get_current_page()
|
||||
self.assertIsNotNone(resumed_page, "Resumed page should not be None")
|
||||
|
||||
# Now try to navigate backward
|
||||
print("\nAttempting backward navigation...")
|
||||
|
||||
backward_pages = []
|
||||
backward_positions = []
|
||||
|
||||
# Try to go back 3 times
|
||||
for i in range(3):
|
||||
prev_page = reader2.previous_page()
|
||||
print(f"Backward step {i+1}: page={'Not None' if prev_page else 'None'}")
|
||||
|
||||
if prev_page is None:
|
||||
print(f"WARNING: previous_page() returned None at step {i+1}")
|
||||
# This is the bug we're testing for!
|
||||
self.fail(f"Backward navigation failed at step {i+1}: previous_page() returned None")
|
||||
|
||||
backward_pages.append(prev_page)
|
||||
backward_positions.append(reader2.manager.current_position.copy())
|
||||
print(f" Position after backward: {backward_positions[-1]}")
|
||||
|
||||
# We should have successfully gone back 3 pages
|
||||
self.assertEqual(len(backward_pages), 3, "Should have navigated back 3 pages")
|
||||
|
||||
# Verify final position matches original position
|
||||
final_backward_position = reader2.manager.current_position.copy()
|
||||
print(f"\nFinal position after backward navigation: {final_backward_position}")
|
||||
print(f"Original position (page 0): {pos0}")
|
||||
|
||||
self.assertEqual(
|
||||
final_backward_position,
|
||||
pos0,
|
||||
"After going forward 3 and back 3, should be at initial position"
|
||||
)
|
||||
|
||||
# Verify the page content matches
|
||||
final_page = reader2.get_current_page()
|
||||
self.assertTrue(
|
||||
self.compare_images(page0, final_page),
|
||||
"Final page should match initial page after forward/backward navigation"
|
||||
)
|
||||
|
||||
reader2.close()
|
||||
|
||||
print("\n✓ Test passed: Backward navigation works correctly after resume")
|
||||
|
||||
def test_backward_navigation_single_step(self):
|
||||
"""
|
||||
Simplified test: Open, go forward 1 page, close, resume, go back 1 page.
|
||||
This is a minimal reproduction case.
|
||||
"""
|
||||
# Session 1: Navigate forward one page
|
||||
reader1 = EbookReader(
|
||||
page_size=(800, 1000),
|
||||
bookmarks_dir=self.temp_dir,
|
||||
buffer_size=0
|
||||
)
|
||||
|
||||
reader1.load_epub(self.epub_path)
|
||||
|
||||
page0 = reader1.get_current_page()
|
||||
pos0 = reader1.manager.current_position.copy()
|
||||
|
||||
page1 = reader1.next_page()
|
||||
self.assertIsNotNone(page1, "Should be able to navigate forward")
|
||||
pos1 = reader1.manager.current_position.copy()
|
||||
|
||||
reader1.close()
|
||||
|
||||
# Session 2: Resume and navigate backward
|
||||
reader2 = EbookReader(
|
||||
page_size=(800, 1000),
|
||||
bookmarks_dir=self.temp_dir,
|
||||
buffer_size=0
|
||||
)
|
||||
|
||||
reader2.load_epub(self.epub_path)
|
||||
|
||||
# Verify we're at page 1
|
||||
self.assertEqual(
|
||||
reader2.manager.current_position,
|
||||
pos1,
|
||||
"Should resume at page 1"
|
||||
)
|
||||
|
||||
# Try to go back
|
||||
prev_page = reader2.previous_page()
|
||||
|
||||
# This is the critical assertion - if this fails, backward nav is broken
|
||||
self.assertIsNotNone(
|
||||
prev_page,
|
||||
"CRITICAL: previous_page() returned None after resume - this indicates a pyWebLayout bug"
|
||||
)
|
||||
|
||||
# Verify we're back at page 0
|
||||
final_pos = reader2.manager.current_position.copy()
|
||||
self.assertEqual(
|
||||
final_pos,
|
||||
pos0,
|
||||
"Should be back at initial position"
|
||||
)
|
||||
|
||||
reader2.close()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -0,0 +1,132 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Test that images render correctly in EPUB files.
|
||||
|
||||
This test verifies that:
|
||||
1. All images in the EPUB are loaded with correct dimensions
|
||||
2. Images can be navigated to without errors
|
||||
3. Pages with images render successfully
|
||||
4. The rendered pages contain actual image content (not blank)
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from dreader.application import EbookReader
|
||||
from pyWebLayout.abstract.block import Image as AbstractImage
|
||||
from PIL import Image
|
||||
import numpy as np
|
||||
|
||||
|
||||
def test_epub_images():
|
||||
"""Test that EPUB images render correctly."""
|
||||
|
||||
# Create reader
|
||||
reader = EbookReader(page_size=(800, 1200))
|
||||
|
||||
# Load EPUB
|
||||
epub_path = "tests/data/library-epub/pg11-images-3.epub"
|
||||
success = reader.load_epub(epub_path)
|
||||
|
||||
assert success, "Failed to load EPUB"
|
||||
assert reader.book_title == "Alice's Adventures in Wonderland"
|
||||
|
||||
# Check that images were parsed
|
||||
images = [b for b in reader.blocks if isinstance(b, AbstractImage)]
|
||||
assert len(images) >= 1, f"Expected at least 1 image, found {len(images)}"
|
||||
|
||||
# Check that all images have dimensions set
|
||||
for img in images:
|
||||
assert img.width is not None, f"Image {img.source} has no width"
|
||||
assert img.height is not None, f"Image {img.source} has no height"
|
||||
assert img.width > 0, f"Image {img.source} has invalid width: {img.width}"
|
||||
assert img.height > 0, f"Image {img.source} has invalid height: {img.height}"
|
||||
|
||||
# Check that image is loaded into memory
|
||||
assert hasattr(img, '_loaded_image'), f"Image {img.source} not loaded"
|
||||
assert img._loaded_image is not None, f"Image {img.source} _loaded_image is None"
|
||||
|
||||
# Test navigation through first 15 pages (which should include all images)
|
||||
for page_num in range(15):
|
||||
page_img = reader.get_current_page()
|
||||
|
||||
assert page_img is not None, f"Page {page_num + 1} failed to render"
|
||||
assert isinstance(page_img, Image.Image), f"Page {page_num + 1} is not a PIL Image"
|
||||
assert page_img.size == (800, 1200), f"Page {page_num + 1} has wrong size: {page_img.size}"
|
||||
|
||||
# Check that page has some non-white content
|
||||
arr = np.array(page_img.convert('RGB'))
|
||||
non_white_pixels = np.sum(arr < 255)
|
||||
|
||||
assert non_white_pixels > 100, f"Page {page_num + 1} appears to be blank (only {non_white_pixels} non-white pixels)"
|
||||
|
||||
# Navigate to next page
|
||||
if page_num < 14:
|
||||
next_result = reader.next_page()
|
||||
if next_result is None:
|
||||
# It's OK to reach end of book early
|
||||
break
|
||||
|
||||
|
||||
def test_cover_image():
|
||||
"""Specifically test that the cover image renders."""
|
||||
|
||||
reader = EbookReader(page_size=(800, 1200))
|
||||
reader.load_epub("tests/data/library-epub/pg11-images-3.epub")
|
||||
|
||||
# The first page should have the cover image
|
||||
page_img = reader.get_current_page()
|
||||
assert page_img is not None, "Cover page failed to render"
|
||||
|
||||
# Save for visual inspection
|
||||
output_path = "/tmp/epub_cover_test.png"
|
||||
page_img.save(output_path)
|
||||
|
||||
# Check that it has significant content (the cover image)
|
||||
arr = np.array(page_img.convert('RGB'))
|
||||
non_white_pixels = np.sum(arr < 255)
|
||||
|
||||
# The cover page should have substantial content
|
||||
assert non_white_pixels > 10000, f"Cover page has too few non-white pixels: {non_white_pixels}"
|
||||
|
||||
|
||||
def test_multiple_epub_images():
|
||||
"""Test images across multiple EPUB files."""
|
||||
|
||||
epub_files = [
|
||||
("tests/data/library-epub/pg11-images-3.epub", "Alice's Adventures in Wonderland"),
|
||||
("tests/data/library-epub/pg16328-images-3.epub", "Beowulf: An Anglo-Saxon Epic Poem"),
|
||||
("tests/data/library-epub/pg5200-images-3.epub", "Metamorphosis"),
|
||||
]
|
||||
|
||||
for epub_path, expected_title in epub_files:
|
||||
reader = EbookReader(page_size=(800, 1200))
|
||||
success = reader.load_epub(epub_path)
|
||||
|
||||
assert success, f"Failed to load {epub_path}"
|
||||
assert reader.book_title == expected_title
|
||||
|
||||
# Check that at least one image exists
|
||||
images = [b for b in reader.blocks if isinstance(b, AbstractImage)]
|
||||
assert len(images) >= 1, f"{epub_path} should have at least 1 image"
|
||||
|
||||
# Check first image is valid
|
||||
img = images[0]
|
||||
assert img.width > 0 and img.height > 0, f"Invalid dimensions in {epub_path}"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Run tests directly
|
||||
print("Testing EPUB images...")
|
||||
|
||||
print("\n1. Testing all images load and render...")
|
||||
test_epub_images()
|
||||
print("✓ PASSED")
|
||||
|
||||
print("\n2. Testing cover image...")
|
||||
test_cover_image()
|
||||
print("✓ PASSED")
|
||||
|
||||
print("\n3. Testing multiple EPUB images...")
|
||||
test_multiple_epub_images()
|
||||
print("✓ PASSED")
|
||||
|
||||
print("\n✓ All tests passed!")
|
||||
@@ -7,6 +7,7 @@ and verify that tap detection works correctly.
|
||||
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
from unittest.mock import MagicMock
|
||||
from dreader import LibraryManager
|
||||
|
||||
|
||||
@@ -46,9 +47,12 @@ class TestLibraryInteraction(unittest.TestCase):
|
||||
# Table should exist
|
||||
self.assertIsNotNone(table)
|
||||
|
||||
# Table should have body rows matching book count
|
||||
# Table should have body rows for 2-column grid layout
|
||||
# Each pair of books gets 2 rows (cover row + detail row)
|
||||
# So N books = ceil(N/2) * 2 rows
|
||||
body_rows = list(table.body_rows())
|
||||
self.assertEqual(len(body_rows), len(books))
|
||||
expected_rows = ((len(books) + 1) // 2) * 2 # Round up to nearest even number, then double
|
||||
self.assertEqual(len(body_rows), expected_rows)
|
||||
|
||||
def test_library_rendering(self):
|
||||
"""Test that library can be rendered to image"""
|
||||
@@ -136,7 +140,7 @@ class TestLibraryInteraction(unittest.TestCase):
|
||||
self.assertIsNone(selected_path, "Tap below last book should not select anything")
|
||||
|
||||
def test_multiple_taps(self):
|
||||
"""Test that multiple taps work correctly"""
|
||||
"""Test that multiple taps work correctly with 2-column grid layout"""
|
||||
books = self.library.scan_library()
|
||||
|
||||
if len(books) < 3:
|
||||
@@ -145,16 +149,20 @@ class TestLibraryInteraction(unittest.TestCase):
|
||||
self.library.create_library_table()
|
||||
self.library.render_library()
|
||||
|
||||
# Tap first book (row 0: y=60-180)
|
||||
# In 2-column layout:
|
||||
# Books 0 and 1 are in the first pair (rows 0-1: cover and detail)
|
||||
# Books 2 and 3 are in the second pair (rows 2-3: cover and detail)
|
||||
|
||||
# Tap first book (left column, first pair cover row)
|
||||
path1 = self.library.handle_library_tap(x=100, y=100)
|
||||
self.assertEqual(path1, books[0]['path'])
|
||||
|
||||
# Tap second book (row 1: y=181-301)
|
||||
path2 = self.library.handle_library_tap(x=400, y=250)
|
||||
# Tap second book (right column, first pair cover row)
|
||||
path2 = self.library.handle_library_tap(x=500, y=100)
|
||||
self.assertEqual(path2, books[1]['path'])
|
||||
|
||||
# Tap third book (row 2: y=302-422)
|
||||
path3 = self.library.handle_library_tap(x=400, y=360)
|
||||
# Tap third book (left column, second pair cover row)
|
||||
path3 = self.library.handle_library_tap(x=100, y=360)
|
||||
self.assertEqual(path3, books[2]['path'])
|
||||
|
||||
# All should be different
|
||||
@@ -162,6 +170,79 @@ class TestLibraryInteraction(unittest.TestCase):
|
||||
self.assertNotEqual(path2, path3)
|
||||
self.assertNotEqual(path1, path3)
|
||||
|
||||
def test_pagination(self):
|
||||
"""Test library pagination with fake book data"""
|
||||
# Create fake books (20 books to ensure multiple pages)
|
||||
fake_books = []
|
||||
for i in range(20):
|
||||
fake_books.append({
|
||||
'path': f'/fake/path/book_{i}.epub',
|
||||
'title': f'Book Title {i}',
|
||||
'author': f'Author {i}',
|
||||
'filename': f'book_{i}.epub',
|
||||
'cover_data': None,
|
||||
'cover_path': None
|
||||
})
|
||||
|
||||
# Create library with 6 books per page
|
||||
library = LibraryManager(
|
||||
library_path=str(self.library_path),
|
||||
page_size=(800, 1200),
|
||||
books_per_page=6
|
||||
)
|
||||
library.books = fake_books
|
||||
|
||||
# Test initial state
|
||||
self.assertEqual(library.current_page, 0)
|
||||
self.assertEqual(library.get_total_pages(), 4) # 20 books / 6 per page = 4 pages
|
||||
|
||||
# Test creating table for first page
|
||||
table = library.create_library_table()
|
||||
self.assertIsNotNone(table)
|
||||
# 6 books = 3 pairs = 6 rows (3 cover rows + 3 detail rows)
|
||||
body_rows = list(table.body_rows())
|
||||
self.assertEqual(len(body_rows), 6)
|
||||
|
||||
# Test navigation to next page
|
||||
self.assertTrue(library.next_page())
|
||||
self.assertEqual(library.current_page, 1)
|
||||
|
||||
# Create table for second page
|
||||
table = library.create_library_table()
|
||||
body_rows = list(table.body_rows())
|
||||
self.assertEqual(len(body_rows), 6) # Still 6 books on page 2
|
||||
|
||||
# Test navigation to last page
|
||||
library.set_page(3)
|
||||
self.assertEqual(library.current_page, 3)
|
||||
table = library.create_library_table()
|
||||
body_rows = list(table.body_rows())
|
||||
# Page 4 has 2 books (20 - 18 = 2) = 1 pair = 2 rows
|
||||
self.assertEqual(len(body_rows), 2)
|
||||
|
||||
# Test can't go beyond last page
|
||||
self.assertFalse(library.next_page())
|
||||
self.assertEqual(library.current_page, 3)
|
||||
|
||||
# Test navigation to previous page
|
||||
self.assertTrue(library.previous_page())
|
||||
self.assertEqual(library.current_page, 2)
|
||||
|
||||
# Test navigation to first page
|
||||
library.set_page(0)
|
||||
self.assertEqual(library.current_page, 0)
|
||||
|
||||
# Test can't go before first page
|
||||
self.assertFalse(library.previous_page())
|
||||
self.assertEqual(library.current_page, 0)
|
||||
|
||||
# Test invalid page number
|
||||
self.assertFalse(library.set_page(-1))
|
||||
self.assertFalse(library.set_page(100))
|
||||
self.assertEqual(library.current_page, 0) # Should stay on current page
|
||||
|
||||
library.cleanup()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user