quantrs2_anneal/comprehensive_integration_testing/
monitoring.rs

1//! Performance monitoring and alerting for tests
2
3use std::collections::HashMap;
4use std::time::{Duration, SystemTime};
5
6/// Performance monitoring for tests
7pub struct TestPerformanceMonitor {
8    /// Performance metrics
9    pub metrics: TestPerformanceMetrics,
10    /// Benchmark comparisons
11    pub benchmarks: HashMap<String, BenchmarkComparison>,
12    /// Performance trends
13    pub trends: PerformanceTrends,
14    /// Alert system
15    pub alert_system: PerformanceAlertSystem,
16}
17
18impl TestPerformanceMonitor {
19    #[must_use]
20    pub fn new() -> Self {
21        Self {
22            metrics: TestPerformanceMetrics::default(),
23            benchmarks: HashMap::new(),
24            trends: PerformanceTrends::default(),
25            alert_system: PerformanceAlertSystem::new(),
26        }
27    }
28
29    /// Record a test execution time
30    pub fn record_execution_time(&mut self, duration: Duration) {
31        self.metrics.execution_time_distribution.push(duration);
32        self.trends
33            .execution_time_trend
34            .push((SystemTime::now(), duration));
35
36        // Update average
37        if !self.metrics.execution_time_distribution.is_empty() {
38            let sum: Duration = self.metrics.execution_time_distribution.iter().sum();
39            self.metrics.avg_execution_time =
40                sum / self.metrics.execution_time_distribution.len() as u32;
41        }
42
43        // Keep only last 1000 entries
44        if self.metrics.execution_time_distribution.len() > 1000 {
45            self.metrics.execution_time_distribution.drain(0..1);
46        }
47        if self.trends.execution_time_trend.len() > 1000 {
48            self.trends.execution_time_trend.drain(0..1);
49        }
50    }
51
52    /// Update success rate
53    pub fn update_success_rate(&mut self, success: bool) {
54        let current_count = self.metrics.execution_time_distribution.len();
55        if current_count == 0 {
56            self.metrics.success_rate = if success { 1.0 } else { 0.0 };
57        } else {
58            let current_successes = (self.metrics.success_rate * current_count as f64) as usize;
59            let new_successes = if success {
60                current_successes + 1
61            } else {
62                current_successes
63            };
64            self.metrics.success_rate = new_successes as f64 / (current_count + 1) as f64;
65        }
66
67        self.trends
68            .success_rate_trend
69            .push((SystemTime::now(), self.metrics.success_rate));
70
71        // Keep only last 1000 entries
72        if self.trends.success_rate_trend.len() > 1000 {
73            self.trends.success_rate_trend.drain(0..1);
74        }
75    }
76
77    /// Set a benchmark baseline
78    pub fn set_benchmark(&mut self, name: String, baseline: PerformanceBaseline) {
79        let comparison = BenchmarkComparison {
80            baseline,
81            current: self.metrics.clone(),
82            delta: PerformanceDelta {
83                execution_time_change: 0.0,
84                success_rate_change: 0.0,
85                resource_usage_change: 0.0,
86                overall_change: 0.0,
87            },
88            timestamp: SystemTime::now(),
89        };
90        self.benchmarks.insert(name, comparison);
91    }
92
93    /// Get current metrics
94    #[must_use]
95    pub const fn get_metrics(&self) -> &TestPerformanceMetrics {
96        &self.metrics
97    }
98
99    /// Get performance trends
100    #[must_use]
101    pub const fn get_trends(&self) -> &PerformanceTrends {
102        &self.trends
103    }
104
105    /// Analyze trends
106    pub fn analyze_trends(&mut self) {
107        // Simple trend analysis based on last N points
108        let window_size = 10;
109
110        // Analyze execution time trend
111        if self.trends.execution_time_trend.len() >= window_size {
112            let recent: Vec<_> = self
113                .trends
114                .execution_time_trend
115                .iter()
116                .rev()
117                .take(window_size)
118                .collect();
119
120            let first_avg = recent
121                .iter()
122                .rev()
123                .take(window_size / 2)
124                .map(|(_, d)| d.as_secs_f64())
125                .sum::<f64>()
126                / (window_size / 2) as f64;
127
128            let second_avg = recent
129                .iter()
130                .take(window_size / 2)
131                .map(|(_, d)| d.as_secs_f64())
132                .sum::<f64>()
133                / (window_size / 2) as f64;
134
135            let change = (second_avg - first_avg) / first_avg;
136
137            self.trends.trend_analysis.execution_time_direction = if change < -0.1 {
138                TrendDirection::Improving
139            } else if change > 0.1 {
140                TrendDirection::Degrading
141            } else {
142                TrendDirection::Stable
143            };
144        }
145    }
146
147    /// Check alert conditions
148    pub fn check_alerts(&mut self) {
149        self.alert_system.check_alerts(&self.metrics);
150    }
151}
152
153/// Test performance metrics
154#[derive(Debug, Clone)]
155pub struct TestPerformanceMetrics {
156    /// Average execution time
157    pub avg_execution_time: Duration,
158    /// Execution time distribution
159    pub execution_time_distribution: Vec<Duration>,
160    /// Success rate
161    pub success_rate: f64,
162    /// Resource efficiency
163    pub resource_efficiency: f64,
164    /// Throughput rate
165    pub throughput_rate: f64,
166}
167
168impl Default for TestPerformanceMetrics {
169    fn default() -> Self {
170        Self {
171            avg_execution_time: Duration::from_secs(0),
172            execution_time_distribution: vec![],
173            success_rate: 0.0,
174            resource_efficiency: 0.0,
175            throughput_rate: 0.0,
176        }
177    }
178}
179
180/// Benchmark comparison data
181#[derive(Debug, Clone)]
182pub struct BenchmarkComparison {
183    /// Baseline performance
184    pub baseline: PerformanceBaseline,
185    /// Current performance
186    pub current: TestPerformanceMetrics,
187    /// Performance delta
188    pub delta: PerformanceDelta,
189    /// Comparison timestamp
190    pub timestamp: SystemTime,
191}
192
193/// Performance baseline
194#[derive(Debug, Clone)]
195pub struct PerformanceBaseline {
196    /// Baseline execution time
197    pub execution_time: Duration,
198    /// Baseline success rate
199    pub success_rate: f64,
200    /// Baseline resource usage
201    pub resource_usage: f64,
202    /// Baseline timestamp
203    pub timestamp: SystemTime,
204}
205
206/// Performance delta comparison
207#[derive(Debug, Clone)]
208pub struct PerformanceDelta {
209    /// Execution time change
210    pub execution_time_change: f64,
211    /// Success rate change
212    pub success_rate_change: f64,
213    /// Resource usage change
214    pub resource_usage_change: f64,
215    /// Overall performance change
216    pub overall_change: f64,
217}
218
219/// Performance trends tracking
220#[derive(Debug, Clone)]
221pub struct PerformanceTrends {
222    /// Execution time trend
223    pub execution_time_trend: Vec<(SystemTime, Duration)>,
224    /// Success rate trend
225    pub success_rate_trend: Vec<(SystemTime, f64)>,
226    /// Resource usage trend
227    pub resource_usage_trend: Vec<(SystemTime, f64)>,
228    /// Trend analysis
229    pub trend_analysis: TrendAnalysis,
230}
231
232impl Default for PerformanceTrends {
233    fn default() -> Self {
234        Self {
235            execution_time_trend: vec![],
236            success_rate_trend: vec![],
237            resource_usage_trend: vec![],
238            trend_analysis: TrendAnalysis::default(),
239        }
240    }
241}
242
243/// Trend analysis results
244#[derive(Debug, Clone)]
245pub struct TrendAnalysis {
246    /// Execution time trend direction
247    pub execution_time_direction: TrendDirection,
248    /// Success rate trend direction
249    pub success_rate_direction: TrendDirection,
250    /// Resource usage trend direction
251    pub resource_usage_direction: TrendDirection,
252    /// Trend confidence
253    pub confidence: f64,
254}
255
256impl Default for TrendAnalysis {
257    fn default() -> Self {
258        Self {
259            execution_time_direction: TrendDirection::Stable,
260            success_rate_direction: TrendDirection::Stable,
261            resource_usage_direction: TrendDirection::Stable,
262            confidence: 0.0,
263        }
264    }
265}
266
267/// Trend directions
268#[derive(Debug, Clone, PartialEq, Eq)]
269pub enum TrendDirection {
270    Improving,
271    Stable,
272    Degrading,
273    Volatile,
274    Unknown,
275}
276
277/// Performance alert system
278pub struct PerformanceAlertSystem {
279    /// Alert rules
280    pub alert_rules: Vec<AlertRule>,
281    /// Active alerts
282    pub active_alerts: HashMap<String, PerformanceAlert>,
283    /// Alert history
284    pub alert_history: Vec<PerformanceAlert>,
285}
286
287impl PerformanceAlertSystem {
288    #[must_use]
289    pub fn new() -> Self {
290        Self {
291            alert_rules: vec![],
292            active_alerts: HashMap::new(),
293            alert_history: vec![],
294        }
295    }
296
297    /// Add an alert rule
298    pub fn add_rule(&mut self, rule: AlertRule) {
299        self.alert_rules.push(rule);
300    }
301
302    /// Remove an alert rule
303    pub fn remove_rule(&mut self, rule_name: &str) {
304        self.alert_rules.retain(|r| r.name != rule_name);
305    }
306
307    /// Check alert conditions against current metrics
308    pub fn check_alerts(&mut self, metrics: &TestPerformanceMetrics) {
309        // Collect rules that should trigger alerts
310        let triggered_rules: Vec<String> = self
311            .alert_rules
312            .iter()
313            .filter_map(|rule| {
314                let should_alert = match &rule.condition {
315                    AlertCondition::ThresholdExceeded(threshold) => {
316                        // Check if execution time exceeds threshold
317                        metrics.avg_execution_time.as_secs_f64() > *threshold
318                    }
319                    AlertCondition::ThresholdBelow(threshold) => {
320                        // Check if success rate is below threshold
321                        metrics.success_rate < *threshold
322                    }
323                    AlertCondition::PercentageChange(_percentage) => {
324                        // Check for significant performance change
325                        false // Placeholder
326                    }
327                    AlertCondition::Custom(_) => false,
328                };
329
330                should_alert.then(|| rule.name.clone())
331            })
332            .collect();
333
334        // Trigger alerts for collected rules
335        for rule_name in triggered_rules {
336            self.trigger_alert(&rule_name, metrics);
337        }
338    }
339
340    /// Trigger an alert
341    fn trigger_alert(&mut self, rule_name: &str, metrics: &TestPerformanceMetrics) {
342        let rule = self.alert_rules.iter().find(|r| r.name == rule_name);
343        if let Some(rule) = rule {
344            let alert = PerformanceAlert {
345                id: format!(
346                    "alert_{}_{}",
347                    rule_name,
348                    SystemTime::now()
349                        .duration_since(SystemTime::UNIX_EPOCH)
350                        .unwrap_or(Duration::ZERO)
351                        .as_secs()
352                ),
353                rule_name: rule_name.to_string(),
354                message: format!("Performance alert triggered for rule: {rule_name}"),
355                severity: rule.severity.clone(),
356                timestamp: SystemTime::now(),
357                metric_value: metrics.avg_execution_time.as_secs_f64(),
358                threshold_value: 0.0, // Placeholder
359                status: AlertStatus::Active,
360            };
361
362            self.active_alerts.insert(alert.id.clone(), alert.clone());
363            self.alert_history.push(alert);
364
365            // Keep only last 1000 alerts in history
366            if self.alert_history.len() > 1000 {
367                self.alert_history.drain(0..1);
368            }
369        }
370    }
371
372    /// Acknowledge an alert
373    pub fn acknowledge_alert(&mut self, alert_id: &str) -> Result<(), String> {
374        let alert = self
375            .active_alerts
376            .get_mut(alert_id)
377            .ok_or_else(|| format!("Alert {alert_id} not found"))?;
378        alert.status = AlertStatus::Acknowledged;
379        Ok(())
380    }
381
382    /// Resolve an alert
383    pub fn resolve_alert(&mut self, alert_id: &str) -> Result<(), String> {
384        let alert = self
385            .active_alerts
386            .remove(alert_id)
387            .ok_or_else(|| format!("Alert {alert_id} not found"))?;
388
389        // Update in history
390        for hist_alert in &mut self.alert_history {
391            if hist_alert.id == alert_id {
392                hist_alert.status = AlertStatus::Resolved;
393            }
394        }
395        Ok(())
396    }
397
398    /// Get active alerts
399    #[must_use]
400    pub fn get_active_alerts(&self) -> Vec<&PerformanceAlert> {
401        self.active_alerts.values().collect()
402    }
403
404    /// Get alert history
405    #[must_use]
406    pub fn get_alert_history(&self) -> &[PerformanceAlert] {
407        &self.alert_history
408    }
409
410    /// Clear all alerts
411    pub fn clear_all_alerts(&mut self) {
412        self.active_alerts.clear();
413        self.alert_history.clear();
414    }
415}
416
417/// Alert rule definition
418#[derive(Debug, Clone)]
419pub struct AlertRule {
420    /// Rule name
421    pub name: String,
422    /// Metric to monitor
423    pub metric: String,
424    /// Alert condition
425    pub condition: AlertCondition,
426    /// Alert severity
427    pub severity: AlertSeverity,
428    /// Alert actions
429    pub actions: Vec<AlertAction>,
430}
431
432/// Alert conditions
433#[derive(Debug, Clone)]
434pub enum AlertCondition {
435    /// Threshold exceeded
436    ThresholdExceeded(f64),
437    /// Threshold below
438    ThresholdBelow(f64),
439    /// Percentage change
440    PercentageChange(f64),
441    /// Custom condition
442    Custom(String),
443}
444
445/// Alert severity levels
446#[derive(Debug, Clone, PartialEq, Eq)]
447pub enum AlertSeverity {
448    Info,
449    Warning,
450    Error,
451    Critical,
452}
453
454/// Alert actions
455#[derive(Debug, Clone)]
456pub enum AlertAction {
457    /// Log alert
458    Log,
459    /// Send email
460    Email(String),
461    /// Execute script
462    ExecuteScript(String),
463    /// Custom action
464    Custom(String),
465}
466
467/// Performance alert
468#[derive(Debug, Clone)]
469pub struct PerformanceAlert {
470    /// Alert ID
471    pub id: String,
472    /// Alert rule name
473    pub rule_name: String,
474    /// Alert message
475    pub message: String,
476    /// Alert severity
477    pub severity: AlertSeverity,
478    /// Alert timestamp
479    pub timestamp: SystemTime,
480    /// Metric value
481    pub metric_value: f64,
482    /// Threshold value
483    pub threshold_value: f64,
484    /// Alert status
485    pub status: AlertStatus,
486}
487
488/// Alert status
489#[derive(Debug, Clone, PartialEq, Eq)]
490pub enum AlertStatus {
491    Active,
492    Resolved,
493    Acknowledged,
494    Suppressed,
495}