#!/usr/bin/env python3 """ Test du système de cleanup complet Valide que tous les composants sont correctement nettoyés à l'arrêt. Tests avec de vraies ressources système plutôt que des mocks. """ import logging import sys import tempfile import time from pathlib import Path import numpy as np # Add current directory to path for imports sys.path.insert(0, str(Path(__file__).parent)) from core.system import initialize_system_cleanup, get_cleanup_manager, shutdown_system, register_cleanup_function def test_real_memory_manager_cleanup(): """Test cleanup avec le vrai MemoryManager.""" print("🧪 Testing Real Memory Manager Cleanup...") # 1. Créer et utiliser le vrai MemoryManager try: from core.execution.memory_cache import get_memory_manager, EffectiveLRUCache print("1. Creating real MemoryManager...") memory_manager = get_memory_manager(enable_monitoring=False, enable_gpu_management=False) # 2. Créer des ressources réelles à nettoyer print("2. Creating real resources...") # Créer un cache réel avec des données test_cache = EffectiveLRUCache(max_size=10, max_memory_mb=1.0, enable_monitoring=False) # Ajouter des données réelles au cache for i in range(5): test_data = np.random.randn(100).astype(np.float32) # Vraies données numpy test_cache.put(f"test_key_{i}", test_data) print(f" ✓ Cache created with {len(test_cache)} items") # Enregistrer le cache pour cleanup memory_manager.register_resource( "test_cache", test_cache, lambda cache: cache.clear(), {"type": "test_cache", "created_by": "test"} ) # 3. Créer des fichiers temporaires réels temp_files = [] for i in range(3): temp_file = tempfile.NamedTemporaryFile(delete=False) temp_file.write(b"test data for cleanup") temp_file.close() temp_files.append(temp_file.name) def cleanup_temp_files(): for file_path in temp_files: try: Path(file_path).unlink(missing_ok=True) print(f" ✓ Cleaned up temp file: {Path(file_path).name}") except Exception as e: print(f" ⚠ Error cleaning temp file: {e}") memory_manager.register_resource( "temp_files", temp_files, lambda files: cleanup_temp_files(), {"type": "temp_files", "count": len(temp_files)} ) print(f" ✓ Registered {len(temp_files)} temp files for cleanup") # 4. Vérifier l'état avant cleanup stats_before = memory_manager.get_stats() print(f" ✓ Resources registered: {stats_before['registered_resources']}") print(f" ✓ Memory usage: {stats_before['current_memory_mb']:.1f}MB") # 5. Tester le cleanup print("3. Testing cleanup...") memory_manager.shutdown() # 6. Vérifier que les ressources ont été nettoyées print("4. Verifying cleanup...") # Vérifier que le cache est vide assert len(test_cache) == 0, f"Cache not cleared: {len(test_cache)} items remain" print(" ✓ Cache cleared successfully") # Vérifier que les fichiers temporaires ont été supprimés remaining_files = [f for f in temp_files if Path(f).exists()] assert len(remaining_files) == 0, f"Temp files not cleaned: {remaining_files}" print(" ✓ Temp files cleaned successfully") # Vérifier l'état du memory manager stats_after = memory_manager.get_stats() assert stats_after['registered_resources'] == 0, "Resources not unregistered" print(" ✓ All resources unregistered") print("✅ Real Memory Manager cleanup test completed successfully!") return True except ImportError as e: print(f"⚠️ Memory Manager not available: {e}") return True # Skip test if not available except Exception as e: print(f"❌ Memory Manager test failed: {e}") raise def test_real_system_integration(): """Test avec l'intégration système complète.""" print("🧪 Testing Real System Integration...") # Reset cleanup manager for clean test from core.system.cleanup_manager import CleanupManager CleanupManager._instance = None # 1. Initialiser le système complet print("1. Initializing complete system...") initialize_system_cleanup() manager = get_cleanup_manager() initial_functions = len(manager._cleanup_functions) initial_objects = len(manager._cleanup_objects) print(f" ✓ Initial cleanup functions: {initial_functions}") print(f" ✓ Initial cleanup objects: {initial_objects}") # 2. Créer des ressources réelles supplémentaires print("2. Creating additional real resources...") # Créer un fichier de log réel log_file = tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.log') log_file.write("Test log entry\n") log_file.write("Another log entry\n") log_file.close() cleanup_executed = [] def cleanup_log_file(): try: Path(log_file.name).unlink(missing_ok=True) cleanup_executed.append("log_file_cleaned") print(f" ✓ Log file cleaned: {Path(log_file.name).name}") except Exception as e: print(f" ⚠ Error cleaning log file: {e}") register_cleanup_function(cleanup_log_file, "Test Log File Cleanup") # Créer un objet avec méthode shutdown réelle class TestResource: def __init__(self): self.active = True self.data = list(range(1000)) # Vraies données def shutdown(self): self.active = False self.data.clear() cleanup_executed.append("test_resource_shutdown") print(" ✓ Test resource shutdown completed") test_resource = TestResource() manager.register_cleanup_object(test_resource, "shutdown") print(f" ✓ Added cleanup function, total: {len(manager._cleanup_functions)}") print(f" ✓ Added cleanup object, total: {len(manager._cleanup_objects)}") # 3. Vérifier l'état avant shutdown assert test_resource.active, "Test resource should be active" assert len(test_resource.data) == 1000, "Test resource should have data" assert Path(log_file.name).exists(), "Log file should exist" # 4. Effectuer le shutdown print("3. Performing system shutdown...") shutdown_system() # 5. Vérifier les résultats print("4. Verifying shutdown results...") # Vérifier que les fonctions de cleanup ont été exécutées assert "log_file_cleaned" in cleanup_executed, "Log file cleanup not executed" assert "test_resource_shutdown" in cleanup_executed, "Test resource shutdown not executed" print(" ✓ All cleanup functions executed") # Vérifier que les ressources ont été nettoyées assert not test_resource.active, "Test resource should be inactive" assert len(test_resource.data) == 0, "Test resource data should be cleared" assert not Path(log_file.name).exists(), "Log file should be deleted" print(" ✓ All resources properly cleaned") # Vérifier l'état du manager assert manager.is_shutdown_in_progress(), "Shutdown should be in progress" print(" ✓ Shutdown state correctly set") print("✅ Real system integration test completed successfully!") return True def test_real_cache_cleanup(): """Test cleanup avec un vrai cache LRU.""" print("🧪 Testing Real Cache Cleanup...") try: from core.execution.memory_cache import EffectiveLRUCache # 1. Créer un cache réel avec monitoring désactivé pour le test print("1. Creating real LRU cache...") cache = EffectiveLRUCache( max_size=50, max_memory_mb=5.0, enable_monitoring=False # Désactiver pour test propre ) # 2. Remplir le cache avec des données réelles print("2. Filling cache with real data...") for i in range(30): # Créer des vecteurs numpy réels (simule des embeddings) vector = np.random.randn(512).astype(np.float32) cache.put(f"embedding_{i}", vector) initial_size = len(cache) initial_memory = cache.get_memory_usage() print(f" ✓ Cache filled: {initial_size} items") print(f" ✓ Memory usage: {initial_memory['current_mb']:.2f}MB") # 3. Vérifier les statistiques stats = cache.get_stats() assert stats['size'] == initial_size, "Cache size mismatch" assert stats['hits'] == 0, "Should have no hits yet" # 4. Tester les opérations du cache print("3. Testing cache operations...") # Test hit value = cache.get("embedding_0") assert value is not None, "Should retrieve existing value" assert isinstance(value, np.ndarray), "Should return numpy array" # Test miss value = cache.get("nonexistent") assert value is None, "Should return None for missing key" stats_after = cache.get_stats() assert stats_after['hits'] == 1, "Should have 1 hit" assert stats_after['misses'] == 1, "Should have 1 miss" print(" ✓ Cache operations working correctly") # 5. Tester le cleanup print("4. Testing cache cleanup...") cache.stop_monitoring() # Arrêter le monitoring cache.clear() # 6. Vérifier que le cache est vide assert len(cache) == 0, "Cache should be empty after clear" final_memory = cache.get_memory_usage() assert final_memory['current_bytes'] == 0, "Memory should be freed" print(" ✓ Cache cleared successfully") print(f" ✓ Memory freed: {initial_memory['current_mb']:.2f}MB → 0MB") print("✅ Real cache cleanup test completed successfully!") return True except ImportError as e: print(f"⚠️ Cache not available: {e}") return True # Skip test if not available def test_weakref_cleanup(): """Test cleanup avec weak references (vraie fonctionnalité du CleanupManager).""" print("🧪 Testing Weak Reference Cleanup...") from core.system.cleanup_manager import CleanupManager import gc # Reset pour test propre - forcer une nouvelle instance CleanupManager._instance = None # 1. Créer un manager frais print("1. Creating fresh cleanup manager...") manager = CleanupManager() # Créer directement pour éviter l'état shutdown # 2. Créer un objet réel avec méthode shutdown print("2. Creating real object with shutdown method...") class RealResource: def __init__(self, name): self.name = name self.active = True self.shutdown_called = False def shutdown(self): self.active = False self.shutdown_called = True print(f" ✓ {self.name} shutdown called") # Créer et enregistrer une ressource resource = RealResource("TestResource1") manager.register_cleanup_object(resource, "shutdown") initial_objects = len(manager._cleanup_objects) print(f" ✓ Registered {initial_objects} cleanup object(s)") # 3. Vérifier que la weak reference fonctionne assert initial_objects > 0, "Should have registered objects" # 4. Supprimer la référence forte et forcer GC print("3. Testing weak reference behavior...") resource_id = id(resource) del resource gc.collect() # Force garbage collection # La weak reference devrait avoir été nettoyée automatiquement # (le callback devrait avoir été appelé) time.sleep(0.1) # Petit délai pour le callback print(" ✓ Weak reference cleanup mechanism working") # 5. Créer une nouvelle ressource et tester le shutdown print("4. Testing shutdown with active resource...") resource2 = RealResource("TestResource2") manager.register_cleanup_object(resource2, "shutdown") # Shutdown devrait appeler la méthode shutdown manager.shutdown() assert resource2.shutdown_called, "Shutdown method should have been called" assert not resource2.active, "Resource should be inactive" print(" ✓ Resource shutdown executed correctly") print("✅ Weak reference cleanup test completed successfully!") return True def main(): """Fonction principale de test.""" print("🎯 RPA Vision V3 - System Cleanup Test") print("=" * 50) # Configuration du logging pour voir les vraies opérations logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' ) try: # Test 1: Memory Manager avec vraies ressources (test isolé) test_real_memory_manager_cleanup() print() # Test 2: Cache LRU réel (test isolé) test_real_cache_cleanup() print() # Test 3: Weak references (test isolé) test_weakref_cleanup() print() # Test 4: Intégration système complète (test final car fait shutdown) test_real_system_integration() print() print("🎉 All real functionality tests passed!") return 0 except Exception as e: print(f"❌ Test failed: {e}") import traceback traceback.print_exc() return 1 if __name__ == "__main__": sys.exit(main())