fixed word highlighting issue, added test for examples
Some checks failed
Python CI / test (3.13) (push) Successful in 7m3s
Python CI / test (3.12) (push) Failing after 3h8m53s

This commit is contained in:
Duncan Tourolle 2025-11-10 18:31:02 +01:00
parent 4b4e9612a0
commit 5a573c901e
2 changed files with 229 additions and 2 deletions

View File

@ -18,8 +18,7 @@ This is useful for:
from PIL import Image, ImageDraw
import numpy as np
from dreader.application import EbookReader
from pyWebLayout.io.gesture import TouchEvent, GestureType
from dreader import EbookReader, TouchEvent, GestureType
from pyWebLayout.core.query import QueryResult

228
tests/test_examples.py Normal file
View File

@ -0,0 +1,228 @@
"""
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()