Spaces:
Sleeping
Sleeping
| #!/usr/bin/env python3 | |
| """ | |
| Test Suite for Optimized Twi Speech Recognition Engine | |
| ===================================================== | |
| This module provides comprehensive tests for the optimized speech recognition | |
| engine to ensure all components work correctly. | |
| Author: AI Assistant | |
| Date: 2025-11-05 | |
| """ | |
| import os | |
| import sys | |
| import time | |
| import tempfile | |
| import logging | |
| from pathlib import Path | |
| import asyncio | |
| # Add src to path | |
| sys.path.insert(0, str(Path(__file__).parent / "src")) | |
| import numpy as np | |
| import soundfile as sf | |
| # Configure logging | |
| logging.basicConfig(level=logging.INFO) | |
| logger = logging.getLogger(__name__) | |
| class EngineTestSuite: | |
| """Test suite for the optimized speech recognition engine.""" | |
| def __init__(self): | |
| self.test_results = {} | |
| self.temp_files = [] | |
| def cleanup(self): | |
| """Clean up temporary files.""" | |
| for file_path in self.temp_files: | |
| try: | |
| if Path(file_path).exists(): | |
| Path(file_path).unlink() | |
| except Exception as e: | |
| logger.warning(f"Failed to cleanup {file_path}: {e}") | |
| def create_test_audio(self, duration=3.0, sample_rate=16000, frequency=440) -> str: | |
| """Create a test audio file.""" | |
| # Generate sine wave | |
| t = np.linspace(0, duration, int(sample_rate * duration), False) | |
| audio = np.sin(frequency * 2 * np.pi * t) | |
| # Add some noise to make it more realistic | |
| noise = np.random.normal(0, 0.01, audio.shape) | |
| audio = audio + noise | |
| # Save to temporary file | |
| with tempfile.NamedTemporaryFile(delete=False, suffix=".wav") as tmp_file: | |
| sf.write(tmp_file.name, audio, sample_rate) | |
| self.temp_files.append(tmp_file.name) | |
| return tmp_file.name | |
| def test_config_import(self) -> bool: | |
| """Test configuration import.""" | |
| try: | |
| from config.config import OptimizedConfig | |
| config = OptimizedConfig() | |
| # Basic validation | |
| assert hasattr(config, "WHISPER") | |
| assert hasattr(config, "INTENTS") | |
| assert len(config.INTENTS) > 0 | |
| logger.info( | |
| f"β Config test passed - {len(config.INTENTS)} intents configured" | |
| ) | |
| return True | |
| except Exception as e: | |
| logger.error(f"β Config test failed: {e}") | |
| return False | |
| def test_speech_recognizer_import(self) -> bool: | |
| """Test speech recognizer import.""" | |
| try: | |
| from speech_recognizer import ( | |
| OptimizedSpeechRecognizer, | |
| create_speech_recognizer, | |
| ) | |
| # Try to create recognizer | |
| recognizer = create_speech_recognizer() | |
| # Basic validation | |
| assert recognizer is not None | |
| assert hasattr(recognizer, "recognize") | |
| assert hasattr(recognizer, "health_check") | |
| logger.info("β Speech recognizer import test passed") | |
| return True | |
| except Exception as e: | |
| logger.error(f"β Speech recognizer import failed: {e}") | |
| return False | |
| def test_whisper_model_load(self) -> bool: | |
| """Test Whisper model loading.""" | |
| try: | |
| import whisper | |
| # Try to load a small model for testing | |
| model = whisper.load_model("tiny") | |
| assert model is not None | |
| # Test basic transcription | |
| test_audio = self.create_test_audio(duration=1.0) | |
| result = model.transcribe(test_audio) | |
| assert "text" in result | |
| logger.info("β Whisper model test passed") | |
| return True | |
| except Exception as e: | |
| logger.error(f"β Whisper model test failed: {e}") | |
| return False | |
| def test_audio_processing(self) -> bool: | |
| """Test audio processing functionality.""" | |
| try: | |
| from speech_recognizer import AudioProcessor | |
| from config.config import OptimizedConfig | |
| config = OptimizedConfig() | |
| processor = AudioProcessor(config) | |
| # Create test audio | |
| test_audio = self.create_test_audio() | |
| # Test audio loading | |
| audio_data = processor.load_audio(test_audio) | |
| assert audio_data is not None | |
| assert len(audio_data) > 0 | |
| logger.info("β Audio processing test passed") | |
| return True | |
| except Exception as e: | |
| logger.error(f"β Audio processing test failed: {e}") | |
| return False | |
| def test_intent_classification(self) -> bool: | |
| """Test intent classification.""" | |
| try: | |
| from speech_recognizer import TwiIntentClassifier | |
| from config.config import OptimizedConfig | |
| config = OptimizedConfig() | |
| classifier = TwiIntentClassifier(config) | |
| # Test classification with sample Twi text | |
| test_texts = [ | |
| "KΙ fie", # Go home | |
| "KΙ cart mu", # Go to cart | |
| "HwehwΙ nneΙma", # Search items | |
| "Boa me", # Help me | |
| "Tua ka", # Make payment | |
| ] | |
| for text in test_texts: | |
| result = classifier.classify_intent(text) | |
| assert "intent" in result | |
| assert "confidence" in result | |
| assert result["confidence"] >= 0.0 | |
| logger.info("β Intent classification test passed") | |
| return True | |
| except Exception as e: | |
| logger.error(f"β Intent classification test failed: {e}") | |
| return False | |
| def test_end_to_end_recognition(self) -> bool: | |
| """Test complete speech recognition pipeline.""" | |
| try: | |
| from speech_recognizer import create_speech_recognizer | |
| # Create recognizer with test configuration | |
| config_overrides = { | |
| "WHISPER": {"model_size": "tiny"} # Use tiny model for faster testing | |
| } | |
| recognizer = create_speech_recognizer(config_overrides) | |
| # Create test audio | |
| test_audio = self.create_test_audio(duration=2.0) | |
| # Perform recognition | |
| result = recognizer.recognize(test_audio) | |
| # Validate result structure | |
| assert "transcription" in result | |
| assert "intent" in result | |
| assert "status" in result | |
| if result["status"] == "success": | |
| assert "text" in result["transcription"] | |
| assert "intent" in result["intent"] | |
| logger.info(f"β End-to-end test passed - Status: {result['status']}") | |
| else: | |
| logger.warning( | |
| f"β οΈ End-to-end test completed with status: {result['status']}" | |
| ) | |
| return True | |
| except Exception as e: | |
| logger.error(f"β End-to-end test failed: {e}") | |
| return False | |
| async def test_async_recognition(self) -> bool: | |
| """Test asynchronous recognition.""" | |
| try: | |
| from speech_recognizer import create_speech_recognizer | |
| recognizer = create_speech_recognizer() | |
| test_audio = self.create_test_audio(duration=1.0) | |
| # Test async recognition | |
| result = await recognizer.recognize_async(test_audio) | |
| assert "transcription" in result | |
| assert "intent" in result | |
| logger.info("β Async recognition test passed") | |
| return True | |
| except Exception as e: | |
| logger.error(f"β Async recognition test failed: {e}") | |
| return False | |
| def test_health_check(self) -> bool: | |
| """Test system health check.""" | |
| try: | |
| from speech_recognizer import create_speech_recognizer | |
| recognizer = create_speech_recognizer() | |
| health = recognizer.health_check() | |
| assert "status" in health | |
| assert "components" in health | |
| assert health["status"] in ["healthy", "degraded", "unhealthy"] | |
| logger.info(f"β Health check test passed - Status: {health['status']}") | |
| return True | |
| except Exception as e: | |
| logger.error(f"β Health check test failed: {e}") | |
| return False | |
| def test_api_server_import(self) -> bool: | |
| """Test API server import.""" | |
| try: | |
| from api_server import app | |
| assert app is not None | |
| logger.info("β API server import test passed") | |
| return True | |
| except Exception as e: | |
| logger.error(f"β API server import failed: {e}") | |
| return False | |
| def test_supported_intents(self) -> bool: | |
| """Test supported intents functionality.""" | |
| try: | |
| from speech_recognizer import create_speech_recognizer | |
| recognizer = create_speech_recognizer() | |
| intents = recognizer.get_supported_intents() | |
| assert isinstance(intents, list) | |
| assert len(intents) > 0 | |
| # Check structure of first intent | |
| if intents: | |
| intent = intents[0] | |
| assert "intent" in intent | |
| assert "description" in intent | |
| assert "examples" in intent | |
| logger.info( | |
| f"β Supported intents test passed - {len(intents)} intents found" | |
| ) | |
| return True | |
| except Exception as e: | |
| logger.error(f"β Supported intents test failed: {e}") | |
| return False | |
| def test_statistics(self) -> bool: | |
| """Test statistics functionality.""" | |
| try: | |
| from speech_recognizer import create_speech_recognizer | |
| recognizer = create_speech_recognizer() | |
| stats = recognizer.get_statistics() | |
| assert isinstance(stats, dict) | |
| assert "total_requests" in stats | |
| assert "successful_requests" in stats | |
| logger.info("β Statistics test passed") | |
| return True | |
| except Exception as e: | |
| logger.error(f"β Statistics test failed: {e}") | |
| return False | |
| async def run_all_tests(self) -> Dict[str, bool]: | |
| """Run all tests and return results.""" | |
| tests = [ | |
| ("config_import", self.test_config_import), | |
| ("speech_recognizer_import", self.test_speech_recognizer_import), | |
| ("whisper_model_load", self.test_whisper_model_load), | |
| ("audio_processing", self.test_audio_processing), | |
| ("intent_classification", self.test_intent_classification), | |
| ("end_to_end_recognition", self.test_end_to_end_recognition), | |
| ("health_check", self.test_health_check), | |
| ("api_server_import", self.test_api_server_import), | |
| ("supported_intents", self.test_supported_intents), | |
| ("statistics", self.test_statistics), | |
| ] | |
| # Run async tests | |
| async_tests = [ | |
| ("async_recognition", self.test_async_recognition), | |
| ] | |
| logger.info("=" * 60) | |
| logger.info("OPTIMIZED TWI SPEECH ENGINE - TEST SUITE") | |
| logger.info("=" * 60) | |
| # Run synchronous tests | |
| for test_name, test_func in tests: | |
| logger.info(f"\nRunning {test_name}...") | |
| try: | |
| self.test_results[test_name] = test_func() | |
| except Exception as e: | |
| logger.error(f"Test {test_name} crashed: {e}") | |
| self.test_results[test_name] = False | |
| # Run asynchronous tests | |
| for test_name, test_func in async_tests: | |
| logger.info(f"\nRunning {test_name}...") | |
| try: | |
| self.test_results[test_name] = await test_func() | |
| except Exception as e: | |
| logger.error(f"Test {test_name} crashed: {e}") | |
| self.test_results[test_name] = False | |
| # Summary | |
| self.print_test_summary() | |
| return self.test_results | |
| def print_test_summary(self): | |
| """Print test results summary.""" | |
| logger.info("\n" + "=" * 60) | |
| logger.info("TEST RESULTS SUMMARY") | |
| logger.info("=" * 60) | |
| passed = sum(1 for result in self.test_results.values() if result) | |
| total = len(self.test_results) | |
| for test_name, result in self.test_results.items(): | |
| status = "β PASS" if result else "β FAIL" | |
| logger.info(f"{test_name:25s} {status}") | |
| logger.info("-" * 60) | |
| logger.info( | |
| f"TOTAL: {passed}/{total} tests passed ({passed / total * 100:.1f}%)" | |
| ) | |
| if passed == total: | |
| logger.info("π ALL TESTS PASSED! Engine is ready for use.") | |
| elif passed >= total * 0.8: | |
| logger.info("β οΈ Most tests passed. Engine should work with minor issues.") | |
| else: | |
| logger.info("β Multiple test failures. Please check the setup.") | |
| logger.info("=" * 60) | |
| async def main(): | |
| """Main test function.""" | |
| test_suite = EngineTestSuite() | |
| try: | |
| results = await test_suite.run_all_tests() | |
| # Cleanup | |
| test_suite.cleanup() | |
| # Exit with appropriate code | |
| passed = sum(1 for result in results.values() if result) | |
| total = len(results) | |
| if passed == total: | |
| sys.exit(0) # All tests passed | |
| elif passed >= total * 0.8: | |
| sys.exit(1) # Most tests passed but some issues | |
| else: | |
| sys.exit(2) # Multiple failures | |
| except Exception as e: | |
| logger.error(f"Test suite crashed: {e}") | |
| test_suite.cleanup() | |
| sys.exit(3) | |
| if __name__ == "__main__": | |
| asyncio.run(main()) | |