""" 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()