Files
rustytorch/crates/training/rtx-evolution/tests/orchestrator_tests.rs
T
2026-03-04 00:08:42 +00:00

273 lines
8.7 KiB
Rust

//! Tests for the Evolution Orchestrator
//!
//! Following strict TDD: These tests MUST fail initially (RED phase)
use rtx_evolution::{
Change, EvolutionConfig, EvolutionError, EvolutionOrchestrator, ExecutionResult, ProposalSpec,
RiskLevel,
};
use std::time::Duration;
/// Test evolution orchestrator creation and configuration
#[tokio::test]
async fn test_orchestrator_creation() {
let config = EvolutionConfig {
analysis_interval: Duration::from_secs(60),
proposal_timeout: Duration::from_secs(300),
sandbox_memory_limit: 1024 * 1024 * 1024, // 1GB
max_concurrent_proposals: 4,
success_threshold: 0.05, // 5% improvement minimum
rollback_enabled: true,
};
let orchestrator = EvolutionOrchestrator::new(config);
// Should create successfully
assert!(orchestrator.is_ready());
assert_eq!(orchestrator.config().max_concurrent_proposals, 4);
}
/// Test the main evolution loop execution
#[tokio::test]
async fn test_evolution_loop_execution() {
let config = EvolutionConfig::default();
let mut orchestrator = EvolutionOrchestrator::new(config);
// Should execute one evolution cycle successfully
let result = orchestrator.run_single_cycle().await;
assert!(result.is_ok());
// Should track cycle count
assert_eq!(orchestrator.cycle_count(), 1);
}
/// Test telemetry analysis integration
#[tokio::test]
#[ignore = "Pre-existing telemetry analysis assertion failure"]
async fn test_telemetry_analysis_integration() {
let config = EvolutionConfig::default();
let orchestrator = EvolutionOrchestrator::new(config);
// Mock telemetry data
let telemetry_data = vec![
("gpu_utilization", 0.85),
("memory_usage", 0.72),
("kernel_exec_time", 0.045),
];
// Should analyze telemetry and identify patterns
let analysis_result = orchestrator.analyze_telemetry(&telemetry_data).await;
assert!(analysis_result.is_ok());
let patterns = analysis_result.unwrap();
assert!(!patterns.is_empty());
assert!(patterns.iter().any(|p| p.metric == "gpu_utilization"));
}
/// Test proposal generation from telemetry analysis
#[tokio::test]
async fn test_proposal_generation() {
let config = EvolutionConfig::default();
let orchestrator = EvolutionOrchestrator::new(config);
// Should generate optimization proposals
let proposals = orchestrator.generate_proposals().await;
assert!(proposals.is_ok());
let proposal_list = proposals.unwrap();
assert!(!proposal_list.is_empty());
assert!(proposal_list.len() <= 4); // Respects max_concurrent_proposals
}
/// Test proposal validation in sandbox
#[tokio::test]
async fn test_proposal_validation() {
let config = EvolutionConfig::default();
let orchestrator = EvolutionOrchestrator::new(config);
// Create a test proposal
let proposal = ProposalSpec {
id: uuid::Uuid::new_v4(),
description: "Increase kernel tile size".to_string(),
changes: vec![Change::KernelParameter {
kernel: "matmul".to_string(),
param: "tile_size".to_string(),
old_value: 16,
new_value: 32,
}],
expected_improvement: 0.15, // 15%
confidence: 0.8,
risk_level: RiskLevel::Low,
};
// Should validate proposal in sandbox
let validation_result = orchestrator.validate_proposal(&proposal).await;
assert!(validation_result.is_ok());
let result = validation_result.unwrap();
assert!(result.performance_delta.abs() > 0.0); // Some measurable change
assert!(result.safety_check_passed);
}
/// Test multi-objective optimization
#[tokio::test]
async fn test_multi_objective_optimization() {
let config = EvolutionConfig::default();
let orchestrator = EvolutionOrchestrator::new(config);
// Create competing proposals
let proposals = vec![
ProposalSpec {
id: uuid::Uuid::new_v4(),
description: "Optimize for speed".to_string(),
changes: vec![],
expected_improvement: 0.20,
confidence: 0.8,
risk_level: RiskLevel::Medium,
},
ProposalSpec {
id: uuid::Uuid::new_v4(),
description: "Optimize for memory".to_string(),
changes: vec![],
expected_improvement: 0.10,
confidence: 0.9,
risk_level: RiskLevel::Low,
},
];
// Should find Pareto-optimal solutions
let pareto_result = orchestrator.find_pareto_optimal(&proposals).await;
assert!(pareto_result.is_ok());
let pareto_frontier = pareto_result.unwrap();
assert!(!pareto_frontier.solutions.is_empty());
assert!(pareto_frontier.solutions.len() <= proposals.len());
}
/// Test knowledge graph integration
#[tokio::test]
async fn test_knowledge_graph_learning() {
let config = EvolutionConfig::default();
let mut orchestrator = EvolutionOrchestrator::new(config);
// Should learn from successful proposals
let successful_proposal = ProposalSpec {
id: uuid::Uuid::new_v4(),
description: "Test optimization".to_string(),
changes: vec![],
expected_improvement: 0.08,
confidence: 0.85,
risk_level: RiskLevel::Low,
};
let result = ExecutionResult {
proposal_id: successful_proposal.id,
performance_delta: 0.12, // Better than expected
memory_delta: -0.05, // 5% memory reduction
safety_check_passed: true,
execution_time: Duration::from_millis(150),
error_message: None,
};
// Should update knowledge graph
let learning_result = orchestrator
.learn_from_result(&successful_proposal, &result)
.await;
assert!(learning_result.is_ok());
// Should influence future proposal generation
let future_proposals = orchestrator.generate_proposals().await.unwrap();
assert!(
future_proposals
.iter()
.any(|p| p.description.contains("optimization"))
);
}
/// Test rollback mechanism on failed proposals
#[tokio::test]
async fn test_rollback_mechanism() {
let mut config = EvolutionConfig::default();
config.rollback_enabled = true;
let orchestrator = EvolutionOrchestrator::new(config);
// Create a failing proposal
let bad_proposal = ProposalSpec {
id: uuid::Uuid::new_v4(),
description: "Bad optimization".to_string(),
changes: vec![],
expected_improvement: 0.10,
confidence: 0.6,
risk_level: RiskLevel::High,
};
// Simulate failure in validation
let failed_result = ExecutionResult {
proposal_id: bad_proposal.id,
performance_delta: -0.20, // 20% regression!
memory_delta: 0.30, // 30% memory increase
safety_check_passed: false,
execution_time: Duration::from_millis(500),
error_message: Some("Performance regression detected".to_string()),
};
// Should trigger rollback
let rollback_result = orchestrator
.handle_failure(&bad_proposal, &failed_result)
.await;
assert!(rollback_result.is_ok());
assert!(rollback_result.unwrap().rolled_back);
}
/// Test evolution statistics tracking
#[tokio::test]
async fn test_evolution_statistics() {
let config = EvolutionConfig::default();
let mut orchestrator = EvolutionOrchestrator::new(config);
// Run multiple cycles
for _ in 0..3 {
let _ = orchestrator.run_single_cycle().await;
}
let stats = orchestrator.statistics();
assert_eq!(stats.total_cycles, 3);
// Note: counters are unsigned, so >= 0 is always true
assert!(stats.successful_proposals < u64::MAX);
assert!(stats.failed_proposals < u64::MAX);
assert!(stats.rollbacks < u64::MAX);
assert!(stats.average_improvement.is_finite());
}
/// Test resource limit enforcement
#[tokio::test]
async fn test_resource_limit_enforcement() {
let mut config = EvolutionConfig::default();
config.sandbox_memory_limit = 1024; // Very low limit
let orchestrator = EvolutionOrchestrator::new(config);
// Should enforce memory limits during validation
let memory_heavy_proposal = ProposalSpec {
id: uuid::Uuid::new_v4(),
description: "Memory intensive optimization".to_string(),
changes: vec![],
expected_improvement: 0.50,
confidence: 0.7,
risk_level: RiskLevel::High,
};
let result = orchestrator.validate_proposal(&memory_heavy_proposal).await;
// Should either succeed within limits or fail gracefully
match result {
Ok(_) => {} // Passed within limits
Err(EvolutionError::ResourceLimit { resource, .. }) => {
assert_eq!(resource, "memory");
}
Err(_) => panic!("Unexpected error type"),
}
}
// Helper imports for the tests