dreader-application/test_main_integration.py
Duncan Tourolle 01e79dfa4b
All checks were successful
Python CI / test (3.12) (push) Successful in 22m19s
Python CI / test (3.13) (push) Successful in 8m23s
Test appplication for offdevice testing
2025-11-09 17:47:34 +01:00

212 lines
6.6 KiB
Python

#!/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()