#!/usr/bin/env python3 """ Comprehensive test runner for ComfyUI Queue Manager integration tests. Orchestrates all test suites and generates detailed reports. """ import argparse import json import os import subprocess import sys import time from datetime import datetime from pathlib import Path from typing import Dict, List, Any, Optional # Add current directory to path sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) class TestResult: """Container for test results.""" def __init__(self, name: str, success: bool, duration: float, details: Optional[Dict] = None, error: Optional[str] = None): self.name = name self.success = success self.duration = duration self.details = details or {} self.error = error self.timestamp = datetime.now() class TestSuite: """Base class for test suites.""" def __init__(self, name: str, description: str): self.name = name self.description = description self.results: List[TestResult] = [] def run(self) -> bool: """Run the test suite. Should be implemented by subclasses.""" raise NotImplementedError def add_result(self, result: TestResult): """Add a test result.""" self.results.append(result) def get_summary(self) -> Dict[str, Any]: """Get summary statistics.""" total_tests = len(self.results) passed_tests = sum(1 for r in self.results if r.success) total_duration = sum(r.duration for r in self.results) return { 'name': self.name, 'description': self.description, 'total_tests': total_tests, 'passed_tests': passed_tests, 'failed_tests': total_tests - passed_tests, 'success_rate': passed_tests / total_tests if total_tests > 0 else 0, 'total_duration': total_duration, 'results': [ { 'name': r.name, 'success': r.success, 'duration': r.duration, 'error': r.error, 'timestamp': r.timestamp.isoformat() } for r in self.results ] } class UnitTestSuite(TestSuite): """Unit test suite runner.""" def __init__(self): super().__init__("Unit Tests", "Individual component unit tests") def run(self) -> bool: """Run all unit tests.""" print(f"Running {self.name}...") # List of unit test files unit_test_files = [ "test_database.py", "test_queue_service.py", "test_api_routes.py", "test_archive_service.py", "test_filter_service.py", "test_import_export_service.py", "test_serialization_service.py", "test_config_manager.py", "test_error_handling.py", "test_logging_monitoring.py" ] all_passed = True for test_file in unit_test_files: if not os.path.exists(test_file): print(f" Skipping {test_file} (not found)") continue print(f" Running {test_file}...") start_time = time.time() try: # Run the test file result = subprocess.run([sys.executable, test_file], capture_output=True, text=True, timeout=60) duration = time.time() - start_time success = result.returncode == 0 if success: print(f" ✓ Passed ({duration:.2f}s)") else: print(f" ✗ Failed ({duration:.2f}s)") print(f" Error: {result.stderr}") all_passed = False self.add_result(TestResult( name=test_file, success=success, duration=duration, details={'stdout': result.stdout, 'stderr': result.stderr}, error=result.stderr if not success else None )) except subprocess.TimeoutExpired: duration = time.time() - start_time print(f" ✗ Timeout ({duration:.2f}s)") all_passed = False self.add_result(TestResult( name=test_file, success=False, duration=duration, error="Test timeout" )) except Exception as e: duration = time.time() - start_time print(f" ✗ Error ({duration:.2f}s): {e}") all_passed = False self.add_result(TestResult( name=test_file, success=False, duration=duration, error=str(e) )) return all_passed class IntegrationTestSuite(TestSuite): """Integration test suite runner.""" def __init__(self): super().__init__("Integration Tests", "End-to-end integration tests") def run(self) -> bool: """Run integration tests.""" print(f"Running {self.name}...") start_time = time.time() try: # Import and run integration test suite from test_integration_suite import run_integration_test_suite result = run_integration_test_suite() duration = time.time() - start_time success = result == 0 if success: print(f" ✓ Integration tests passed ({duration:.2f}s)") else: print(f" ✗ Integration tests failed ({duration:.2f}s)") self.add_result(TestResult( name="integration_test_suite", success=success, duration=duration, details={'return_code': result} )) return success except Exception as e: duration = time.time() - start_time print(f" ✗ Integration tests error ({duration:.2f}s): {e}") self.add_result(TestResult( name="integration_test_suite", success=False, duration=duration, error=str(e) )) return False class PerformanceTestSuite(TestSuite): """Performance test suite runner.""" def __init__(self): super().__init__("Performance Tests", "Performance and benchmarking tests") def run(self) -> bool: """Run performance tests.""" print(f"Running {self.name}...") start_time = time.time() try: # Import and run performance benchmarks from test_performance_benchmark import run_performance_benchmarks result = run_performance_benchmarks() duration = time.time() - start_time success = result == 0 if success: print(f" ✓ Performance tests passed ({duration:.2f}s)") else: print(f" ✗ Performance tests failed ({duration:.2f}s)") self.add_result(TestResult( name="performance_benchmarks", success=success, duration=duration, details={'return_code': result} )) return success except Exception as e: duration = time.time() - start_time print(f" ✗ Performance tests error ({duration:.2f}s): {e}") self.add_result(TestResult( name="performance_benchmarks", success=False, duration=duration, error=str(e) )) return False class APITestSuite(TestSuite): """API test suite runner.""" def __init__(self): super().__init__("API Tests", "API endpoint and integration tests") def run(self) -> bool: """Run API tests.""" print(f"Running {self.name}...") # List of API test files api_test_files = [ "test_complete_api.py", "test_api_queue_endpoints.py", "test_control_endpoints_simple.py", "test_queue_endpoints_simple.py" ] all_passed = True for test_file in api_test_files: if not os.path.exists(test_file): print(f" Skipping {test_file} (not found)") continue print(f" Running {test_file}...") start_time = time.time() try: result = subprocess.run([sys.executable, test_file], capture_output=True, text=True, timeout=120) duration = time.time() - start_time success = result.returncode == 0 if success: print(f" ✓ Passed ({duration:.2f}s)") else: print(f" ✗ Failed ({duration:.2f}s)") print(f" Error: {result.stderr}") all_passed = False self.add_result(TestResult( name=test_file, success=success, duration=duration, details={'stdout': result.stdout, 'stderr': result.stderr}, error=result.stderr if not success else None )) except subprocess.TimeoutExpired: duration = time.time() - start_time print(f" ✗ Timeout ({duration:.2f}s)") all_passed = False self.add_result(TestResult( name=test_file, success=False, duration=duration, error="Test timeout" )) except Exception as e: duration = time.time() - start_time print(f" ✗ Error ({duration:.2f}s): {e}") all_passed = False self.add_result(TestResult( name=test_file, success=False, duration=duration, error=str(e) )) return all_passed class WorkflowTestSuite(TestSuite): """Workflow execution test suite runner.""" def __init__(self): super().__init__("Workflow Tests", "Workflow execution and monitoring tests") def run(self) -> bool: """Run workflow tests.""" print(f"Running {self.name}...") # List of workflow test files workflow_test_files = [ "test_workflow_execution.py", "test_node_registration.py", "test_menu_integration.py", "test_menu_functionality.py" ] all_passed = True for test_file in workflow_test_files: if not os.path.exists(test_file): print(f" Skipping {test_file} (not found)") continue print(f" Running {test_file}...") start_time = time.time() try: result = subprocess.run([sys.executable, test_file], capture_output=True, text=True, timeout=180) duration = time.time() - start_time success = result.returncode == 0 if success: print(f" ✓ Passed ({duration:.2f}s)") else: print(f" ✗ Failed ({duration:.2f}s)") print(f" Error: {result.stderr}") all_passed = False self.add_result(TestResult( name=test_file, success=success, duration=duration, details={'stdout': result.stdout, 'stderr': result.stderr}, error=result.stderr if not success else None )) except subprocess.TimeoutExpired: duration = time.time() - start_time print(f" ✗ Timeout ({duration:.2f}s)") all_passed = False self.add_result(TestResult( name=test_file, success=False, duration=duration, error="Test timeout" )) except Exception as e: duration = time.time() - start_time print(f" ✗ Error ({duration:.2f}s): {e}") all_passed = False self.add_result(TestResult( name=test_file, success=False, duration=duration, error=str(e) )) return all_passed class TestRunner: """Main test runner that orchestrates all test suites.""" def __init__(self): self.test_suites: List[TestSuite] = [] self.start_time = None self.end_time = None def add_test_suite(self, test_suite: TestSuite): """Add a test suite to run.""" self.test_suites.append(test_suite) def run_all(self, suite_names: Optional[List[str]] = None) -> bool: """Run all test suites or specified ones.""" print("=" * 80) print("COMFYUI QUEUE MANAGER - COMPREHENSIVE TEST SUITE") print("=" * 80) self.start_time = time.time() # Filter test suites if specific ones requested suites_to_run = self.test_suites if suite_names: suites_to_run = [suite for suite in self.test_suites if suite.name.lower().replace(' ', '_') in suite_names] if not suites_to_run: print("No test suites to run!") return False all_passed = True for suite in suites_to_run: print(f"\n{'='*60}") print(f"{suite.name.upper()}: {suite.description}") print('='*60) suite_passed = suite.run() all_passed = all_passed and suite_passed # Print suite summary summary = suite.get_summary() print(f"\n{suite.name} Summary:") print(f" Total tests: {summary['total_tests']}") print(f" Passed: {summary['passed_tests']}") print(f" Failed: {summary['failed_tests']}") print(f" Success rate: {summary['success_rate']:.1%}") print(f" Duration: {summary['total_duration']:.2f}s") self.end_time = time.time() # Generate final report self._generate_final_report(all_passed) return all_passed def _generate_final_report(self, all_passed: bool): """Generate final test report.""" total_duration = self.end_time - self.start_time print("\n" + "=" * 80) print("FINAL TEST REPORT") print("=" * 80) # Overall statistics total_tests = sum(len(suite.results) for suite in self.test_suites) total_passed = sum(sum(1 for r in suite.results if r.success) for suite in self.test_suites) overall_success_rate = total_passed / total_tests if total_tests > 0 else 0 print(f"\nOverall Results:") print(f" Total test suites: {len(self.test_suites)}") print(f" Total tests: {total_tests}") print(f" Passed: {total_passed}") print(f" Failed: {total_tests - total_passed}") print(f" Success rate: {overall_success_rate:.1%}") print(f" Total duration: {total_duration:.2f}s") # Suite breakdown print(f"\nSuite Breakdown:") for suite in self.test_suites: summary = suite.get_summary() status = "✓" if summary['failed_tests'] == 0 else "✗" print(f" {status} {suite.name}: {summary['passed_tests']}/{summary['total_tests']} " f"({summary['success_rate']:.1%}) in {summary['total_duration']:.2f}s") # Failed tests details failed_tests = [] for suite in self.test_suites: for result in suite.results: if not result.success: failed_tests.append((suite.name, result)) if failed_tests: print(f"\nFailed Tests ({len(failed_tests)}):") for suite_name, result in failed_tests: print(f" ✗ {suite_name}/{result.name}: {result.error or 'Unknown error'}") # Save detailed report self._save_detailed_report() # Final status if all_passed: print(f"\n🎉 ALL TESTS PASSED!") print(f" {total_tests} tests completed successfully in {total_duration:.2f}s") else: print(f"\n❌ SOME TESTS FAILED!") print(f" {total_passed}/{total_tests} tests passed ({overall_success_rate:.1%})") def _save_detailed_report(self): """Save detailed test report to file.""" timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") report_file = f"test_report_{timestamp}.json" report_data = { 'timestamp': datetime.now().isoformat(), 'total_duration': self.end_time - self.start_time, 'test_suites': [suite.get_summary() for suite in self.test_suites] } try: with open(report_file, 'w') as f: json.dump(report_data, f, indent=2) print(f"\nDetailed report saved to: {report_file}") except Exception as e: print(f"\nFailed to save detailed report: {e}") def main(): """Main entry point.""" parser = argparse.ArgumentParser(description="Run ComfyUI Queue Manager integration tests") parser.add_argument('--suites', nargs='*', choices=['unit_tests', 'integration_tests', 'performance_tests', 'api_tests', 'workflow_tests'], help='Specific test suites to run (default: all)') parser.add_argument('--quick', action='store_true', help='Run quick tests only (skip performance tests)') parser.add_argument('--performance-only', action='store_true', help='Run performance tests only') args = parser.parse_args() # Create test runner runner = TestRunner() # Add test suites based on arguments if args.performance_only: runner.add_test_suite(PerformanceTestSuite()) elif args.quick: runner.add_test_suite(UnitTestSuite()) runner.add_test_suite(APITestSuite()) runner.add_test_suite(IntegrationTestSuite()) else: # Add all test suites runner.add_test_suite(UnitTestSuite()) runner.add_test_suite(APITestSuite()) runner.add_test_suite(WorkflowTestSuite()) runner.add_test_suite(IntegrationTestSuite()) if not args.quick: runner.add_test_suite(PerformanceTestSuite()) # Run tests success = runner.run_all(args.suites) return 0 if success else 1 if __name__ == "__main__": sys.exit(main())