#!/usr/bin/env python3 """ Test result visualization script for YAMS. Generates charts and reports from test and benchmark data. """ import json import sys import argparse from pathlib import Path from typing import Dict, List, Optional import datetime # Try to import plotting libraries (optional) try: import matplotlib.pyplot as plt import numpy as np HAS_MATPLOTLIB = True except ImportError: HAS_MATPLOTLIB = False print("Warning: matplotlib not installed. Charts will not be generated.") print("Install with: pip install matplotlib") class TestResultVisualizer: def __init__(self, results_path: Path): """Initialize visualizer with test results.""" self.results_path = results_path self.data = self.load_results() def load_results(self) -> Dict: """Load test results from JSON file.""" with open(self.results_path, 'r') as f: return json.load(f) def generate_html_report(self, output_path: Path): """Generate interactive HTML dashboard.""" html = """ YAMS Test Results Dashboard

šŸš€ YAMS Test Results Dashboard

Generated: {timestamp}

{summary_section} {benchmark_section} {coverage_section} {test_details_section}
""" # Generate sections summary = self._generate_summary_section() benchmarks = self._generate_benchmark_section() coverage = self._generate_coverage_section() details = self._generate_test_details_section() scripts = self._generate_chart_scripts() # Fill template html = html.format( timestamp=datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), summary_section=summary, benchmark_section=benchmarks, coverage_section=coverage, test_details_section=details, chart_scripts=scripts ) # Write HTML file with open(output_path, 'w') as f: f.write(html) print(f"šŸ“Š HTML dashboard generated: {output_path}") def _generate_summary_section(self) -> str: """Generate summary metrics section.""" total_tests = self.data.get('total_tests', 0) passed = self.data.get('passed', 0) failed = self.data.get('failed', 0) skipped = self.data.get('skipped', 0) duration = self.data.get('duration_seconds', 0) pass_rate = (passed / total_tests * 100) if total_tests > 0 else 0 return f"""

Test Summary

{total_tests}
Total Tests
{passed}
Passed
{failed}
Failed
{skipped}
Skipped
{pass_rate:.1f}%
Pass Rate
{duration:.2f}s
Duration
""" def _generate_benchmark_section(self) -> str: """Generate benchmark results section.""" if 'benchmarks' not in self.data and 'summary' not in self.data: return "" rows = [] benchmarks = self.data.get('summary', {}) for name, details in benchmarks.items(): trend = details.get('trend', 'stable') trend_icon = "ā†—ļø" if trend == "degrading" else "ā†˜ļø" if trend == "improving" else "→" trend_class = "regression" if trend == "degrading" else "improvement" if trend == "improving" else "" rows.append(f""" {name} {details.get('latest', 0):.2f} {details.get('unit', '')} {details.get('average', 0):.2f} {details.get('min', 0):.2f} {details.get('max', 0):.2f} {trend_icon} {trend} {details.get('runs', 0)} """) return f"""

Benchmark Results

{''.join(rows)}
Benchmark Latest Average Min Max Trend Runs
""" def _generate_coverage_section(self) -> str: """Generate code coverage section.""" coverage = self.data.get('coverage', {}) if not coverage: return "" line_coverage = coverage.get('line_percent', 0) branch_coverage = coverage.get('branch_percent', 0) function_coverage = coverage.get('function_percent', 0) return f"""

Code Coverage

{line_coverage:.1f}%
Line Coverage
{branch_coverage:.1f}%
Branch Coverage
{function_coverage:.1f}%
Function Coverage
""" def _generate_test_details_section(self) -> str: """Generate detailed test results section.""" if 'test_cases' not in self.data: return "" rows = [] for test in self.data.get('test_cases', []): status_badge = 'PASS' if test['passed'] else \ 'FAIL' duration = test.get('duration_ms', 0) rows.append(f""" {test['name']} {test.get('suite', 'Unknown')} {status_badge} {duration:.2f}ms {test.get('error', '')} """) return f"""

Test Details

