204 lines
8.4 KiB
Python
204 lines
8.4 KiB
Python
"""
|
|
Integration tests for the complete text generation pipeline.
|
|
Tests how the core modules work together.
|
|
"""
|
|
|
|
import pytest
|
|
from unittest.mock import Mock, patch, MagicMock
|
|
import sys
|
|
import os
|
|
|
|
# Add src to path for importing modules
|
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../../src'))
|
|
|
|
from fixtures.sample_data import *
|
|
|
|
|
|
class TestTextGenerationPipeline:
|
|
"""Integration tests for the complete text generation workflow"""
|
|
|
|
@pytest.mark.integration
|
|
@patch('src.core.text_processor.process_multiline_text')
|
|
@patch('src.core.text_fitting.find_optimal_font_size')
|
|
@patch('src.core.text_fitting.calculate_text_position')
|
|
def test_basic_text_generation_workflow(self, mock_calc_position, mock_find_size, mock_process_text):
|
|
"""Test the basic workflow from text input to positioned output"""
|
|
# Setup mocks
|
|
mock_process_text.return_value = {
|
|
'lines': ['Hello', 'World'],
|
|
'total_height': 40,
|
|
'line_count': 2
|
|
}
|
|
mock_find_size.return_value = 24
|
|
mock_calc_position.return_value = {
|
|
'x': 50, 'y': 50,
|
|
'text_width': 100, 'text_height': 40
|
|
}
|
|
|
|
# Simulate a basic workflow
|
|
text = "Hello\nWorld"
|
|
font = Mock()
|
|
canvas_width, canvas_height = 300, 200
|
|
|
|
# Step 1: Process multiline text
|
|
from src.core.text_processor import process_multiline_text
|
|
processed = process_multiline_text(text, font, canvas_width, canvas_height)
|
|
|
|
# Step 2: Find optimal font size
|
|
from src.core.text_fitting import find_optimal_font_size
|
|
optimal_size = find_optimal_font_size(text, "/fake/font.ttf", 200, 100)
|
|
|
|
# Step 3: Calculate position
|
|
from src.core.text_fitting import calculate_text_position
|
|
available_area = {'x': 0, 'y': 0, 'width': canvas_width, 'height': canvas_height}
|
|
position = calculate_text_position(text, font, available_area, 'center')
|
|
|
|
# Verify workflow completed
|
|
assert processed['line_count'] == 2
|
|
assert optimal_size == 24
|
|
assert position['x'] == 50 and position['y'] == 50
|
|
|
|
# Verify functions were called in sequence
|
|
mock_process_text.assert_called_once()
|
|
mock_find_size.assert_called_once()
|
|
mock_calc_position.assert_called_once()
|
|
|
|
@pytest.mark.integration
|
|
@patch('src.core.text_fitting.check_text_overlap')
|
|
@patch('src.core.text_fitting.resolve_text_conflicts')
|
|
def test_overlap_detection_and_resolution_workflow(self, mock_resolve, mock_check):
|
|
"""Test the workflow for detecting and resolving text overlaps"""
|
|
# Setup mocks
|
|
mock_check.return_value = [(0, 1)] # Positions 0 and 1 overlap
|
|
mock_resolve.return_value = [
|
|
{'x': 0, 'y': 0, 'width': 100, 'height': 50},
|
|
{'x': 0, 'y': 60, 'width': 100, 'height': 50} # Moved down
|
|
]
|
|
|
|
# Initial overlapping positions
|
|
positions = [
|
|
{'x': 0, 'y': 0, 'width': 100, 'height': 50},
|
|
{'x': 50, 'y': 25, 'width': 100, 'height': 50} # Overlaps
|
|
]
|
|
|
|
# Step 1: Check for overlaps
|
|
from src.core.text_fitting import check_text_overlap
|
|
overlaps = check_text_overlap(positions)
|
|
|
|
# Step 2: Resolve conflicts
|
|
from src.core.text_fitting import resolve_text_conflicts
|
|
resolved = resolve_text_conflicts(positions, 500, 400)
|
|
|
|
# Verify workflow
|
|
assert len(overlaps) == 1
|
|
assert len(resolved) == 2
|
|
assert resolved[1]['y'] == 60 # Second position moved down
|
|
|
|
mock_check.assert_called_once_with(positions)
|
|
mock_resolve.assert_called_once()
|
|
|
|
@pytest.mark.integration
|
|
def test_text_metrics_and_fitting_integration(self):
|
|
"""Test integration between text metrics calculation and fitting"""
|
|
with patch('src.core.text_processor.calculate_text_metrics') as mock_metrics, \
|
|
patch('src.core.text_fitting.calculate_text_scaling') as mock_scaling:
|
|
|
|
# Setup mocks
|
|
mock_metrics.return_value = {
|
|
'width': 200, 'height': 50,
|
|
'ascent': 40, 'descent': 10
|
|
}
|
|
mock_scaling.return_value = (1.5, 2.0, 1.5) # width_scale, height_scale, uniform_scale
|
|
|
|
# Test workflow
|
|
font = Mock()
|
|
text = "Sample Text"
|
|
|
|
from src.core.text_processor import calculate_text_metrics
|
|
from src.core.text_fitting import calculate_text_scaling
|
|
|
|
# Get text metrics
|
|
metrics = calculate_text_metrics(text, font)
|
|
|
|
# Calculate scaling based on metrics
|
|
scaling = calculate_text_scaling(text, font, 300, 100)
|
|
|
|
# Verify integration
|
|
assert metrics['width'] == 200
|
|
assert metrics['height'] == 50
|
|
assert scaling[2] == 1.5 # uniform_scale
|
|
|
|
mock_metrics.assert_called_once_with(text, font)
|
|
mock_scaling.assert_called_once()
|
|
|
|
@pytest.mark.integration
|
|
@patch('src.core.text_fitting.calculate_available_text_area')
|
|
@patch('src.core.text_processor.optimize_text_layout')
|
|
def test_area_calculation_and_layout_optimization(self, mock_optimize, mock_calc_area):
|
|
"""Test integration between area calculation and layout optimization"""
|
|
# Setup mocks
|
|
mock_calc_area.return_value = {
|
|
'x': 10, 'y': 10, 'width': 300, 'height': 200, 'area': 60000
|
|
}
|
|
mock_optimize.return_value = [
|
|
{'x': 10, 'y': 10, 'width': 100, 'height': 50, 'text': 'Block 1'},
|
|
{'x': 150, 'y': 10, 'width': 80, 'height': 40, 'text': 'Block 2'}
|
|
]
|
|
|
|
# Test data
|
|
canvas_width, canvas_height = 400, 300
|
|
overlays = [{'x': 350, 'y': 250, 'width': 50, 'height': 50}]
|
|
text_blocks = [
|
|
{'x': 50, 'y': 50, 'width': 100, 'height': 50, 'text': 'Block 1'},
|
|
{'x': 75, 'y': 75, 'width': 80, 'height': 40, 'text': 'Block 2'} # Overlaps
|
|
]
|
|
|
|
# Test workflow
|
|
from src.core.text_fitting import calculate_available_text_area
|
|
from src.core.text_processor import optimize_text_layout
|
|
|
|
# Step 1: Calculate available area
|
|
available_area = calculate_available_text_area(canvas_width, canvas_height, overlays)
|
|
|
|
# Step 2: Optimize layout
|
|
optimized_blocks = optimize_text_layout(text_blocks, canvas_width, canvas_height)
|
|
|
|
# Verify integration
|
|
assert available_area['width'] == 300
|
|
assert available_area['area'] == 60000
|
|
assert len(optimized_blocks) == 2
|
|
assert optimized_blocks[0]['x'] == 10 # First block positioned in available area
|
|
|
|
mock_calc_area.assert_called_once_with(canvas_width, canvas_height, overlays)
|
|
mock_optimize.assert_called_once_with(text_blocks, canvas_width, canvas_height)
|
|
|
|
|
|
class TestErrorHandlingIntegration:
|
|
"""Integration tests for error handling across modules"""
|
|
|
|
@pytest.mark.integration
|
|
def test_graceful_degradation_on_font_errors(self):
|
|
"""Test that the system degrades gracefully when font operations fail"""
|
|
with patch('PIL.ImageFont.truetype') as mock_truetype:
|
|
mock_truetype.side_effect = OSError("Font not found")
|
|
|
|
# Test that font size finding falls back gracefully
|
|
from src.core.text_fitting import find_optimal_font_size
|
|
result = find_optimal_font_size("Test", "/fake/font.ttf", 200, 100)
|
|
|
|
assert result == 8 # Should fall back to minimum size
|
|
|
|
@pytest.mark.integration
|
|
def test_error_handling_in_text_processing_chain(self):
|
|
"""Test error handling when text processing components fail"""
|
|
with patch('src.core.text_processor.wrap_text_to_width') as mock_wrap:
|
|
mock_wrap.side_effect = Exception("Text wrapping failed")
|
|
|
|
# Test that multiline processing handles wrap failures
|
|
from src.core.text_processor import process_multiline_text
|
|
result = process_multiline_text("Test text", Mock(), 200, 100)
|
|
|
|
# Should return fallback result instead of crashing
|
|
assert 'lines' in result
|
|
assert 'total_height' in result
|
|
assert 'line_count' in result |