229 lines
8.6 KiB
Python
229 lines
8.6 KiB
Python
"""
|
|
Unit tests for example scripts.
|
|
|
|
This test suite validates that all example scripts:
|
|
1. Can be imported without errors (syntax checks, import validation)
|
|
2. Have valid import statements
|
|
3. Can run their main functions without crashing (when applicable)
|
|
|
|
This helps catch issues like:
|
|
- Incorrect import paths
|
|
- Missing dependencies
|
|
- API breakages that affect examples
|
|
"""
|
|
|
|
import unittest
|
|
import importlib.util
|
|
import sys
|
|
import os
|
|
from pathlib import Path
|
|
|
|
|
|
class TestExampleImports(unittest.TestCase):
|
|
"""Test that all example scripts can be imported successfully"""
|
|
|
|
def setUp(self):
|
|
"""Set up test fixtures"""
|
|
# Get the project root directory
|
|
self.project_root = Path(__file__).parent.parent
|
|
self.examples_dir = self.project_root / "examples"
|
|
|
|
# Add project root to Python path if not already there
|
|
if str(self.project_root) not in sys.path:
|
|
sys.path.insert(0, str(self.project_root))
|
|
|
|
def _import_module_from_file(self, file_path: Path):
|
|
"""
|
|
Import a Python module from a file path.
|
|
|
|
Args:
|
|
file_path: Path to the Python file
|
|
|
|
Returns:
|
|
The imported module
|
|
|
|
Raises:
|
|
Any import errors that occur
|
|
"""
|
|
spec = importlib.util.spec_from_file_location(file_path.stem, file_path)
|
|
if spec is None or spec.loader is None:
|
|
raise ImportError(f"Could not load spec for {file_path}")
|
|
|
|
module = importlib.util.module_from_spec(spec)
|
|
sys.modules[file_path.stem] = module
|
|
spec.loader.exec_module(module)
|
|
return module
|
|
|
|
def test_word_selection_highlighting_imports(self):
|
|
"""Test word_selection_highlighting.py can be imported"""
|
|
example_file = self.examples_dir / "word_selection_highlighting.py"
|
|
self.assertTrue(example_file.exists(), f"Example file not found: {example_file}")
|
|
|
|
try:
|
|
module = self._import_module_from_file(example_file)
|
|
|
|
# Verify key components are available
|
|
self.assertTrue(hasattr(module, 'draw_highlight'))
|
|
self.assertTrue(hasattr(module, 'example_1_single_word_selection'))
|
|
self.assertTrue(hasattr(module, 'example_2_range_selection'))
|
|
self.assertTrue(hasattr(module, 'example_3_interactive_word_lookup'))
|
|
self.assertTrue(hasattr(module, 'example_4_multi_word_annotation'))
|
|
self.assertTrue(hasattr(module, 'example_5_link_highlighting'))
|
|
|
|
except ImportError as e:
|
|
self.fail(f"Failed to import word_selection_highlighting.py: {e}")
|
|
|
|
def test_demo_pagination_imports(self):
|
|
"""Test demo_pagination.py can be imported"""
|
|
example_file = self.examples_dir / "demo_pagination.py"
|
|
self.assertTrue(example_file.exists(), f"Example file not found: {example_file}")
|
|
|
|
try:
|
|
module = self._import_module_from_file(example_file)
|
|
self.assertTrue(hasattr(module, 'main'))
|
|
except ImportError as e:
|
|
self.fail(f"Failed to import demo_pagination.py: {e}")
|
|
|
|
def test_demo_toc_overlay_imports(self):
|
|
"""Test demo_toc_overlay.py can be imported"""
|
|
example_file = self.examples_dir / "demo_toc_overlay.py"
|
|
self.assertTrue(example_file.exists(), f"Example file not found: {example_file}")
|
|
|
|
try:
|
|
module = self._import_module_from_file(example_file)
|
|
self.assertTrue(hasattr(module, 'main'))
|
|
except ImportError as e:
|
|
self.fail(f"Failed to import demo_toc_overlay.py: {e}")
|
|
|
|
def test_demo_settings_overlay_imports(self):
|
|
"""Test demo_settings_overlay.py can be imported"""
|
|
example_file = self.examples_dir / "demo_settings_overlay.py"
|
|
self.assertTrue(example_file.exists(), f"Example file not found: {example_file}")
|
|
|
|
try:
|
|
module = self._import_module_from_file(example_file)
|
|
self.assertTrue(hasattr(module, 'main'))
|
|
except ImportError as e:
|
|
self.fail(f"Failed to import demo_settings_overlay.py: {e}")
|
|
|
|
def test_library_reading_integration_imports(self):
|
|
"""Test library_reading_integration.py can be imported"""
|
|
example_file = self.examples_dir / "library_reading_integration.py"
|
|
self.assertTrue(example_file.exists(), f"Example file not found: {example_file}")
|
|
|
|
try:
|
|
module = self._import_module_from_file(example_file)
|
|
self.assertTrue(hasattr(module, 'main'))
|
|
self.assertTrue(hasattr(module, 'simulate_mode_transition_workflow'))
|
|
except ImportError as e:
|
|
self.fail(f"Failed to import library_reading_integration.py: {e}")
|
|
|
|
def test_all_examples_have_correct_dreader_imports(self):
|
|
"""
|
|
Verify all example scripts use correct import paths for dreader classes.
|
|
|
|
This test specifically checks that examples don't use outdated import paths
|
|
like 'from dreader.application import' when they should use 'from dreader import'.
|
|
"""
|
|
# Get all Python files in examples directory
|
|
example_files = list(self.examples_dir.glob("*.py"))
|
|
|
|
problematic_imports = []
|
|
|
|
for example_file in example_files:
|
|
# Skip __init__.py and other special files
|
|
if example_file.name.startswith('_'):
|
|
continue
|
|
|
|
with open(example_file, 'r') as f:
|
|
content = f.read()
|
|
|
|
# Check for problematic import patterns
|
|
if 'from pyWebLayout.io.gesture import' in content:
|
|
problematic_imports.append(
|
|
f"{example_file.name}: Uses 'from pyWebLayout.io.gesture import' "
|
|
f"(should be 'from dreader import')"
|
|
)
|
|
|
|
if 'from dreader.application import EbookReader' in content:
|
|
# This is acceptable, but check if TouchEvent/GestureType are also imported correctly
|
|
if 'from pyWebLayout.io.gesture import TouchEvent' in content:
|
|
problematic_imports.append(
|
|
f"{example_file.name}: Mixes dreader.application and pyWebLayout.io.gesture imports"
|
|
)
|
|
|
|
if problematic_imports:
|
|
self.fail(
|
|
"Found problematic imports in example files:\n" +
|
|
"\n".join(f" - {issue}" for issue in problematic_imports)
|
|
)
|
|
|
|
|
|
class TestExampleFunctions(unittest.TestCase):
|
|
"""Test key functionality in example scripts"""
|
|
|
|
def test_draw_highlight_function(self):
|
|
"""Test the draw_highlight function from word_selection_highlighting"""
|
|
from PIL import Image
|
|
|
|
# Import the module
|
|
project_root = Path(__file__).parent.parent
|
|
example_file = project_root / "examples" / "word_selection_highlighting.py"
|
|
|
|
spec = importlib.util.spec_from_file_location("word_selection_highlighting", example_file)
|
|
module = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(module)
|
|
|
|
# Create a test image
|
|
test_image = Image.new('RGBA', (100, 100), (255, 255, 255, 255))
|
|
|
|
# Test the draw_highlight function
|
|
bounds = (10, 10, 50, 20)
|
|
result = module.draw_highlight(test_image, bounds)
|
|
|
|
# Verify the result is an image
|
|
self.assertIsInstance(result, Image.Image)
|
|
self.assertEqual(result.size, (100, 100))
|
|
self.assertEqual(result.mode, 'RGBA')
|
|
|
|
|
|
class TestExampleDocumentation(unittest.TestCase):
|
|
"""Test that examples have proper documentation"""
|
|
|
|
def test_all_examples_have_docstrings(self):
|
|
"""Verify all example scripts have module docstrings"""
|
|
project_root = Path(__file__).parent.parent
|
|
examples_dir = project_root / "examples"
|
|
|
|
example_files = [
|
|
f for f in examples_dir.glob("*.py")
|
|
if not f.name.startswith('_') and f.name not in ['__init__.py']
|
|
]
|
|
|
|
missing_docstrings = []
|
|
|
|
for example_file in example_files:
|
|
spec = importlib.util.spec_from_file_location(example_file.stem, example_file)
|
|
if spec is None or spec.loader is None:
|
|
continue
|
|
|
|
module = importlib.util.module_from_spec(spec)
|
|
try:
|
|
spec.loader.exec_module(module)
|
|
|
|
if not module.__doc__ or len(module.__doc__.strip()) < 10:
|
|
missing_docstrings.append(example_file.name)
|
|
except:
|
|
# If module can't be loaded, skip docstring check
|
|
# (import test will catch the error)
|
|
pass
|
|
|
|
if missing_docstrings:
|
|
self.fail(
|
|
f"Examples missing proper docstrings: {', '.join(missing_docstrings)}"
|
|
)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|