{''.join(rows)}
Test Name Suite Status Duration Error
""" def _generate_chart_scripts(self) -> str: """Generate Chart.js scripts for visualization.""" return """ // Test Results Pie Chart var testCtx = document.getElementById('testResultChart'); if (testCtx) { new Chart(testCtx, { type: 'doughnut', data: { labels: ['Passed', 'Failed', 'Skipped'], datasets: [{ data: [""" + str(self.data.get('passed', 0)) + """, """ + str(self.data.get('failed', 0)) + """, """ + str(self.data.get('skipped', 0)) + """], backgroundColor: ['#22c55e', '#ef4444', '#f59e0b'] }] } }); } // Benchmark Trend Chart var benchCtx = document.getElementById('benchmarkChart'); if (benchCtx) { // This would need historical data to show trends // Placeholder for now } // Coverage Bar Chart var coverageCtx = document.getElementById('coverageChart'); if (coverageCtx) { new Chart(coverageCtx, { type: 'bar', data: { labels: ['Line', 'Branch', 'Function'], datasets: [{ label: 'Coverage %', data: [""" + str(self.data.get('coverage', {}).get('line_percent', 0)) + """, """ + str(self.data.get('coverage', {}).get('branch_percent', 0)) + """, """ + str(self.data.get('coverage', {}).get('function_percent', 0)) + """], backgroundColor: '#667eea' }] }, options: { scales: { y: { beginAtZero: true, max: 100 } } } }); } """ def plot_benchmark_trends(self, output_dir: Path): """Generate matplotlib charts for benchmark trends.""" if not HAS_MATPLOTLIB: return # Create output directory output_dir.mkdir(parents=True, exist_ok=True) # Plot benchmark comparisons if 'summary' in self.data: fig, axes = plt.subplots(2, 2, figsize=(12, 10)) fig.suptitle('YAMS Benchmark Analysis', fontsize=16) benchmarks = self.data['summary'] names = list(benchmarks.keys())[:4] # Top 4 benchmarks for idx, (ax, name) in enumerate(zip(axes.flat, names)): if name not in benchmarks: ax.axis('off') continue details = benchmarks[name] values = [ details.get('min', 0), details.get('average', 0), details.get('max', 0), details.get('latest', 0) ] labels = ['Min', 'Avg', 'Max', 'Latest'] colors = ['green', 'blue', 'orange', 'red'] ax.bar(labels, values, color=colors, alpha=0.7) ax.set_title(name) ax.set_ylabel(details.get('unit', 'Value')) ax.grid(True, alpha=0.3) plt.tight_layout() plt.savefig(output_dir / 'benchmark_analysis.png', dpi=150) plt.close() print(f"šŸ“ˆ Benchmark charts saved to {output_dir}") def generate_markdown_summary(self, output_path: Path): """Generate a markdown summary of test results.""" md = f"""# YAMS Test Results Summary Generated: {datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")} ## Overall Statistics | Metric | Value | |--------|-------| | Total Tests | {self.data.get('total_tests', 0)} | | Passed | {self.data.get('passed', 0)} | | Failed | {self.data.get('failed', 0)} | | Skipped | {self.data.get('skipped', 0)} | | Pass Rate | {(self.data.get('passed', 0) / max(self.data.get('total_tests', 1), 1) * 100):.1f}% | | Duration | {self.data.get('duration_seconds', 0):.2f}s | """ # Add benchmark summary if available if 'summary' in self.data: md += "## Benchmark Summary\n\n" md += "| Benchmark | Latest | Average | Trend |\n" md += "|-----------|--------|---------|-------|\n" for name, details in self.data['summary'].items(): trend = details.get('trend', 'stable') trend_icon = "šŸ“ˆ" if trend == "degrading" else "šŸ“‰" if trend == "improving" else "āž”ļø" md += f"| {name} | {details.get('latest', 0):.2f} {details.get('unit', '')} | " md += f"{details.get('average', 0):.2f} | {trend_icon} {trend} |\n" # Add coverage if available if 'coverage' in self.data: coverage = self.data['coverage'] md += f""" ## Code Coverage - Line Coverage: {coverage.get('line_percent', 0):.1f}% - Branch Coverage: {coverage.get('branch_percent', 0):.1f}% - Function Coverage: {coverage.get('function_percent', 0):.1f}% """ # Write markdown file with open(output_path, 'w') as f: f.write(md) print(f"šŸ“ Markdown summary saved to {output_path}") def main(): parser = argparse.ArgumentParser(description='Visualize YAMS test results') parser.add_argument('results', type=Path, help='Test results JSON file') parser.add_argument('--output-dir', type=Path, default=Path('test_reports'), help='Output directory for reports') parser.add_argument('--format', choices=['html', 'markdown', 'charts', 'all'], default='all', help='Output format') args = parser.parse_args() # Validate input if not args.results.exists(): print(f"Error: Results file not found: {args.results}") sys.exit(1) # Create output directory args.output_dir.mkdir(parents=True, exist_ok=True) # Generate reports visualizer = TestResultVisualizer(args.results) if args.format in ['html', 'all']: visualizer.generate_html_report(args.output_dir / 'dashboard.html') if args.format in ['markdown', 'all']: visualizer.generate_markdown_summary(args.output_dir / 'summary.md') if args.format in ['charts', 'all']: visualizer.plot_benchmark_trends(args.output_dir / 'charts') print(f"\nāœ… Reports generated in {args.output_dir}") if __name__ == '__main__': main()