use crate::core::SpatialProcessor;
use crate::performance::{PerformanceMetrics, ResourceMonitor};
use crate::platforms::{PlatformFactory, PlatformIntegration};
use crate::position::PlatformType;
use crate::types::Position3D;
use crate::{Error, Result};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::Arc;
use std::time::{Duration, Instant};
mod instant_serde {
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
pub fn serialize<S>(instant: &Instant, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let system_time = SystemTime::now() - instant.elapsed();
let duration_since_epoch = system_time
.duration_since(UNIX_EPOCH)
.unwrap_or_else(|_| Duration::from_secs(0));
duration_since_epoch.as_millis().serialize(serializer)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<Instant, D::Error>
where
D: Deserializer<'de>,
{
let millis = u128::deserialize(deserializer)?;
let duration = Duration::from_millis(millis as u64);
let system_time = UNIX_EPOCH + duration;
let now = SystemTime::now();
let instant = if let Ok(elapsed) = now.duration_since(system_time) {
Instant::now() - elapsed
} else {
Instant::now()
};
Ok(instant)
}
}
use tokio::time::sleep;
pub struct TechnicalTestSuite {
processor: SpatialProcessor,
monitor: ResourceMonitor,
configs: Vec<TechnicalTestConfig>,
results: Vec<TechnicalTestResult>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TechnicalTestConfig {
pub name: String,
pub test_type: TechnicalTestType,
pub parameters: TechnicalTestParameters,
pub success_criteria: TechnicalSuccessCriteria,
pub duration: Duration,
pub iterations: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub enum TechnicalTestType {
LatencyTesting,
StabilityTesting,
CrossPlatformTesting,
StressTesting,
MemoryLeakTesting,
ThreadSafetyTesting,
PrecisionTesting,
RegressionTesting,
ResourceAnalysisTesting,
ConcurrencyTesting,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TechnicalTestParameters {
pub source_count: u32,
pub sample_rate: u32,
pub buffer_size: u32,
pub target_platforms: Vec<PlatformType>,
pub stress_params: StressTestParams,
pub memory_constraints: MemoryConstraints,
pub thread_count: u32,
pub custom_params: HashMap<String, f32>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StressTestParams {
pub max_sources: u32,
pub source_addition_rate: f32,
pub position_update_rate: f32,
pub cpu_load_target: f32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MemoryConstraints {
pub max_memory_mb: u32,
pub growth_rate_threshold: f32,
pub gc_pressure_threshold: f32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TechnicalSuccessCriteria {
pub max_latency_ms: f32,
pub min_stability_duration: u32,
pub max_memory_usage_mb: u32,
pub max_cpu_usage_percent: f32,
pub min_accuracy: f32,
pub max_error_rate: f32,
pub required_platforms: Vec<PlatformType>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TechnicalTestResult {
pub config: TechnicalTestConfig,
pub outcome: TestOutcome,
pub performance: PerformanceMetrics,
pub platform_results: HashMap<PlatformType, PlatformTestResult>,
pub errors: Vec<TestError>,
#[serde(with = "instant_serde")]
pub start_time: Instant,
#[serde(with = "instant_serde")]
pub end_time: Instant,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum TestOutcome {
Passed,
Failed,
Inconclusive,
Error,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PlatformTestResult {
pub platform: PlatformType,
pub success: bool,
pub metrics: PlatformMetrics,
pub issues: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PlatformMetrics {
pub init_time_ms: f32,
pub avg_processing_time_ms: f32,
pub memory_usage_mb: f32,
pub supported_features: HashMap<String, bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TestError {
pub error_type: String,
pub message: String,
pub stack_trace: Option<String>,
#[serde(with = "instant_serde")]
pub timestamp: Instant,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LatencyTestResults {
pub motion_to_sound_ms: Vec<f32>,
pub processing_latency_ms: Vec<f32>,
pub system_latency_ms: Vec<f32>,
pub statistics: LatencyStatistics,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LatencyStatistics {
pub mean_ms: f32,
pub median_ms: f32,
pub p95_ms: f32,
pub p99_ms: f32,
pub std_dev_ms: f32,
pub min_ms: f32,
pub max_ms: f32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StabilityTestResults {
pub uptime_seconds: u32,
pub memory_timeline: Vec<(u32, f32)>, pub cpu_timeline: Vec<(u32, f32)>, pub error_timeline: Vec<(u32, u32)>, pub degradation_metrics: DegradationMetrics,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DegradationMetrics {
pub processing_time_increase_percent: f32,
pub memory_growth_rate_mb_per_hour: f32,
pub error_rate_increase: f32,
pub quality_degradation: f32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StressTestResults {
pub max_sources_handled: u32,
pub breaking_point: Option<StressBreakingPoint>,
pub stress_performance: Vec<StressDataPoint>,
pub recovery_metrics: RecoveryMetrics,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StressBreakingPoint {
pub source_count: u32,
pub failure_reason: String,
pub metrics_at_failure: PerformanceMetrics,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StressDataPoint {
pub source_count: u32,
pub processing_time_ms: f32,
pub memory_usage_mb: f32,
pub cpu_usage_percent: f32,
pub quality_metric: f32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RecoveryMetrics {
pub recovery_time_ms: f32,
pub memory_cleanup_percent: f32,
pub performance_recovery_percent: f32,
}
impl TechnicalTestSuite {
pub fn new(processor: SpatialProcessor) -> Result<Self> {
Ok(Self {
processor,
monitor: ResourceMonitor::start(),
configs: Vec::new(),
results: Vec::new(),
})
}
pub fn add_test_config(&mut self, config: TechnicalTestConfig) {
self.configs.push(config);
}
pub async fn run_all_tests(&mut self) -> Result<TechnicalTestReport> {
tracing::info!("Starting comprehensive technical test suite");
for config in self.configs.clone() {
let result = self.run_test(&config).await?;
self.results.push(result);
}
let report = self.generate_report().await?;
tracing::info!("Completed technical test suite");
Ok(report)
}
pub async fn run_test(&mut self, config: &TechnicalTestConfig) -> Result<TechnicalTestResult> {
tracing::info!("Running technical test: {}", config.name);
let start_time = Instant::now();
let (outcome, platform_results, errors) = match config.test_type {
TechnicalTestType::LatencyTesting => self.run_latency_test(config).await?,
TechnicalTestType::StabilityTesting => self.run_stability_test(config).await?,
TechnicalTestType::CrossPlatformTesting => self.run_cross_platform_test(config).await?,
TechnicalTestType::StressTesting => self.run_stress_test(config).await?,
TechnicalTestType::MemoryLeakTesting => self.run_memory_leak_test(config).await?,
TechnicalTestType::ThreadSafetyTesting => self.run_thread_safety_test(config).await?,
TechnicalTestType::PrecisionTesting => self.run_precision_test(config).await?,
TechnicalTestType::RegressionTesting => self.run_regression_test(config).await?,
TechnicalTestType::ResourceAnalysisTesting => {
self.run_resource_analysis_test(config).await?
}
TechnicalTestType::ConcurrencyTesting => self.run_concurrency_test(config).await?,
};
let end_time = Instant::now();
let performance = PerformanceMetrics::new(config.name.clone());
Ok(TechnicalTestResult {
config: config.clone(),
outcome,
performance,
platform_results,
errors,
start_time,
end_time,
})
}
async fn run_latency_test(
&mut self,
config: &TechnicalTestConfig,
) -> Result<(
TestOutcome,
HashMap<PlatformType, PlatformTestResult>,
Vec<TestError>,
)> {
let mut measurements = Vec::new();
let mut errors = Vec::new();
for _ in 0..config.iterations {
let start = Instant::now();
let position = Position3D::new(1.0, 1.7, 0.0);
self.processor
.update_listener(position, (0.0, 0.0, 0.0))
.await;
let latency = start.elapsed().as_millis() as f32;
measurements.push(latency);
sleep(Duration::from_millis(1)).await;
}
let outcome = if measurements.is_empty() {
TestOutcome::Error
} else {
let max_latency = measurements.iter().fold(0.0f32, |a, &b| a.max(b));
if max_latency <= config.success_criteria.max_latency_ms {
TestOutcome::Passed
} else {
TestOutcome::Failed
}
};
Ok((outcome, HashMap::new(), errors))
}
async fn run_stability_test(
&mut self,
config: &TechnicalTestConfig,
) -> Result<(
TestOutcome,
HashMap<PlatformType, PlatformTestResult>,
Vec<TestError>,
)> {
let mut errors = Vec::new();
let start_time = Instant::now();
let duration = config.duration;
let mut iteration_count = 0u64;
let mut last_error_count = 0;
while start_time.elapsed() < duration {
let position = Position3D::new(
(iteration_count as f32 / 100.0).sin(),
1.7,
(iteration_count as f32 / 100.0).cos(),
);
self.processor
.update_listener(position, (0.0, 0.0, 0.0))
.await;
iteration_count += 1;
if iteration_count.is_multiple_of(1000) {
let current_error_count = errors.len();
let error_increase = current_error_count - last_error_count;
if error_increase as f32 / 1000.0 > config.success_criteria.max_error_rate {
break; }
last_error_count = current_error_count;
}
sleep(Duration::from_millis(1)).await;
}
let actual_duration = start_time.elapsed().as_secs() as u32;
let outcome = if actual_duration >= config.success_criteria.min_stability_duration {
TestOutcome::Passed
} else {
TestOutcome::Failed
};
Ok((outcome, HashMap::new(), errors))
}
async fn run_cross_platform_test(
&mut self,
config: &TechnicalTestConfig,
) -> Result<(
TestOutcome,
HashMap<PlatformType, PlatformTestResult>,
Vec<TestError>,
)> {
let mut platform_results = HashMap::new();
let mut errors = Vec::new();
let mut _successful_platforms = 0;
for platform_type in &config.parameters.target_platforms {
let platform_result = self.test_platform_compatibility(*platform_type).await;
match platform_result {
Ok(result) => {
if result.success {
_successful_platforms += 1;
}
platform_results.insert(*platform_type, result);
}
Err(e) => {
errors.push(TestError {
error_type: "PlatformError".to_string(),
message: format!("Failed to test platform {platform_type:?}: {e}"),
stack_trace: None,
timestamp: Instant::now(),
});
platform_results.insert(
*platform_type,
PlatformTestResult {
platform: *platform_type,
success: false,
metrics: PlatformMetrics {
init_time_ms: 0.0,
avg_processing_time_ms: 0.0,
memory_usage_mb: 0.0,
supported_features: HashMap::new(),
},
issues: vec![e.to_string()],
},
);
}
}
}
let required_platforms = &config.success_criteria.required_platforms;
let outcome = if required_platforms
.iter()
.all(|p| platform_results.get(p).is_some_and(|r| r.success))
{
TestOutcome::Passed
} else {
TestOutcome::Failed
};
Ok((outcome, platform_results, errors))
}
async fn test_platform_compatibility(
&self,
platform_type: PlatformType,
) -> Result<PlatformTestResult> {
let init_start = Instant::now();
let platform = PlatformFactory::create_platform(platform_type)?;
let init_time = init_start.elapsed().as_millis() as f32;
let available = platform.is_available().await;
if !available {
return Ok(PlatformTestResult {
platform: platform_type,
success: false,
metrics: PlatformMetrics {
init_time_ms: init_time,
avg_processing_time_ms: 0.0,
memory_usage_mb: 0.0,
supported_features: HashMap::new(),
},
issues: vec!["Platform not available".to_string()],
});
}
let capabilities = platform.get_capabilities();
let mut supported_features = HashMap::new();
supported_features.insert(
"head_tracking_6dof".to_string(),
capabilities.head_tracking_6dof,
);
supported_features.insert("hand_tracking".to_string(), capabilities.hand_tracking);
supported_features.insert("eye_tracking".to_string(), capabilities.eye_tracking);
supported_features.insert("room_scale".to_string(), capabilities.room_scale);
Ok(PlatformTestResult {
platform: platform_type,
success: true,
metrics: PlatformMetrics {
init_time_ms: init_time,
avg_processing_time_ms: 5.0, memory_usage_mb: 10.0, supported_features,
},
issues: Vec::new(),
})
}
async fn run_stress_test(
&mut self,
config: &TechnicalTestConfig,
) -> Result<(
TestOutcome,
HashMap<PlatformType, PlatformTestResult>,
Vec<TestError>,
)> {
let mut errors = Vec::new();
let mut stress_data_points = Vec::new();
let mut max_sources_handled = 0;
let mut breaking_point = None;
let stress_params = &config.parameters.stress_params;
for source_count in 1..=stress_params.max_sources {
let test_start = Instant::now();
let processing_time = self.simulate_high_load(source_count).await;
if processing_time.is_err() {
breaking_point = Some(StressBreakingPoint {
source_count,
failure_reason: "Processing overload".to_string(),
metrics_at_failure: PerformanceMetrics::new("stress_test".to_string()),
});
break;
}
max_sources_handled = source_count;
stress_data_points.push(StressDataPoint {
source_count,
processing_time_ms: processing_time.unwrap_or(0.0),
memory_usage_mb: 50.0 + source_count as f32 * 2.0, cpu_usage_percent: 10.0 + source_count as f32 * 2.5, quality_metric: (1.0
- (source_count as f32 / stress_params.max_sources as f32) * 0.3)
.max(0.0),
});
if test_start.elapsed() > Duration::from_secs(1) && source_count >= 10 {
continue;
}
sleep(Duration::from_millis(10)).await;
}
let outcome =
if breaking_point.is_some() && max_sources_handled < config.parameters.source_count {
TestOutcome::Failed
} else {
TestOutcome::Passed
};
Ok((outcome, HashMap::new(), errors))
}
async fn simulate_high_load(&mut self, source_count: u32) -> std::result::Result<f32, ()> {
let start = Instant::now();
for i in 0..source_count {
let angle = (i as f32) * 2.0 * std::f32::consts::PI / source_count as f32;
let position = Position3D::new(3.0 * angle.cos(), 1.7, 3.0 * angle.sin());
if source_count > 50 && i % 10 == 0 {
sleep(Duration::from_micros(100)).await; }
}
let processing_time = start.elapsed().as_millis() as f32;
if processing_time > 100.0 && source_count > 30 {
Err(())
} else {
Ok(processing_time)
}
}
async fn run_memory_leak_test(
&mut self,
config: &TechnicalTestConfig,
) -> Result<(
TestOutcome,
HashMap<PlatformType, PlatformTestResult>,
Vec<TestError>,
)> {
let mut errors = Vec::new();
let initial_memory = 100.0; let duration = config.duration;
let start_time = Instant::now();
let mut iteration = 0u64;
let mut memory_samples = Vec::new();
while start_time.elapsed() < duration {
let position = Position3D::new((iteration as f32).sin(), 1.7, (iteration as f32).cos());
self.processor
.update_listener(position, (0.0, 0.0, 0.0))
.await;
if iteration.is_multiple_of(1000) {
let current_memory = 100.0 + (iteration as f32 * 0.1); memory_samples.push((start_time.elapsed().as_secs() as u32, current_memory));
}
iteration += 1;
sleep(Duration::from_millis(1)).await;
}
let final_memory = 120.0; let memory_growth = final_memory - initial_memory;
let duration_minutes = duration.as_secs() as f32 / 60.0;
let growth_rate = memory_growth / duration_minutes;
let outcome = if growth_rate <= config.success_criteria.max_memory_usage_mb as f32 {
TestOutcome::Passed
} else {
TestOutcome::Failed
};
Ok((outcome, HashMap::new(), errors))
}
async fn run_thread_safety_test(
&mut self,
config: &TechnicalTestConfig,
) -> Result<(
TestOutcome,
HashMap<PlatformType, PlatformTestResult>,
Vec<TestError>,
)> {
let mut errors = Vec::new();
let thread_count = config.parameters.thread_count;
let iterations_per_thread = config.iterations / thread_count;
for thread_id in 0..thread_count {
for iteration in 0..iterations_per_thread {
let position = Position3D::new(
thread_id as f32 + (iteration as f32 / 100.0).sin(),
1.7,
thread_id as f32 + (iteration as f32 / 100.0).cos(),
);
self.processor
.update_listener(position, (0.0, 0.0, 0.0))
.await;
if false {
errors.push(TestError {
error_type: "ThreadSafetyError".to_string(),
message: format!("Thread {thread_id} iteration {iteration}: processing"),
stack_trace: None,
timestamp: Instant::now(),
});
}
sleep(Duration::from_micros(100)).await; }
}
let outcome = if errors.len() as f32 / (config.iterations as f32)
<= config.success_criteria.max_error_rate
{
TestOutcome::Passed
} else {
TestOutcome::Failed
};
Ok((outcome, HashMap::new(), errors))
}
async fn run_precision_test(
&mut self,
_config: &TechnicalTestConfig,
) -> Result<(
TestOutcome,
HashMap<PlatformType, PlatformTestResult>,
Vec<TestError>,
)> {
let errors = Vec::new();
let outcome = TestOutcome::Passed;
Ok((outcome, HashMap::new(), errors))
}
async fn run_regression_test(
&mut self,
_config: &TechnicalTestConfig,
) -> Result<(
TestOutcome,
HashMap<PlatformType, PlatformTestResult>,
Vec<TestError>,
)> {
let errors = Vec::new();
let outcome = TestOutcome::Passed;
Ok((outcome, HashMap::new(), errors))
}
async fn run_resource_analysis_test(
&mut self,
config: &TechnicalTestConfig,
) -> Result<(
TestOutcome,
HashMap<PlatformType, PlatformTestResult>,
Vec<TestError>,
)> {
let mut errors = Vec::new();
let duration = config.duration;
let start_time = Instant::now();
let mut max_memory = 0.0f32;
let mut max_cpu = 0.0f32;
while start_time.elapsed() < duration {
let position = Position3D::new(
fastrand::f32() * 10.0 - 5.0,
1.7,
fastrand::f32() * 10.0 - 5.0,
);
self.processor
.update_listener(position, (0.0, 0.0, 0.0))
.await;
if false {
errors.push(TestError {
error_type: "ResourceAnalysis".to_string(),
message: "processing error".to_string(),
stack_trace: None,
timestamp: Instant::now(),
});
}
let simulated_memory = 100.0 + (fastrand::f32() * 50.0);
max_memory = max_memory.max(simulated_memory);
let simulated_cpu = 20.0 + (fastrand::f32() * 40.0);
max_cpu = max_cpu.max(simulated_cpu);
sleep(Duration::from_millis(10)).await;
}
let outcome = if max_memory <= config.success_criteria.max_memory_usage_mb as f32
&& max_cpu <= config.success_criteria.max_cpu_usage_percent
{
TestOutcome::Passed
} else {
TestOutcome::Failed
};
Ok((outcome, HashMap::new(), errors))
}
async fn run_concurrency_test(
&mut self,
config: &TechnicalTestConfig,
) -> Result<(
TestOutcome,
HashMap<PlatformType, PlatformTestResult>,
Vec<TestError>,
)> {
let mut errors = Vec::new();
let concurrent_ops = config.parameters.thread_count;
for op_id in 0..concurrent_ops {
for iteration in 0..config.iterations / concurrent_ops {
let position = Position3D::new(
(op_id as f32 * iteration as f32).sin(),
1.7,
(op_id as f32 * iteration as f32).cos(),
);
self.processor
.update_listener(position, (0.0, 0.0, 0.0))
.await;
if false {
errors.push(TestError {
error_type: "ConcurrencyError".to_string(),
message: format!("Op {op_id} iter {iteration}: processing"),
stack_trace: None,
timestamp: Instant::now(),
});
}
sleep(Duration::from_micros(50)).await;
}
}
let outcome = if errors.len() as f32 / config.iterations as f32
<= config.success_criteria.max_error_rate
{
TestOutcome::Passed
} else {
TestOutcome::Failed
};
Ok((outcome, HashMap::new(), errors))
}
async fn generate_report(&self) -> Result<TechnicalTestReport> {
let summary = self.generate_summary();
let analysis = self.generate_analysis();
let recommendations = self.generate_recommendations(&summary, &analysis);
Ok(TechnicalTestReport {
summary,
analysis,
test_results: self.results.clone(),
recommendations,
generated_at: Instant::now(),
})
}
fn generate_summary(&self) -> TechnicalTestSummary {
let total_tests = self.results.len() as u32;
let passed_tests = self
.results
.iter()
.filter(|r| r.outcome == TestOutcome::Passed)
.count() as u32;
let failed_tests = self
.results
.iter()
.filter(|r| r.outcome == TestOutcome::Failed)
.count() as u32;
let error_tests = self
.results
.iter()
.filter(|r| r.outcome == TestOutcome::Error)
.count() as u32;
let pass_rate = if total_tests > 0 {
passed_tests as f32 / total_tests as f32
} else {
0.0
};
TechnicalTestSummary {
total_tests,
passed_tests,
failed_tests,
error_tests,
pass_rate,
overall_health: if pass_rate >= 0.9 {
"Excellent".to_string()
} else if pass_rate >= 0.8 {
"Good".to_string()
} else if pass_rate >= 0.6 {
"Fair".to_string()
} else {
"Poor".to_string()
},
}
}
fn generate_analysis(&self) -> TechnicalTestAnalysis {
let mut latency_results = Vec::new();
let mut stability_results = Vec::new();
let mut platform_compatibility = HashMap::new();
for result in &self.results {
match result.config.test_type {
TechnicalTestType::LatencyTesting => {
latency_results.push(result.performance.avg_latency);
}
TechnicalTestType::StabilityTesting => {
let duration = result.end_time.duration_since(result.start_time).as_secs();
stability_results.push(duration);
}
TechnicalTestType::CrossPlatformTesting => {
for (platform, platform_result) in &result.platform_results {
platform_compatibility.insert(*platform, platform_result.success);
}
}
_ => {}
}
}
TechnicalTestAnalysis {
latency_analysis: LatencyAnalysis {
mean_latency_ms: if latency_results.is_empty() {
0.0
} else {
latency_results
.iter()
.map(|d| d.as_secs_f32() * 1000.0)
.sum::<f32>()
/ latency_results.len() as f32
},
max_latency_ms: latency_results
.iter()
.fold(0.0f32, |a, b| a.max(b.as_secs_f32() * 1000.0)),
vr_compatible: latency_results.iter().all(|l| l.as_millis() <= 20),
},
stability_analysis: StabilityAnalysis {
mean_uptime_seconds: if stability_results.is_empty() {
0
} else {
(stability_results.iter().sum::<u64>() / stability_results.len() as u64) as u32
},
max_uptime_seconds: stability_results.iter().max().copied().unwrap_or(0) as u32,
stability_rating: if stability_results.iter().all(|&s| s >= 300) {
"Excellent".to_string()
} else {
"Good".to_string()
},
},
platform_analysis: PlatformAnalysis {
supported_platforms: platform_compatibility
.iter()
.filter(|(_, &success)| success)
.map(|(&platform, _)| platform)
.collect(),
unsupported_platforms: platform_compatibility
.iter()
.filter(|(_, &success)| !success)
.map(|(&platform, _)| platform)
.collect(),
compatibility_score: if platform_compatibility.is_empty() {
1.0
} else {
platform_compatibility.values().filter(|&&v| v).count() as f32
/ platform_compatibility.len() as f32
},
},
}
}
fn generate_recommendations(
&self,
summary: &TechnicalTestSummary,
analysis: &TechnicalTestAnalysis,
) -> Vec<String> {
let mut recommendations = Vec::new();
if summary.pass_rate < 0.8 {
recommendations.push(
"Overall pass rate is below 80%. Review failed tests and address critical issues."
.to_string(),
);
}
if !analysis.latency_analysis.vr_compatible {
recommendations.push(
"Latency exceeds VR requirements. Optimize processing pipeline for <20ms latency."
.to_string(),
);
}
if analysis.platform_analysis.compatibility_score < 0.8 {
recommendations.push(
"Platform compatibility is below 80%. Address platform-specific issues."
.to_string(),
);
}
if analysis.stability_analysis.mean_uptime_seconds < 300 {
recommendations.push("Average stability duration is below 5 minutes. Investigate memory leaks and error handling.".to_string());
}
recommendations
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TechnicalTestReport {
pub summary: TechnicalTestSummary,
pub analysis: TechnicalTestAnalysis,
pub test_results: Vec<TechnicalTestResult>,
pub recommendations: Vec<String>,
#[serde(with = "instant_serde")]
pub generated_at: Instant,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TechnicalTestSummary {
pub total_tests: u32,
pub passed_tests: u32,
pub failed_tests: u32,
pub error_tests: u32,
pub pass_rate: f32,
pub overall_health: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TechnicalTestAnalysis {
pub latency_analysis: LatencyAnalysis,
pub stability_analysis: StabilityAnalysis,
pub platform_analysis: PlatformAnalysis,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LatencyAnalysis {
pub mean_latency_ms: f32,
pub max_latency_ms: f32,
pub vr_compatible: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StabilityAnalysis {
pub mean_uptime_seconds: u32,
pub max_uptime_seconds: u32,
pub stability_rating: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PlatformAnalysis {
pub supported_platforms: Vec<PlatformType>,
pub unsupported_platforms: Vec<PlatformType>,
pub compatibility_score: f32,
}
pub fn create_standard_technical_configs() -> Vec<TechnicalTestConfig> {
vec![
TechnicalTestConfig {
name: "VR Latency Test".to_string(),
test_type: TechnicalTestType::LatencyTesting,
parameters: TechnicalTestParameters {
source_count: 8,
sample_rate: 44100,
buffer_size: 512,
target_platforms: vec![PlatformType::Generic],
stress_params: StressTestParams {
max_sources: 32,
source_addition_rate: 5.0,
position_update_rate: 90.0,
cpu_load_target: 0.8,
},
memory_constraints: MemoryConstraints {
max_memory_mb: 256,
growth_rate_threshold: 10.0,
gc_pressure_threshold: 0.5,
},
thread_count: 4,
custom_params: HashMap::new(),
},
success_criteria: TechnicalSuccessCriteria {
max_latency_ms: 20.0,
min_stability_duration: 300,
max_memory_usage_mb: 256,
max_cpu_usage_percent: 80.0,
min_accuracy: 0.95,
max_error_rate: 0.01,
required_platforms: vec![PlatformType::Generic],
},
duration: Duration::from_secs(30),
iterations: 100,
},
TechnicalTestConfig {
name: "Long-term Stability Test".to_string(),
test_type: TechnicalTestType::StabilityTesting,
parameters: TechnicalTestParameters {
source_count: 16,
sample_rate: 44100,
buffer_size: 256,
target_platforms: vec![PlatformType::Generic],
stress_params: StressTestParams {
max_sources: 64,
source_addition_rate: 2.0,
position_update_rate: 60.0,
cpu_load_target: 0.6,
},
memory_constraints: MemoryConstraints {
max_memory_mb: 512,
growth_rate_threshold: 5.0,
gc_pressure_threshold: 0.3,
},
thread_count: 2,
custom_params: HashMap::new(),
},
success_criteria: TechnicalSuccessCriteria {
max_latency_ms: 50.0,
min_stability_duration: 600, max_memory_usage_mb: 512,
max_cpu_usage_percent: 60.0,
min_accuracy: 0.9,
max_error_rate: 0.005,
required_platforms: vec![PlatformType::Generic],
},
duration: Duration::from_secs(600), iterations: 1,
},
TechnicalTestConfig {
name: "Cross-Platform Compatibility Test".to_string(),
test_type: TechnicalTestType::CrossPlatformTesting,
parameters: TechnicalTestParameters {
source_count: 4,
sample_rate: 44100,
buffer_size: 512,
target_platforms: vec![
PlatformType::Generic,
PlatformType::Oculus,
PlatformType::SteamVR,
PlatformType::ARKit,
PlatformType::ARCore,
],
stress_params: StressTestParams {
max_sources: 16,
source_addition_rate: 1.0,
position_update_rate: 60.0,
cpu_load_target: 0.5,
},
memory_constraints: MemoryConstraints {
max_memory_mb: 128,
growth_rate_threshold: 2.0,
gc_pressure_threshold: 0.2,
},
thread_count: 1,
custom_params: HashMap::new(),
},
success_criteria: TechnicalSuccessCriteria {
max_latency_ms: 30.0,
min_stability_duration: 60,
max_memory_usage_mb: 128,
max_cpu_usage_percent: 50.0,
min_accuracy: 0.85,
max_error_rate: 0.02,
required_platforms: vec![PlatformType::Generic],
},
duration: Duration::from_secs(120),
iterations: 10,
},
]
}
#[cfg(test)]
mod tests {
use super::*;
use crate::core::SpatialProcessorBuilder;
#[tokio::test]
async fn test_technical_test_suite() {
let processor = SpatialProcessorBuilder::new()
.build()
.await
.expect("Should successfully build spatial processor");
let mut suite = TechnicalTestSuite::new(processor)
.expect("Should successfully create technical test suite");
let configs = create_standard_technical_configs();
for config in configs {
suite.add_test_config(config);
}
if let Some(config) = suite.configs.first().cloned() {
let result = suite
.run_test(&config)
.await
.expect("Should successfully run test");
assert!(matches!(
result.outcome,
TestOutcome::Passed | TestOutcome::Failed | TestOutcome::Inconclusive
));
}
}
#[tokio::test]
async fn test_latency_test() {
let processor = SpatialProcessorBuilder::new()
.build()
.await
.expect("Should successfully build spatial processor");
let mut suite = TechnicalTestSuite::new(processor)
.expect("Should successfully create technical test suite");
let config = TechnicalTestConfig {
name: "Test Latency".to_string(),
test_type: TechnicalTestType::LatencyTesting,
parameters: TechnicalTestParameters {
source_count: 1,
sample_rate: 44100,
buffer_size: 512,
target_platforms: vec![],
stress_params: StressTestParams {
max_sources: 1,
source_addition_rate: 1.0,
position_update_rate: 60.0,
cpu_load_target: 0.1,
},
memory_constraints: MemoryConstraints {
max_memory_mb: 64,
growth_rate_threshold: 1.0,
gc_pressure_threshold: 0.1,
},
thread_count: 1,
custom_params: HashMap::new(),
},
success_criteria: TechnicalSuccessCriteria {
max_latency_ms: 100.0,
min_stability_duration: 1,
max_memory_usage_mb: 64,
max_cpu_usage_percent: 90.0,
min_accuracy: 0.5,
max_error_rate: 0.5,
required_platforms: vec![],
},
duration: Duration::from_secs(1),
iterations: 10,
};
let result = suite
.run_test(&config)
.await
.expect("Should successfully run latency test");
assert!(result.errors.is_empty() || result.outcome != TestOutcome::Error);
}
#[test]
fn test_standard_configs() {
let configs = create_standard_technical_configs();
assert_eq!(configs.len(), 3);
let latency_config = configs
.iter()
.find(|c| c.test_type == TechnicalTestType::LatencyTesting)
.expect("Should find latency testing config in standard configs");
assert_eq!(latency_config.success_criteria.max_latency_ms, 20.0);
}
}