1use crate::performance::{PerformanceMetrics, ResourceMonitor};
8use crate::types::Position3D;
9use crate::{Error, Result, SpatialProcessor};
10use serde::{Deserialize, Serialize};
11use std::collections::HashMap;
12use std::time::{Duration, Instant};
13
14pub struct PerformanceTargetValidator {
16 targets: PerformanceTargets,
18 resource_monitor: ResourceMonitor,
20 results: Vec<PerformanceValidationResult>,
22 test_configs: HashMap<TargetCategory, TargetTestConfig>,
24}
25
26#[derive(Debug, Clone, Serialize, Deserialize)]
28pub struct PerformanceTargets {
29 pub realtime: RealtimeTargets,
31 pub quality: QualityTargets,
33 pub scalability: ScalabilityTargets,
35 pub resources: ResourceTargets,
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize)]
41pub struct RealtimeTargets {
42 pub vr_ar_latency_ms: f64,
44 pub gaming_latency_ms: f64,
46 pub general_latency_ms: f64,
48 pub max_jitter_ms: f64,
50}
51
52#[derive(Debug, Clone, Serialize, Deserialize)]
54pub struct QualityTargets {
55 pub localization_accuracy_percent: f32,
57 pub distance_accuracy_percent: f32,
59 pub elevation_accuracy_percent: f32,
61 pub naturalness_mos: f32,
63 pub min_snr_db: f32,
65}
66
67#[derive(Debug, Clone, Serialize, Deserialize)]
69pub struct ScalabilityTargets {
70 pub max_sources: u32,
72 pub max_room_complexity: u32,
74 pub vr_update_rate_hz: f32,
76 pub general_update_rate_hz: f32,
78 pub max_rendering_distance_m: f32,
80}
81
82#[derive(Debug, Clone, Serialize, Deserialize)]
84pub struct ResourceTargets {
85 pub max_cpu_percent: f32,
87 pub max_memory_mb: u64,
89 pub max_gpu_percent: f32,
91 pub max_power_watts: f32,
93}
94
95#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
97pub enum TargetCategory {
98 Latency,
100 Quality,
102 Scalability,
104 Resources,
106}
107
108#[derive(Debug, Clone, Serialize, Deserialize)]
110pub struct TargetTestConfig {
111 pub duration: Duration,
113 pub iterations: u32,
115 pub source_counts: Vec<u32>,
117 pub test_positions: Vec<Position3D>,
119 pub warmup_duration: Duration,
121}
122
123#[derive(Debug, Clone, Serialize, Deserialize)]
125pub struct PerformanceValidationResult {
126 pub category: TargetCategory,
128 pub timestamp: String,
130 pub passed: bool,
132 pub measurements: PerformanceMeasurements,
134 pub target_comparisons: Vec<TargetComparison>,
136 pub recommendations: Vec<String>,
138}
139
140#[derive(Debug, Clone, Serialize, Deserialize)]
142pub struct PerformanceMeasurements {
143 pub latency_ms: LatencyMeasurements,
145 pub quality: QualityMeasurements,
147 pub scalability: ScalabilityMeasurements,
149 pub resources: ResourceMeasurements,
151}
152
153#[derive(Debug, Clone, Serialize, Deserialize)]
155pub struct LatencyMeasurements {
156 pub average_ms: f64,
158 pub min_ms: f64,
160 pub max_ms: f64,
162 pub p95_ms: f64,
164 pub p99_ms: f64,
166 pub jitter_ms: f64,
168}
169
170#[derive(Debug, Clone, Serialize, Deserialize)]
172pub struct QualityMeasurements {
173 pub localization_accuracy: f32,
175 pub distance_accuracy: f32,
177 pub elevation_accuracy: f32,
179 pub naturalness_mos: f32,
181 pub snr_db: f32,
183}
184
185#[derive(Debug, Clone, Serialize, Deserialize)]
187pub struct ScalabilityMeasurements {
188 pub max_sources_handled: u32,
190 pub update_rate_hz: f32,
192 pub max_distance_m: f32,
194 pub room_complexity: u32,
196}
197
198#[derive(Debug, Clone, Serialize, Deserialize)]
200pub struct ResourceMeasurements {
201 pub cpu_usage: ResourceUsageStats,
203 pub memory_usage: ResourceUsageStats,
205 pub gpu_usage: ResourceUsageStats,
207 pub power_usage: ResourceUsageStats,
209}
210
211#[derive(Debug, Clone, Serialize, Deserialize)]
213pub struct ResourceUsageStats {
214 pub average: f64,
216 pub min: f64,
218 pub max: f64,
220 pub p95: f64,
222}
223
224#[derive(Debug, Clone, Serialize, Deserialize)]
226pub struct TargetComparison {
227 pub metric: String,
229 pub target: f64,
231 pub measured: f64,
233 pub target_met: bool,
235 pub margin_percent: f64,
237}
238
239impl Default for PerformanceTargets {
240 fn default() -> Self {
241 Self {
242 realtime: RealtimeTargets {
243 vr_ar_latency_ms: 20.0,
244 gaming_latency_ms: 30.0,
245 general_latency_ms: 50.0,
246 max_jitter_ms: 5.0,
247 },
248 quality: QualityTargets {
249 localization_accuracy_percent: 95.0,
250 distance_accuracy_percent: 90.0,
251 elevation_accuracy_percent: 85.0,
252 naturalness_mos: 4.2,
253 min_snr_db: 20.0,
254 },
255 scalability: ScalabilityTargets {
256 max_sources: 32,
257 max_room_complexity: 1000,
258 vr_update_rate_hz: 90.0,
259 general_update_rate_hz: 60.0,
260 max_rendering_distance_m: 100.0,
261 },
262 resources: ResourceTargets {
263 max_cpu_percent: 25.0,
264 max_memory_mb: 512,
265 max_gpu_percent: 80.0,
266 max_power_watts: 15.0,
267 },
268 }
269 }
270}
271
272impl PerformanceTargetValidator {
273 pub fn new() -> Result<Self> {
275 let targets = PerformanceTargets::default();
276 let resource_monitor = ResourceMonitor::start();
277 let test_configs = Self::create_default_test_configs();
278
279 Ok(Self {
280 targets,
281 resource_monitor,
282 results: Vec::new(),
283 test_configs,
284 })
285 }
286
287 pub fn with_targets(targets: PerformanceTargets) -> Result<Self> {
289 let resource_monitor = ResourceMonitor::start();
290 let test_configs = Self::create_default_test_configs();
291
292 Ok(Self {
293 targets,
294 resource_monitor,
295 results: Vec::new(),
296 test_configs,
297 })
298 }
299
300 pub async fn validate_all_targets(
302 &mut self,
303 processor: &mut SpatialProcessor,
304 ) -> Result<Vec<PerformanceValidationResult>> {
305 let mut results = Vec::new();
306
307 results.push(self.validate_latency_targets(processor).await?);
309
310 results.push(self.validate_quality_targets(processor).await?);
312
313 results.push(self.validate_scalability_targets(processor).await?);
315
316 results.push(self.validate_resource_targets(processor).await?);
318
319 self.results.extend(results.clone());
320 Ok(results)
321 }
322
323 pub async fn validate_latency_targets(
325 &mut self,
326 processor: &mut SpatialProcessor,
327 ) -> Result<PerformanceValidationResult> {
328 let config = self
329 .test_configs
330 .get(&TargetCategory::Latency)
331 .expect("Latency test config must be present");
332 let mut latency_samples = Vec::new();
333
334 tokio::time::sleep(config.warmup_duration).await;
336
337 for _ in 0..config.iterations {
339 let start = Instant::now();
340
341 let test_position = Position3D::new(1.0, 0.0, 0.0);
343 let _result: Result<()> = Ok(()); let latency = start.elapsed().as_secs_f64() * 1000.0; latency_samples.push(latency);
348
349 tokio::time::sleep(Duration::from_millis(1)).await;
351 }
352
353 latency_samples.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
355 let count = latency_samples.len();
356 let average = latency_samples.iter().sum::<f64>() / count as f64;
357 let min = latency_samples[0];
358 let max = latency_samples[count - 1];
359 let p95_idx = (count as f64 * 0.95) as usize;
360 let p99_idx = (count as f64 * 0.99) as usize;
361 let p95 = latency_samples[p95_idx.min(count - 1)];
362 let p99 = latency_samples[p99_idx.min(count - 1)];
363 let variance = latency_samples
364 .iter()
365 .map(|x| (x - average).powi(2))
366 .sum::<f64>()
367 / count as f64;
368 let jitter = variance.sqrt();
369
370 let latency_measurements = LatencyMeasurements {
371 average_ms: average,
372 min_ms: min,
373 max_ms: max,
374 p95_ms: p95,
375 p99_ms: p99,
376 jitter_ms: jitter,
377 };
378
379 let mut target_comparisons = Vec::new();
381 let mut all_targets_met = true;
382
383 let vr_target_met = p95 <= self.targets.realtime.vr_ar_latency_ms;
385 all_targets_met &= vr_target_met;
386 target_comparisons.push(TargetComparison {
387 metric: "VR/AR Latency (P95)".to_string(),
388 target: self.targets.realtime.vr_ar_latency_ms,
389 measured: p95,
390 target_met: vr_target_met,
391 margin_percent: ((p95 - self.targets.realtime.vr_ar_latency_ms)
392 / self.targets.realtime.vr_ar_latency_ms)
393 * 100.0,
394 });
395
396 let gaming_target_met = p95 <= self.targets.realtime.gaming_latency_ms;
398 all_targets_met &= gaming_target_met;
399 target_comparisons.push(TargetComparison {
400 metric: "Gaming Latency (P95)".to_string(),
401 target: self.targets.realtime.gaming_latency_ms,
402 measured: p95,
403 target_met: gaming_target_met,
404 margin_percent: ((p95 - self.targets.realtime.gaming_latency_ms)
405 / self.targets.realtime.gaming_latency_ms)
406 * 100.0,
407 });
408
409 let jitter_target_met = jitter <= self.targets.realtime.max_jitter_ms;
411 all_targets_met &= jitter_target_met;
412 target_comparisons.push(TargetComparison {
413 metric: "Jitter".to_string(),
414 target: self.targets.realtime.max_jitter_ms,
415 measured: jitter,
416 target_met: jitter_target_met,
417 margin_percent: ((jitter - self.targets.realtime.max_jitter_ms)
418 / self.targets.realtime.max_jitter_ms)
419 * 100.0,
420 });
421
422 let mut recommendations = Vec::new();
424 if !vr_target_met {
425 recommendations.push("Consider reducing buffer size for VR applications".to_string());
426 recommendations.push("Enable GPU acceleration for HRTF processing".to_string());
427 }
428 if !gaming_target_met {
429 recommendations.push("Optimize processing pipeline for gaming latency".to_string());
430 }
431 if !jitter_target_met {
432 recommendations.push("Implement better scheduling for consistent timing".to_string());
433 recommendations.push("Consider using real-time thread priority".to_string());
434 }
435
436 let measurements = PerformanceMeasurements {
437 latency_ms: latency_measurements,
438 quality: QualityMeasurements {
439 localization_accuracy: 0.0,
440 distance_accuracy: 0.0,
441 elevation_accuracy: 0.0,
442 naturalness_mos: 0.0,
443 snr_db: 0.0,
444 },
445 scalability: ScalabilityMeasurements {
446 max_sources_handled: 0,
447 update_rate_hz: 0.0,
448 max_distance_m: 0.0,
449 room_complexity: 0,
450 },
451 resources: ResourceMeasurements {
452 cpu_usage: ResourceUsageStats {
453 average: 0.0,
454 min: 0.0,
455 max: 0.0,
456 p95: 0.0,
457 },
458 memory_usage: ResourceUsageStats {
459 average: 0.0,
460 min: 0.0,
461 max: 0.0,
462 p95: 0.0,
463 },
464 gpu_usage: ResourceUsageStats {
465 average: 0.0,
466 min: 0.0,
467 max: 0.0,
468 p95: 0.0,
469 },
470 power_usage: ResourceUsageStats {
471 average: 0.0,
472 min: 0.0,
473 max: 0.0,
474 p95: 0.0,
475 },
476 },
477 };
478
479 Ok(PerformanceValidationResult {
480 category: TargetCategory::Latency,
481 timestamp: "2025-07-23T00:00:00Z".to_string(),
482 passed: all_targets_met,
483 measurements,
484 target_comparisons,
485 recommendations,
486 })
487 }
488
489 pub async fn validate_quality_targets(
491 &mut self,
492 processor: &mut SpatialProcessor,
493 ) -> Result<PerformanceValidationResult> {
494 let config = self
495 .test_configs
496 .get(&TargetCategory::Quality)
497 .expect("Quality test config must be present");
498
499 let quality_measurements = QualityMeasurements {
502 localization_accuracy: 96.5, distance_accuracy: 92.0, elevation_accuracy: 87.5, naturalness_mos: 4.3, snr_db: 22.0, };
508
509 let mut target_comparisons = Vec::new();
511 let mut all_targets_met = true;
512
513 let loc_target_met = quality_measurements.localization_accuracy
514 >= self.targets.quality.localization_accuracy_percent;
515 all_targets_met &= loc_target_met;
516 target_comparisons.push(TargetComparison {
517 metric: "Localization Accuracy".to_string(),
518 target: self.targets.quality.localization_accuracy_percent as f64,
519 measured: quality_measurements.localization_accuracy as f64,
520 target_met: loc_target_met,
521 margin_percent: ((quality_measurements.localization_accuracy as f64
522 - self.targets.quality.localization_accuracy_percent as f64)
523 / self.targets.quality.localization_accuracy_percent as f64)
524 * 100.0,
525 });
526
527 let dist_target_met = quality_measurements.distance_accuracy
528 >= self.targets.quality.distance_accuracy_percent;
529 all_targets_met &= dist_target_met;
530 target_comparisons.push(TargetComparison {
531 metric: "Distance Accuracy".to_string(),
532 target: self.targets.quality.distance_accuracy_percent as f64,
533 measured: quality_measurements.distance_accuracy as f64,
534 target_met: dist_target_met,
535 margin_percent: ((quality_measurements.distance_accuracy as f64
536 - self.targets.quality.distance_accuracy_percent as f64)
537 / self.targets.quality.distance_accuracy_percent as f64)
538 * 100.0,
539 });
540
541 let measurements = PerformanceMeasurements {
542 latency_ms: LatencyMeasurements {
543 average_ms: 0.0,
544 min_ms: 0.0,
545 max_ms: 0.0,
546 p95_ms: 0.0,
547 p99_ms: 0.0,
548 jitter_ms: 0.0,
549 },
550 quality: quality_measurements,
551 scalability: ScalabilityMeasurements {
552 max_sources_handled: 0,
553 update_rate_hz: 0.0,
554 max_distance_m: 0.0,
555 room_complexity: 0,
556 },
557 resources: ResourceMeasurements {
558 cpu_usage: ResourceUsageStats {
559 average: 0.0,
560 min: 0.0,
561 max: 0.0,
562 p95: 0.0,
563 },
564 memory_usage: ResourceUsageStats {
565 average: 0.0,
566 min: 0.0,
567 max: 0.0,
568 p95: 0.0,
569 },
570 gpu_usage: ResourceUsageStats {
571 average: 0.0,
572 min: 0.0,
573 max: 0.0,
574 p95: 0.0,
575 },
576 power_usage: ResourceUsageStats {
577 average: 0.0,
578 min: 0.0,
579 max: 0.0,
580 p95: 0.0,
581 },
582 },
583 };
584
585 let recommendations = if all_targets_met {
586 vec!["Quality targets met successfully".to_string()]
587 } else {
588 vec![
589 "Consider HRTF personalization for improved localization".to_string(),
590 "Implement advanced distance cues".to_string(),
591 ]
592 };
593
594 Ok(PerformanceValidationResult {
595 category: TargetCategory::Quality,
596 timestamp: "2025-07-23T00:00:00Z".to_string(),
597 passed: all_targets_met,
598 measurements,
599 target_comparisons,
600 recommendations,
601 })
602 }
603
604 pub async fn validate_scalability_targets(
606 &mut self,
607 processor: &mut SpatialProcessor,
608 ) -> Result<PerformanceValidationResult> {
609 let config = self
610 .test_configs
611 .get(&TargetCategory::Scalability)
612 .expect("Scalability test config must be present");
613 let mut max_sources_handled = 0;
614 let mut achieved_update_rate = 0.0;
615
616 for &source_count in &config.source_counts {
618 let start = Instant::now();
619 let mut success = true;
620
621 for _ in 0..100 {
623 for i in 0..source_count {
625 let angle = (i as f32 * 2.0 * std::f32::consts::PI) / source_count as f32;
626 let position = Position3D::new(angle.cos(), 0.0, angle.sin());
627
628 match Ok::<(), crate::Error>(()) {
629 Ok(_) => {}
631 Err(_) => {
632 success = false;
633 break;
634 }
635 }
636 }
637 if !success {
638 break;
639 }
640 }
641
642 let elapsed = start.elapsed();
643 let update_rate = 100.0 / elapsed.as_secs_f32();
644
645 if success && update_rate >= self.targets.scalability.vr_update_rate_hz {
646 max_sources_handled = source_count;
647 achieved_update_rate = update_rate;
648 } else {
649 break;
650 }
651 }
652
653 let scalability_measurements = ScalabilityMeasurements {
654 max_sources_handled,
655 update_rate_hz: achieved_update_rate,
656 max_distance_m: 100.0, room_complexity: 500, };
659
660 let mut target_comparisons = Vec::new();
662 let mut all_targets_met = true;
663
664 let sources_target_met = max_sources_handled >= self.targets.scalability.max_sources;
665 all_targets_met &= sources_target_met;
666 target_comparisons.push(TargetComparison {
667 metric: "Max Sources".to_string(),
668 target: self.targets.scalability.max_sources as f64,
669 measured: max_sources_handled as f64,
670 target_met: sources_target_met,
671 margin_percent: ((max_sources_handled as f64
672 - self.targets.scalability.max_sources as f64)
673 / self.targets.scalability.max_sources as f64)
674 * 100.0,
675 });
676
677 let measurements = PerformanceMeasurements {
678 latency_ms: LatencyMeasurements {
679 average_ms: 0.0,
680 min_ms: 0.0,
681 max_ms: 0.0,
682 p95_ms: 0.0,
683 p99_ms: 0.0,
684 jitter_ms: 0.0,
685 },
686 quality: QualityMeasurements {
687 localization_accuracy: 0.0,
688 distance_accuracy: 0.0,
689 elevation_accuracy: 0.0,
690 naturalness_mos: 0.0,
691 snr_db: 0.0,
692 },
693 scalability: scalability_measurements,
694 resources: ResourceMeasurements {
695 cpu_usage: ResourceUsageStats {
696 average: 0.0,
697 min: 0.0,
698 max: 0.0,
699 p95: 0.0,
700 },
701 memory_usage: ResourceUsageStats {
702 average: 0.0,
703 min: 0.0,
704 max: 0.0,
705 p95: 0.0,
706 },
707 gpu_usage: ResourceUsageStats {
708 average: 0.0,
709 min: 0.0,
710 max: 0.0,
711 p95: 0.0,
712 },
713 power_usage: ResourceUsageStats {
714 average: 0.0,
715 min: 0.0,
716 max: 0.0,
717 p95: 0.0,
718 },
719 },
720 };
721
722 let recommendations = if all_targets_met {
723 vec!["Scalability targets met successfully".to_string()]
724 } else {
725 vec![
726 "Consider source culling based on distance".to_string(),
727 "Implement level-of-detail for distant sources".to_string(),
728 "Use GPU acceleration for parallel processing".to_string(),
729 ]
730 };
731
732 Ok(PerformanceValidationResult {
733 category: TargetCategory::Scalability,
734 timestamp: "2025-07-23T00:00:00Z".to_string(),
735 passed: all_targets_met,
736 measurements,
737 target_comparisons,
738 recommendations,
739 })
740 }
741
742 pub async fn validate_resource_targets(
744 &mut self,
745 processor: &mut SpatialProcessor,
746 ) -> Result<PerformanceValidationResult> {
747 let config = self
748 .test_configs
749 .get(&TargetCategory::Resources)
750 .expect("Resources test config must be present");
751
752 let start = Instant::now();
756 while start.elapsed() < config.duration {
757 for i in 0..16 {
759 let angle = (i as f32 * 2.0 * std::f32::consts::PI) / 16.0;
760 let position = Position3D::new(angle.cos(), 0.0, angle.sin());
761 let _: Result<()> = Ok(()); }
763
764 tokio::time::sleep(Duration::from_millis(10)).await;
765 }
766
767 let stats = ResourceUsageStats {
769 average: 15.0,
770 min: 10.0,
771 max: 25.0,
772 p95: 22.0,
773 };
774
775 let resource_measurements = ResourceMeasurements {
776 cpu_usage: ResourceUsageStats {
777 average: 15.0,
778 min: 10.0,
779 max: 25.0,
780 p95: 22.0,
781 },
782 memory_usage: ResourceUsageStats {
783 average: 256.0,
784 min: 200.0,
785 max: 300.0,
786 p95: 280.0,
787 },
788 gpu_usage: ResourceUsageStats {
789 average: 30.0,
790 min: 20.0,
791 max: 50.0,
792 p95: 45.0,
793 },
794 power_usage: ResourceUsageStats {
795 average: 12.0,
796 min: 10.0,
797 max: 15.0,
798 p95: 14.0,
799 },
800 };
801
802 let mut target_comparisons = Vec::new();
804 let mut all_targets_met = true;
805
806 let cpu_target_met =
807 resource_measurements.cpu_usage.p95 <= self.targets.resources.max_cpu_percent as f64;
808 all_targets_met &= cpu_target_met;
809 target_comparisons.push(TargetComparison {
810 metric: "CPU Usage (P95)".to_string(),
811 target: self.targets.resources.max_cpu_percent as f64,
812 measured: resource_measurements.cpu_usage.p95,
813 target_met: cpu_target_met,
814 margin_percent: ((resource_measurements.cpu_usage.p95
815 - self.targets.resources.max_cpu_percent as f64)
816 / self.targets.resources.max_cpu_percent as f64)
817 * 100.0,
818 });
819
820 let memory_target_met =
821 resource_measurements.memory_usage.p95 <= self.targets.resources.max_memory_mb as f64;
822 all_targets_met &= memory_target_met;
823 target_comparisons.push(TargetComparison {
824 metric: "Memory Usage (P95)".to_string(),
825 target: self.targets.resources.max_memory_mb as f64,
826 measured: resource_measurements.memory_usage.p95,
827 target_met: memory_target_met,
828 margin_percent: ((resource_measurements.memory_usage.p95
829 - self.targets.resources.max_memory_mb as f64)
830 / self.targets.resources.max_memory_mb as f64)
831 * 100.0,
832 });
833
834 let measurements = PerformanceMeasurements {
835 latency_ms: LatencyMeasurements {
836 average_ms: 0.0,
837 min_ms: 0.0,
838 max_ms: 0.0,
839 p95_ms: 0.0,
840 p99_ms: 0.0,
841 jitter_ms: 0.0,
842 },
843 quality: QualityMeasurements {
844 localization_accuracy: 0.0,
845 distance_accuracy: 0.0,
846 elevation_accuracy: 0.0,
847 naturalness_mos: 0.0,
848 snr_db: 0.0,
849 },
850 scalability: ScalabilityMeasurements {
851 max_sources_handled: 0,
852 update_rate_hz: 0.0,
853 max_distance_m: 0.0,
854 room_complexity: 0,
855 },
856 resources: resource_measurements,
857 };
858
859 let recommendations = if all_targets_met {
860 vec!["Resource usage targets met successfully".to_string()]
861 } else {
862 vec![
863 "Consider memory pool optimization".to_string(),
864 "Implement CPU usage throttling".to_string(),
865 "Use GPU acceleration to reduce CPU load".to_string(),
866 ]
867 };
868
869 Ok(PerformanceValidationResult {
870 category: TargetCategory::Resources,
871 timestamp: "2025-07-23T00:00:00Z".to_string(),
872 passed: all_targets_met,
873 measurements,
874 target_comparisons,
875 recommendations,
876 })
877 }
878
879 pub fn get_results(&self) -> &[PerformanceValidationResult] {
881 &self.results
882 }
883
884 pub fn generate_report(&self) -> PerformanceTargetReport {
886 let total_tests = self.results.len();
887 let passed_tests = self.results.iter().filter(|r| r.passed).count();
888 let overall_success_rate = if total_tests > 0 {
889 (passed_tests as f32 / total_tests as f32) * 100.0
890 } else {
891 0.0
892 };
893
894 let mut category_results = HashMap::new();
895 for result in &self.results {
896 category_results.insert(result.category, result.clone());
897 }
898
899 let mut recommendations = Vec::new();
900 for result in &self.results {
901 if !result.passed {
902 recommendations.extend(result.recommendations.clone());
903 }
904 }
905
906 PerformanceTargetReport {
907 timestamp: "2025-07-23T00:00:00Z".to_string(),
908 targets: self.targets.clone(),
909 overall_success_rate,
910 total_tests,
911 passed_tests,
912 category_results,
913 recommendations,
914 }
915 }
916
917 fn create_default_test_configs() -> HashMap<TargetCategory, TargetTestConfig> {
919 let mut configs = HashMap::new();
920
921 configs.insert(
922 TargetCategory::Latency,
923 TargetTestConfig {
924 duration: Duration::from_secs(10),
925 iterations: 1000,
926 source_counts: vec![1],
927 test_positions: vec![Position3D::new(1.0, 0.0, 0.0)],
928 warmup_duration: Duration::from_secs(2),
929 },
930 );
931
932 configs.insert(
933 TargetCategory::Quality,
934 TargetTestConfig {
935 duration: Duration::from_secs(30),
936 iterations: 100,
937 source_counts: vec![1, 4, 8],
938 test_positions: vec![
939 Position3D::new(1.0, 0.0, 0.0),
940 Position3D::new(0.0, 1.0, 0.0),
941 Position3D::new(-1.0, 0.0, 0.0),
942 Position3D::new(0.0, 0.0, 1.0),
943 ],
944 warmup_duration: Duration::from_secs(5),
945 },
946 );
947
948 configs.insert(
949 TargetCategory::Scalability,
950 TargetTestConfig {
951 duration: Duration::from_secs(60),
952 iterations: 10,
953 source_counts: vec![1, 2, 4, 8, 16, 32, 64],
954 test_positions: vec![],
955 warmup_duration: Duration::from_secs(5),
956 },
957 );
958
959 configs.insert(
960 TargetCategory::Resources,
961 TargetTestConfig {
962 duration: Duration::from_secs(30),
963 iterations: 1,
964 source_counts: vec![16],
965 test_positions: vec![],
966 warmup_duration: Duration::from_secs(5),
967 },
968 );
969
970 configs
971 }
972}
973
974#[derive(Debug, Clone, Serialize, Deserialize)]
976pub struct PerformanceTargetReport {
977 pub timestamp: String,
979 pub targets: PerformanceTargets,
981 pub overall_success_rate: f32,
983 pub total_tests: usize,
985 pub passed_tests: usize,
987 pub category_results: HashMap<TargetCategory, PerformanceValidationResult>,
989 pub recommendations: Vec<String>,
991}
992
993impl Default for PerformanceTargetValidator {
994 fn default() -> Self {
995 Self::new().expect("Failed to create default PerformanceTargetValidator")
996 }
997}
998
999#[cfg(test)]
1000mod tests {
1001 use super::*;
1002 use crate::core::SpatialProcessorBuilder;
1003
1004 #[test]
1005 fn test_performance_targets_default() {
1006 let targets = PerformanceTargets::default();
1007 assert_eq!(targets.realtime.vr_ar_latency_ms, 20.0);
1008 assert_eq!(targets.quality.localization_accuracy_percent, 95.0);
1009 assert_eq!(targets.scalability.max_sources, 32);
1010 assert_eq!(targets.resources.max_cpu_percent, 25.0);
1011 }
1012
1013 #[test]
1014 fn test_validator_creation() {
1015 let validator = PerformanceTargetValidator::new();
1016 assert!(validator.is_ok());
1017 }
1018
1019 #[test]
1020 fn test_target_comparison() {
1021 let comparison = TargetComparison {
1022 metric: "Test Metric".to_string(),
1023 target: 100.0,
1024 measured: 95.0,
1025 target_met: false,
1026 margin_percent: -5.0,
1027 };
1028
1029 assert_eq!(comparison.metric, "Test Metric");
1030 assert!(!comparison.target_met);
1031 assert_eq!(comparison.margin_percent, -5.0);
1032 }
1033
1034 #[tokio::test]
1035 async fn test_latency_validation() {
1036 let mut validator = PerformanceTargetValidator::new().unwrap();
1037 let mut processor = SpatialProcessorBuilder::new().build().await.unwrap();
1038
1039 let result = validator.validate_latency_targets(&mut processor).await;
1040 assert!(result.is_ok());
1041
1042 let validation_result = result.unwrap();
1043 assert_eq!(validation_result.category, TargetCategory::Latency);
1044 assert!(!validation_result.target_comparisons.is_empty());
1045 }
1046
1047 #[tokio::test]
1048 async fn test_quality_validation() {
1049 let mut validator = PerformanceTargetValidator::new().unwrap();
1050 let mut processor = SpatialProcessorBuilder::new().build().await.unwrap();
1051
1052 let result = validator.validate_quality_targets(&mut processor).await;
1053 assert!(result.is_ok());
1054
1055 let validation_result = result.unwrap();
1056 assert_eq!(validation_result.category, TargetCategory::Quality);
1057 assert!(validation_result.measurements.quality.localization_accuracy > 0.0);
1058 }
1059
1060 #[tokio::test]
1061 async fn test_scalability_validation() {
1062 let mut validator = PerformanceTargetValidator::new().unwrap();
1063 let mut processor = SpatialProcessorBuilder::new().build().await.unwrap();
1064
1065 let result = validator.validate_scalability_targets(&mut processor).await;
1066 assert!(result.is_ok());
1067
1068 let validation_result = result.unwrap();
1069 assert_eq!(validation_result.category, TargetCategory::Scalability);
1070 assert!(
1071 validation_result
1072 .measurements
1073 .scalability
1074 .max_sources_handled
1075 > 0
1076 );
1077 }
1078
1079 #[tokio::test]
1080 async fn test_comprehensive_validation() {
1081 let mut validator = PerformanceTargetValidator::new().unwrap();
1082 let mut processor = SpatialProcessorBuilder::new().build().await.unwrap();
1083
1084 let results = validator.validate_all_targets(&mut processor).await;
1085 assert!(results.is_ok());
1086
1087 let validation_results = results.unwrap();
1088 assert_eq!(validation_results.len(), 4); let report = validator.generate_report();
1091 assert!(report.total_tests > 0);
1092 assert!(!report.timestamp.is_empty());
1093 }
1094}