445 lines
16 KiB
Python
445 lines
16 KiB
Python
"""
|
|
Unit tests for visualization and reporting system.
|
|
|
|
Tests the comprehensive visualization and reporting system to analyze
|
|
performance data and generate actionable insights.
|
|
"""
|
|
|
|
import json
|
|
import pytest
|
|
import tempfile
|
|
from pathlib import Path
|
|
from unittest.mock import MagicMock, patch, mock_open
|
|
from typing import Dict, List, Any
|
|
from datetime import datetime, timezone
|
|
|
|
import pandas as pd
|
|
import plotly.graph_objects as go
|
|
from plotly.subplots import make_subplots
|
|
|
|
from src.services.performance import PerformanceMetrics
|
|
from src.services.performance_profiling import BenchmarkData
|
|
|
|
|
|
class TestVisualizationReportingSystem:
|
|
"""Test the visualization and reporting system."""
|
|
|
|
@pytest.fixture
|
|
def sample_benchmark_data(self):
|
|
"""Create sample benchmark data for testing."""
|
|
return [
|
|
BenchmarkData(
|
|
operation_name="transcription",
|
|
batch_size=1,
|
|
duration_seconds=10.5,
|
|
peak_memory_mb=1024.0,
|
|
throughput_items_per_second=2.5,
|
|
timestamp=datetime.now(timezone.utc)
|
|
),
|
|
BenchmarkData(
|
|
operation_name="transcription",
|
|
batch_size=2,
|
|
duration_seconds=8.2,
|
|
peak_memory_mb=1536.0,
|
|
throughput_items_per_second=3.1,
|
|
timestamp=datetime.now(timezone.utc)
|
|
),
|
|
BenchmarkData(
|
|
operation_name="transcription",
|
|
batch_size=4,
|
|
duration_seconds=6.8,
|
|
peak_memory_mb=2048.0,
|
|
throughput_items_per_second=3.7,
|
|
timestamp=datetime.now(timezone.utc)
|
|
)
|
|
]
|
|
|
|
@pytest.fixture
|
|
def sample_performance_metrics(self):
|
|
"""Create sample performance metrics for testing."""
|
|
return [
|
|
PerformanceMetrics(
|
|
operation="transcription",
|
|
duration_seconds=10.0,
|
|
memory_peak_mb=1024.0,
|
|
cpu_peak_percent=50.0,
|
|
throughput_items_per_second=2.0,
|
|
error_count=0,
|
|
success_count=10,
|
|
total_count=10
|
|
),
|
|
PerformanceMetrics(
|
|
operation="diarization",
|
|
duration_seconds=15.0,
|
|
memory_peak_mb=1536.0,
|
|
cpu_peak_percent=60.0,
|
|
throughput_items_per_second=1.5,
|
|
error_count=1,
|
|
success_count=9,
|
|
total_count=10
|
|
)
|
|
]
|
|
|
|
def test_interactive_chart_creation(self, sample_benchmark_data):
|
|
"""Test creation of interactive charts using Plotly."""
|
|
from src.services.visualization_reporting import InteractiveChartGenerator
|
|
|
|
generator = InteractiveChartGenerator()
|
|
|
|
# Test throughput chart
|
|
fig = generator.create_throughput_chart(sample_benchmark_data)
|
|
assert isinstance(fig, go.Figure)
|
|
assert len(fig.data) > 0
|
|
assert fig.layout.title.text == "Transcription Throughput by Batch Size"
|
|
|
|
# Test memory usage chart
|
|
fig = generator.create_memory_chart(sample_benchmark_data)
|
|
assert isinstance(fig, go.Figure)
|
|
assert len(fig.data) > 0
|
|
assert fig.layout.title.text == "Memory Usage by Batch Size"
|
|
|
|
# Test combined chart
|
|
fig = generator.create_combined_chart(sample_benchmark_data)
|
|
assert isinstance(fig, go.Figure)
|
|
assert len(fig.data) > 0
|
|
|
|
def test_bottleneck_identification(self, sample_performance_metrics):
|
|
"""Test bottleneck identification algorithms."""
|
|
from src.services.visualization_reporting import BottleneckAnalyzer
|
|
|
|
analyzer = BottleneckAnalyzer()
|
|
|
|
# Test bottleneck identification
|
|
bottlenecks = analyzer.identify_bottlenecks(sample_performance_metrics)
|
|
|
|
assert isinstance(bottlenecks, list)
|
|
assert len(bottlenecks) > 0
|
|
|
|
# Check that bottlenecks have required fields
|
|
for bottleneck in bottlenecks:
|
|
assert 'component' in bottleneck
|
|
assert 'severity' in bottleneck
|
|
assert 'description' in bottleneck
|
|
assert 'recommendation' in bottleneck
|
|
|
|
def test_report_generation_formats(self, sample_benchmark_data, tmp_path):
|
|
"""Test report generation in various formats."""
|
|
from src.services.visualization_reporting import ReportGenerator
|
|
|
|
generator = ReportGenerator()
|
|
|
|
# Test HTML report
|
|
html_path = tmp_path / "report.html"
|
|
generator.generate_html_report(sample_benchmark_data, html_path)
|
|
assert html_path.exists()
|
|
|
|
# Test PDF report
|
|
pdf_path = tmp_path / "report.pdf"
|
|
generator.generate_pdf_report(sample_benchmark_data, pdf_path)
|
|
assert pdf_path.exists()
|
|
|
|
# Test CSV export
|
|
csv_path = tmp_path / "data.csv"
|
|
generator.export_csv(sample_benchmark_data, csv_path)
|
|
assert csv_path.exists()
|
|
|
|
# Test JSON export
|
|
json_path = tmp_path / "data.json"
|
|
generator.export_json(sample_benchmark_data, json_path)
|
|
assert json_path.exists()
|
|
|
|
def test_comparison_views(self, sample_benchmark_data):
|
|
"""Test before/after optimization analysis."""
|
|
from src.services.visualization_reporting import ComparisonAnalyzer
|
|
|
|
analyzer = ComparisonAnalyzer()
|
|
|
|
# Create "before" and "after" data
|
|
before_data = sample_benchmark_data[:2] # First two entries
|
|
after_data = sample_benchmark_data[1:] # Last two entries
|
|
|
|
# Test comparison analysis
|
|
comparison = analyzer.compare_performance(before_data, after_data)
|
|
|
|
assert isinstance(comparison, dict)
|
|
assert 'improvements' in comparison
|
|
assert 'regressions' in comparison
|
|
assert 'summary' in comparison
|
|
|
|
# Test comparison chart
|
|
fig = analyzer.create_comparison_chart(before_data, after_data)
|
|
assert isinstance(fig, go.Figure)
|
|
assert len(fig.data) > 0
|
|
|
|
def test_trend_analysis(self, sample_benchmark_data):
|
|
"""Test trend analysis for performance metrics over time."""
|
|
from src.services.visualization_reporting import TrendAnalyzer
|
|
|
|
analyzer = TrendAnalyzer()
|
|
|
|
# Test trend calculation
|
|
trends = analyzer.calculate_trends(sample_benchmark_data)
|
|
|
|
assert isinstance(trends, dict)
|
|
assert 'throughput_trend' in trends
|
|
assert 'memory_trend' in trends
|
|
assert 'duration_trend' in trends
|
|
|
|
# Test trend visualization
|
|
fig = analyzer.create_trend_chart(sample_benchmark_data)
|
|
assert isinstance(fig, go.Figure)
|
|
assert len(fig.data) > 0
|
|
|
|
def test_report_templates(self, sample_benchmark_data, tmp_path):
|
|
"""Test different report templates."""
|
|
from src.services.visualization_reporting import ReportTemplateManager
|
|
|
|
manager = ReportTemplateManager()
|
|
|
|
# Test executive summary template
|
|
exec_path = tmp_path / "executive_summary.html"
|
|
manager.generate_executive_summary(sample_benchmark_data, exec_path)
|
|
assert exec_path.exists()
|
|
|
|
# Test detailed technical report template
|
|
tech_path = tmp_path / "technical_report.html"
|
|
manager.generate_technical_report(sample_benchmark_data, tech_path)
|
|
assert tech_path.exists()
|
|
|
|
# Test custom template
|
|
custom_path = tmp_path / "custom_report.html"
|
|
template_vars = {"title": "Custom Report", "author": "Test User"}
|
|
manager.generate_custom_report(sample_benchmark_data, custom_path, template_vars)
|
|
assert custom_path.exists()
|
|
|
|
def test_export_functionality(self, sample_benchmark_data, tmp_path):
|
|
"""Test export functionality for various formats."""
|
|
from src.services.visualization_reporting import DataExporter
|
|
|
|
exporter = DataExporter()
|
|
|
|
# Test CSV export
|
|
csv_path = tmp_path / "export.csv"
|
|
exporter.export_to_csv(sample_benchmark_data, csv_path)
|
|
assert csv_path.exists()
|
|
|
|
# Verify CSV content
|
|
df = pd.read_csv(csv_path)
|
|
assert len(df) == len(sample_benchmark_data)
|
|
assert 'operation_name' in df.columns
|
|
assert 'batch_size' in df.columns
|
|
|
|
# Test JSON export
|
|
json_path = tmp_path / "export.json"
|
|
exporter.export_to_json(sample_benchmark_data, json_path)
|
|
assert json_path.exists()
|
|
|
|
# Verify JSON content
|
|
with open(json_path, 'r') as f:
|
|
data = json.load(f)
|
|
assert isinstance(data, list)
|
|
assert len(data) == len(sample_benchmark_data)
|
|
|
|
def test_chart_customization(self, sample_benchmark_data):
|
|
"""Test chart customization options."""
|
|
from src.services.visualization_reporting import InteractiveChartGenerator
|
|
|
|
generator = InteractiveChartGenerator()
|
|
|
|
# Test custom colors
|
|
custom_colors = ['#FF6B6B', '#4ECDC4', '#45B7D1']
|
|
fig = generator.create_throughput_chart(
|
|
sample_benchmark_data,
|
|
colors=custom_colors
|
|
)
|
|
assert isinstance(fig, go.Figure)
|
|
|
|
# Test custom layout
|
|
custom_layout = {
|
|
'title': 'Custom Title',
|
|
'xaxis_title': 'Custom X Axis',
|
|
'yaxis_title': 'Custom Y Axis'
|
|
}
|
|
fig = generator.create_memory_chart(
|
|
sample_benchmark_data,
|
|
layout=custom_layout
|
|
)
|
|
assert fig.layout.title.text == 'Custom Title'
|
|
assert fig.layout.xaxis.title.text == 'Custom X Axis'
|
|
|
|
def test_performance_insights(self, sample_performance_metrics):
|
|
"""Test performance insights generation."""
|
|
from src.services.visualization_reporting import PerformanceInsightsGenerator
|
|
|
|
generator = PerformanceInsightsGenerator()
|
|
|
|
# Test insights generation
|
|
insights = generator.generate_insights(sample_performance_metrics)
|
|
|
|
assert isinstance(insights, list)
|
|
assert len(insights) > 0
|
|
|
|
# Check insight structure
|
|
for insight in insights:
|
|
assert 'type' in insight
|
|
assert 'message' in insight
|
|
assert 'severity' in insight
|
|
assert 'actionable' in insight
|
|
|
|
def test_report_validation(self, sample_benchmark_data, tmp_path):
|
|
"""Test report validation and error handling."""
|
|
from src.services.visualization_reporting import ReportValidator
|
|
|
|
validator = ReportValidator()
|
|
|
|
# Test valid data
|
|
is_valid = validator.validate_data(sample_benchmark_data)
|
|
assert is_valid
|
|
|
|
# Test invalid data
|
|
invalid_data = [{"invalid": "data"}]
|
|
is_valid = validator.validate_data(invalid_data)
|
|
assert not is_valid
|
|
|
|
# Test report file validation
|
|
report_path = tmp_path / "test_report.html"
|
|
with open(report_path, 'w') as f:
|
|
f.write("<html><body>Test Report</body></html>")
|
|
|
|
is_valid = validator.validate_report_file(report_path)
|
|
assert is_valid
|
|
|
|
|
|
class TestVisualizationReportingIntegration:
|
|
"""Integration tests for visualization and reporting system."""
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_complete_reporting_workflow(self, tmp_path):
|
|
"""Test the complete reporting workflow."""
|
|
from src.services.visualization_reporting import (
|
|
ReportGenerator,
|
|
InteractiveChartGenerator,
|
|
BottleneckAnalyzer,
|
|
ComparisonAnalyzer,
|
|
TrendAnalyzer
|
|
)
|
|
|
|
# Create sample data
|
|
sample_data = [
|
|
BenchmarkData(
|
|
operation_name="transcription",
|
|
batch_size=1,
|
|
duration_seconds=10.0,
|
|
peak_memory_mb=1024.0,
|
|
throughput_items_per_second=2.0,
|
|
timestamp=datetime.now(timezone.utc)
|
|
),
|
|
BenchmarkData(
|
|
operation_name="transcription",
|
|
batch_size=2,
|
|
duration_seconds=8.0,
|
|
peak_memory_mb=1536.0,
|
|
throughput_items_per_second=2.5,
|
|
timestamp=datetime.now(timezone.utc)
|
|
)
|
|
]
|
|
|
|
# Initialize components
|
|
generator = ReportGenerator()
|
|
chart_generator = InteractiveChartGenerator()
|
|
bottleneck_analyzer = BottleneckAnalyzer()
|
|
comparison_analyzer = ComparisonAnalyzer()
|
|
trend_analyzer = TrendAnalyzer()
|
|
|
|
# Generate charts
|
|
throughput_chart = chart_generator.create_throughput_chart(sample_data)
|
|
memory_chart = chart_generator.create_memory_chart(sample_data)
|
|
combined_chart = chart_generator.create_combined_chart(sample_data)
|
|
|
|
# Analyze bottlenecks
|
|
bottlenecks = bottleneck_analyzer.identify_bottlenecks(sample_data)
|
|
|
|
# Generate trends
|
|
trends = trend_analyzer.calculate_trends(sample_data)
|
|
|
|
# Generate comprehensive report
|
|
report_path = tmp_path / "comprehensive_report.html"
|
|
generator.generate_comprehensive_report(
|
|
sample_data,
|
|
report_path,
|
|
charts=[throughput_chart, memory_chart, combined_chart],
|
|
bottlenecks=bottlenecks,
|
|
trends=trends
|
|
)
|
|
|
|
# Verify report was generated
|
|
assert report_path.exists()
|
|
|
|
# Verify report content
|
|
with open(report_path, 'r') as f:
|
|
content = f.read()
|
|
assert "Performance Report" in content
|
|
assert "Bottlenecks" in content
|
|
assert "Trends" in content
|
|
|
|
def test_multi_format_export(self, tmp_path):
|
|
"""Test exporting data in multiple formats."""
|
|
from src.services.visualization_reporting import MultiFormatExporter
|
|
|
|
exporter = MultiFormatExporter()
|
|
|
|
# Create sample data
|
|
sample_data = [
|
|
BenchmarkData(
|
|
operation_name="test",
|
|
batch_size=1,
|
|
duration_seconds=5.0,
|
|
peak_memory_mb=512.0,
|
|
throughput_items_per_second=1.0,
|
|
timestamp=datetime.now(timezone.utc)
|
|
)
|
|
]
|
|
|
|
# Export in multiple formats
|
|
export_paths = exporter.export_all_formats(sample_data, tmp_path)
|
|
|
|
# Verify all formats were created
|
|
assert 'html' in export_paths
|
|
assert 'pdf' in export_paths
|
|
assert 'csv' in export_paths
|
|
assert 'json' in export_paths
|
|
|
|
# Verify files exist
|
|
for format_type, path in export_paths.items():
|
|
assert Path(path).exists()
|
|
|
|
def test_report_scheduling(self):
|
|
"""Test automated report scheduling."""
|
|
from src.services.visualization_reporting import ReportScheduler
|
|
|
|
scheduler = ReportScheduler()
|
|
|
|
# Test schedule creation
|
|
schedule = scheduler.create_schedule(
|
|
frequency="daily",
|
|
time="09:00",
|
|
report_type="executive_summary"
|
|
)
|
|
|
|
assert schedule['frequency'] == "daily"
|
|
assert schedule['time'] == "09:00"
|
|
assert schedule['report_type'] == "executive_summary"
|
|
|
|
# Test schedule validation
|
|
is_valid = scheduler.validate_schedule(schedule)
|
|
assert is_valid
|
|
|
|
# Test invalid schedule
|
|
invalid_schedule = {
|
|
'frequency': "invalid",
|
|
'time': "25:00",
|
|
'report_type': "unknown"
|
|
}
|
|
is_valid = scheduler.validate_schedule(invalid_schedule)
|
|
assert not is_valid
|