#!/usr/bin/env python3 """ Quick integration test for the main application controller. This test verifies that all components integrate correctly without requiring a GUI. It uses a mock HAL to simulate display/input. """ import sys import asyncio from pathlib import Path from PIL import Image # Add parent directory to path sys.path.insert(0, str(Path(__file__).parent)) from dreader.main import DReaderApplication, AppConfig from dreader.hal import DisplayHAL from dreader.gesture import TouchEvent, GestureType class MockDisplayHAL(DisplayHAL): """Mock HAL for headless testing.""" def __init__(self): self.images_displayed = [] self.events = [] async def show_image(self, image: Image.Image): """Record displayed images.""" self.images_displayed.append(image) print(f" [HAL] Displayed image: {image.size}") async def get_touch_event(self): """Return queued events.""" if self.events: return self.events.pop(0) return None async def set_brightness(self, level: int): """Mock brightness control.""" print(f" [HAL] Brightness set to {level}") def queue_event(self, event: TouchEvent): """Add event to queue for testing.""" self.events.append(event) async def test_integration(): """Test the complete integration.""" print("=" * 70) print("DReader Main Application Integration Test") print("=" * 70) # Find test library library_path = Path(__file__).parent / "tests" / "data" / "library-epub" if not library_path.exists(): print(f"\nError: Test library not found at {library_path}") print("Please ensure test EPUB files exist in tests/data/library-epub/") return False print(f"\nLibrary path: {library_path}") # Create mock HAL hal = MockDisplayHAL() # Create config config = AppConfig( display_hal=hal, library_path=str(library_path), page_size=(800, 1200), auto_save_interval=999 # Don't auto-save during test ) # Create application app = DReaderApplication(config) try: # Test 1: Start application (should show library) print("\n" + "-" * 70) print("Test 1: Starting application") print("-" * 70) await app.start() print(f"✓ Application started") print(f" Current mode: {app.get_current_mode()}") print(f" Images displayed: {len(hal.images_displayed)}") assert len(hal.images_displayed) > 0, "No image displayed after start" assert app.get_current_mode().value == "library", "Should start in library mode" print("✓ Test 1 passed") # Test 2: Simulate tap on first book print("\n" + "-" * 70) print("Test 2: Selecting a book from library") print("-" * 70) # Tap in approximate location of first book tap_event = TouchEvent(GestureType.TAP, 400, 150) print(f" Simulating tap at (400, 150)") images_before = len(hal.images_displayed) await app.handle_touch(tap_event) print(f" Current mode: {app.get_current_mode()}") print(f" New images displayed: {len(hal.images_displayed) - images_before}") # Should have transitioned to reading mode if app.get_current_mode().value == "reading": print("✓ Successfully entered reading mode") print("✓ Test 2 passed") else: print("⚠ Tap may not have hit a book (this is OK for the test)") print(" Manually entering reading mode for further tests...") # Get first book from library and enter reading mode manually if app.library: books = app.library.scan_library() if books: await app._enter_reading_mode(books[0]['path']) print(f"✓ Loaded book: {books[0]['title']}") # Test 3: Page navigation print("\n" + "-" * 70) print("Test 3: Page navigation") print("-" * 70) if app.get_current_mode().value == "reading": images_before = len(hal.images_displayed) # Next page (swipe left) swipe_event = TouchEvent(GestureType.SWIPE_LEFT, 600, 600) print(" Simulating swipe left (next page)") await app.handle_touch(swipe_event) print(f" New images displayed: {len(hal.images_displayed) - images_before}") print("✓ Test 3 passed") else: print("⊘ Skipping (not in reading mode)") # Test 4: Font size change (pinch gesture) print("\n" + "-" * 70) print("Test 4: Font size adjustment") print("-" * 70) if app.get_current_mode().value == "reading": images_before = len(hal.images_displayed) # Increase font size pinch_event = TouchEvent(GestureType.PINCH_OUT, 400, 600) print(" Simulating pinch out (increase font)") await app.handle_touch(pinch_event) print(f" New images displayed: {len(hal.images_displayed) - images_before}") print("✓ Test 4 passed") else: print("⊘ Skipping (not in reading mode)") # Test 5: State persistence check print("\n" + "-" * 70) print("Test 5: State persistence") print("-" * 70) state = app.state print(f" Current mode: {state.mode}") print(f" Current book: {state.current_book}") print(f" Font scale: {state.settings.font_scale}") # Save state success = app.state_manager.save_state(force=True) if success: print("✓ State saved successfully") print("✓ Test 5 passed") else: print("✗ State save failed") return False # Test 6: Graceful shutdown print("\n" + "-" * 70) print("Test 6: Graceful shutdown") print("-" * 70) await app.shutdown() print("✓ Application shut down cleanly") print("✓ Test 6 passed") # Summary print("\n" + "=" * 70) print("✓ ALL TESTS PASSED") print("=" * 70) print(f"\nTotal images displayed: {len(hal.images_displayed)}") print(f"Final mode: {app.get_current_mode()}") print("\nIntegration test successful!") return True except Exception as e: print(f"\n✗ TEST FAILED: {e}") import traceback traceback.print_exc() return False def main(): """Main entry point.""" success = asyncio.run(test_integration()) sys.exit(0 if success else 1) if __name__ == "__main__": main()