Files
dreader-application/tests/test_backward_navigation_detailed.py
T
dtourolleandClaude Opus 5 f7d59d025f chore: tidy repository layout and consolidate hardware docs
Root had grown to 30 entries, most of it generated output and one-off
scripts. It now holds 12.

Docs:
- Merge HARDWARE_SETUP, HARDWARE_PINOUT, GPIO_BUTTONS and
  ACCELEROMETER_PAGE_FLIP into a single docs/HARDWARE.md
- Move ARCHITECTURE, REQUIREMENTS and HAL_IMPLEMENTATION_SPEC to docs/,
  leaving only README.md in the root
- Update cross-references in README, setup_rpi.py and
  install_hardware_drivers.sh, and re-base ARCHITECTURE's source links

Merging surfaced three errors, reconciled against hardware_config.json:
- The power button was documented as GPIO 21 to GND with a pull-up. It is
  active high (pull_up: false); wiring it as documented reads as
  permanently pressed.
- GPIO_BUTTONS used GPIO 23 for next-page; it is GPIO 27.
- The FT5316 INT pin was routed to GPIO 27, which collides with the
  next-page button. Now documented as a conflict.

Scripts:
- Move debug_overlay_links.py and debug_previous_page.py to scripts/debug/
- Rename test_pagination_visual.py to scripts/debug/visualize_pagination.py;
  it renders output and asserts nothing, so the test_ prefix was misleading
- Fix the __file__-relative paths these three relied on
- Track update_pyweblayout.sh under scripts/

Tests:
- Reword the backward-navigation tests, which described a pyWebLayout bug
  that is now fixed, as regression tests

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 22:27:53 +02:00

241 lines
7.4 KiB
Python

"""
Detailed regression tests for backward navigation.
These cover the ways backward navigation has broken before:
1. Complete failure (previous_page returns None)
2. Imprecise positioning (lands on wrong block)
3. Failure only after resuming from a saved position
4. Failure during continuous navigation
See test_backward_nav_minimal.py for the minimal cover-page case.
"""
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()