212 lines
6.6 KiB
Python
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()
|