Skip to main content

optirs_bench/ci_cd_automation/
performance_gates.rs

1// Performance Gates and Monitoring
2//
3// This module provides comprehensive performance gate evaluation, monitoring,
4// and validation capabilities for CI/CD automation, ensuring performance
5// regressions are caught before deployment.
6
7use crate::error::{OptimError, Result};
8use crate::performance_regression_detector::{
9    MetricType as RegressionMetricType, MetricValue, PerformanceMeasurement,
10};
11use serde::{Deserialize, Serialize};
12use std::collections::HashMap;
13use std::time::{Duration, SystemTime};
14
15use super::config::{
16    ComparisonOperator, GateEvaluationStrategy, GateFailureAction, GateFailureHandling,
17    GateFailureNotificationConfig, GateSeverity, GateType, MetricGate, MetricType,
18    PerformanceGatesConfig,
19};
20use super::test_execution::{CiCdTestResult, TestSuiteStatistics};
21
22/// Performance gate evaluator
23#[derive(Debug, Clone)]
24pub struct PerformanceGateEvaluator {
25    /// Gate configuration
26    pub config: PerformanceGatesConfig,
27    /// Baseline metrics for comparison
28    pub baseline_metrics: HashMap<MetricType, BaselineMetric>,
29    /// Gate evaluation history
30    pub evaluation_history: Vec<GateEvaluationResult>,
31    /// Current gate states
32    pub gate_states: HashMap<MetricType, GateState>,
33    /// Performance trend analyzer
34    pub trend_analyzer: PerformanceTrendAnalyzer,
35    /// Alert manager
36    pub alert_manager: AlertManager,
37}
38
39/// Baseline metric for gate comparison
40#[derive(Debug, Clone, Serialize, Deserialize)]
41pub struct BaselineMetric {
42    /// Metric type
43    pub metric_type: MetricType,
44    /// Baseline value
45    pub baseline_value: f64,
46    /// Acceptable variance (percentage)
47    pub variance_threshold: f64,
48    /// Confidence interval
49    pub confidence_interval: (f64, f64),
50    /// Sample size used for baseline
51    pub sample_size: usize,
52    /// Last updated timestamp
53    pub last_updated: SystemTime,
54    /// Statistical significance
55    pub statistical_significance: f64,
56}
57
58/// Current state of a performance gate
59#[derive(Debug, Clone, Serialize, Deserialize)]
60pub struct GateState {
61    /// Gate metric type
62    pub metric_type: MetricType,
63    /// Current gate status
64    pub status: GateStatus,
65    /// Current metric value
66    pub current_value: f64,
67    /// Baseline value for comparison
68    pub baseline_value: f64,
69    /// Percentage change from baseline
70    pub percentage_change: f64,
71    /// Gate evaluation timestamp
72    pub evaluated_at: SystemTime,
73    /// Gate configuration used
74    pub gate_config: MetricGate,
75    /// Evaluation details
76    pub evaluation_details: GateEvaluationDetails,
77}
78
79/// Gate status enumeration
80#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
81pub enum GateStatus {
82    /// Gate passed successfully
83    Passed,
84    /// Gate failed
85    Failed,
86    /// Gate evaluation warning
87    Warning,
88    /// Gate was skipped
89    Skipped,
90    /// Gate evaluation error
91    Error,
92    /// Gate not evaluated yet
93    NotEvaluated,
94}
95
96/// Detailed gate evaluation information
97#[derive(Debug, Clone, Serialize, Deserialize)]
98pub struct GateEvaluationDetails {
99    /// Evaluation method used
100    pub evaluation_method: String,
101    /// Statistical test results
102    pub statistical_tests: Vec<StatisticalTestResult>,
103    /// Confidence level
104    pub confidence_level: f64,
105    /// P-value (for statistical tests)
106    pub p_value: Option<f64>,
107    /// Effect size
108    pub effect_size: Option<f64>,
109    /// Additional metadata
110    pub metadata: HashMap<String, String>,
111}
112
113/// Statistical test result
114#[derive(Debug, Clone, Serialize, Deserialize)]
115pub struct StatisticalTestResult {
116    /// Test name
117    pub test_name: String,
118    /// Test statistic value
119    pub test_statistic: f64,
120    /// P-value
121    pub p_value: f64,
122    /// Degrees of freedom
123    pub degrees_of_freedom: Option<u32>,
124    /// Test conclusion
125    pub conclusion: TestConclusion,
126}
127
128/// Statistical test conclusion
129#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
130pub enum TestConclusion {
131    /// Significant difference detected
132    Significant,
133    /// No significant difference
134    NotSignificant,
135    /// Inconclusive result
136    Inconclusive,
137}
138
139/// Gate evaluation result
140#[derive(Debug, Clone, Serialize, Deserialize)]
141pub struct GateEvaluationResult {
142    /// Evaluation ID
143    pub evaluation_id: String,
144    /// Evaluation timestamp
145    pub timestamp: SystemTime,
146    /// Overall evaluation status
147    pub overall_status: OverallGateStatus,
148    /// Individual gate results
149    pub gate_results: Vec<IndividualGateResult>,
150    /// Evaluation summary
151    pub summary: GateEvaluationSummary,
152    /// Actions taken
153    pub actions_taken: Vec<GateAction>,
154    /// Evaluation duration
155    pub evaluation_duration: Duration,
156}
157
158/// Overall gate evaluation status
159#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
160pub enum OverallGateStatus {
161    /// All gates passed
162    AllPassed,
163    /// Some gates failed
164    SomeFailed,
165    /// All gates failed
166    AllFailed,
167    /// Gates were skipped
168    Skipped,
169    /// Evaluation error occurred
170    Error,
171}
172
173/// Individual gate evaluation result
174#[derive(Debug, Clone, Serialize, Deserialize)]
175pub struct IndividualGateResult {
176    /// Metric type
177    pub metric_type: MetricType,
178    /// Gate status
179    pub status: GateStatus,
180    /// Current value
181    pub current_value: f64,
182    /// Threshold value
183    pub threshold_value: f64,
184    /// Percentage deviation
185    pub percentage_deviation: f64,
186    /// Gate severity
187    pub severity: GateSeverity,
188    /// Failure reason (if failed)
189    pub failure_reason: Option<String>,
190    /// Evaluation details
191    pub details: GateEvaluationDetails,
192}
193
194/// Gate evaluation summary
195#[derive(Debug, Clone, Serialize, Deserialize)]
196pub struct GateEvaluationSummary {
197    /// Total gates evaluated
198    pub total_gates: usize,
199    /// Gates passed
200    pub gates_passed: usize,
201    /// Gates failed
202    pub gates_failed: usize,
203    /// Gates with warnings
204    pub gates_warnings: usize,
205    /// Gates skipped
206    pub gates_skipped: usize,
207    /// Critical failures
208    pub critical_failures: usize,
209    /// Performance improvement detected
210    pub improvements_detected: usize,
211    /// Regression severity
212    pub regression_severity: RegressionSeverity,
213}
214
215/// Regression severity levels
216#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
217pub enum RegressionSeverity {
218    /// No regression detected
219    None,
220    /// Minor regression
221    Minor,
222    /// Moderate regression
223    Moderate,
224    /// Major regression
225    Major,
226    /// Critical regression
227    Critical,
228}
229
230/// Actions taken during gate evaluation
231#[derive(Debug, Clone, Serialize, Deserialize)]
232pub struct GateAction {
233    /// Action type
234    pub action_type: GateActionType,
235    /// Action description
236    pub description: String,
237    /// Action timestamp
238    pub timestamp: SystemTime,
239    /// Action result
240    pub result: ActionResult,
241}
242
243/// Types of gate actions
244#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
245pub enum GateActionType {
246    /// Send notification
247    SendNotification,
248    /// Create issue/ticket
249    CreateIssue,
250    /// Fail build
251    FailBuild,
252    /// Mark as unstable
253    MarkUnstable,
254    /// Log warning
255    LogWarning,
256    /// Update baseline
257    UpdateBaseline,
258    /// Override gate
259    OverrideGate,
260}
261
262/// Result of a gate action
263#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
264pub enum ActionResult {
265    /// Action succeeded
266    Success,
267    /// Action failed
268    Failed,
269    /// Action was skipped
270    Skipped,
271}
272
273/// Performance trend analyzer for gate evaluation
274#[derive(Debug, Clone)]
275pub struct PerformanceTrendAnalyzer {
276    /// Historical performance data
277    pub performance_history: Vec<PerformanceDataPoint>,
278    /// Trend analysis configuration
279    pub config: TrendAnalysisConfig,
280    /// Detected trends
281    pub detected_trends: HashMap<MetricType, PerformanceTrend>,
282}
283
284/// Performance data point for trend analysis
285#[derive(Debug, Clone, Serialize, Deserialize)]
286pub struct PerformanceDataPoint {
287    /// Timestamp
288    pub timestamp: SystemTime,
289    /// Metric type
290    pub metric_type: MetricType,
291    /// Metric value
292    pub value: f64,
293    /// Context information
294    pub context: HashMap<String, String>,
295    /// Data quality score
296    pub quality_score: f64,
297}
298
299/// Trend analysis configuration
300#[derive(Debug, Clone, Serialize, Deserialize)]
301pub struct TrendAnalysisConfig {
302    /// Minimum data points for trend analysis
303    pub min_data_points: usize,
304    /// Analysis window size
305    pub window_size: usize,
306    /// Trend detection sensitivity
307    pub sensitivity: f64,
308    /// Confidence level for trends
309    pub confidence_level: f64,
310    /// Enable seasonal adjustment
311    pub enable_seasonal_adjustment: bool,
312}
313
314/// Detected performance trend
315#[derive(Debug, Clone, Serialize, Deserialize)]
316pub struct PerformanceTrend {
317    /// Metric type
318    pub metric_type: MetricType,
319    /// Trend direction
320    pub direction: TrendDirection,
321    /// Trend strength (0.0 to 1.0)
322    pub strength: f64,
323    /// Trend significance
324    pub significance: f64,
325    /// Trend slope
326    pub slope: f64,
327    /// Trend duration
328    pub duration: Duration,
329    /// Confidence interval
330    pub confidence_interval: (f64, f64),
331    /// Last updated
332    pub last_updated: SystemTime,
333}
334
335/// Trend directions
336#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
337pub enum TrendDirection {
338    /// Improving trend
339    Improving,
340    /// Degrading trend
341    Degrading,
342    /// Stable trend
343    Stable,
344    /// Volatile/unclear trend
345    Volatile,
346}
347
348/// Alert manager for performance gates
349#[derive(Debug, Clone)]
350pub struct AlertManager {
351    /// Alert configuration
352    pub config: AlertConfiguration,
353    /// Active alerts
354    pub active_alerts: HashMap<String, PerformanceAlert>,
355    /// Alert history
356    pub alert_history: Vec<PerformanceAlert>,
357    /// Alert escalation rules
358    pub escalation_rules: Vec<EscalationRule>,
359}
360
361/// Alert configuration
362#[derive(Debug, Clone, Serialize, Deserialize)]
363pub struct AlertConfiguration {
364    /// Enable alerts
365    pub enabled: bool,
366    /// Alert severity threshold
367    pub severity_threshold: GateSeverity,
368    /// Alert cooldown period
369    pub cooldown_period: Duration,
370    /// Maximum alerts per hour
371    pub max_alerts_per_hour: u32,
372    /// Alert channels
373    pub alert_channels: Vec<AlertChannel>,
374}
375
376/// Performance alert
377#[derive(Debug, Clone, Serialize, Deserialize)]
378pub struct PerformanceAlert {
379    /// Alert ID
380    pub id: String,
381    /// Alert type
382    pub alert_type: AlertType,
383    /// Alert severity
384    pub severity: AlertSeverity,
385    /// Alert title
386    pub title: String,
387    /// Alert description
388    pub description: String,
389    /// Affected metrics
390    pub affected_metrics: Vec<MetricType>,
391    /// Alert timestamp
392    pub timestamp: SystemTime,
393    /// Alert status
394    pub status: AlertStatus,
395    /// Related gate evaluation
396    pub gate_evaluation_id: Option<String>,
397    /// Alert metadata
398    pub metadata: HashMap<String, String>,
399}
400
401/// Alert types
402#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
403pub enum AlertType {
404    /// Performance regression alert
405    PerformanceRegression,
406    /// Threshold breach alert
407    ThresholdBreach,
408    /// Trend change alert
409    TrendChange,
410    /// Gate failure alert
411    GateFailure,
412    /// System anomaly alert
413    SystemAnomaly,
414}
415
416/// Alert severity levels
417#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
418pub enum AlertSeverity {
419    /// Informational alert
420    Info,
421    /// Warning alert
422    Warning,
423    /// Error alert
424    Error,
425    /// Critical alert
426    Critical,
427}
428
429/// Alert status
430#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
431pub enum AlertStatus {
432    /// Alert is active
433    Active,
434    /// Alert has been acknowledged
435    Acknowledged,
436    /// Alert has been resolved
437    Resolved,
438    /// Alert has been suppressed
439    Suppressed,
440}
441
442/// Alert channels
443#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
444pub enum AlertChannel {
445    /// Email alerts
446    Email,
447    /// Slack alerts
448    Slack,
449    /// Webhook alerts
450    Webhook,
451    /// Dashboard alerts
452    Dashboard,
453    /// Log alerts
454    Log,
455}
456
457/// Alert escalation rule
458#[derive(Debug, Clone, Serialize, Deserialize)]
459pub struct EscalationRule {
460    /// Rule name
461    pub name: String,
462    /// Trigger condition
463    pub trigger_condition: EscalationTrigger,
464    /// Escalation delay
465    pub delay: Duration,
466    /// Target alert channels
467    pub target_channels: Vec<AlertChannel>,
468    /// Target recipients
469    pub target_recipients: Vec<String>,
470    /// Rule enabled
471    pub enabled: bool,
472}
473
474/// Escalation triggers
475#[derive(Debug, Clone, Serialize, Deserialize)]
476pub enum EscalationTrigger {
477    /// Time-based escalation
478    TimeElapsed(Duration),
479    /// Severity-based escalation
480    SeverityLevel(AlertSeverity),
481    /// Multiple failures
482    MultipleFailures(u32),
483    /// Custom condition
484    Custom(String),
485}
486
487/// Gate evaluation result for public API
488#[derive(Debug, Clone, Serialize, Deserialize)]
489pub struct GateResult {
490    /// Overall gate status
491    pub status: OverallGateStatus,
492    /// Individual gate results
493    pub gate_results: Vec<IndividualGateResult>,
494    /// Gate evaluation summary
495    pub summary: GateEvaluationSummary,
496    /// Evaluation timestamp
497    pub timestamp: SystemTime,
498}
499
500impl PerformanceGateEvaluator {
501    /// Create a new performance gate evaluator
502    pub fn new(config: PerformanceGatesConfig) -> Result<Self> {
503        Ok(Self {
504            config,
505            baseline_metrics: HashMap::new(),
506            evaluation_history: Vec::new(),
507            gate_states: HashMap::new(),
508            trend_analyzer: PerformanceTrendAnalyzer::new(TrendAnalysisConfig::default())?,
509            alert_manager: AlertManager::new(AlertConfiguration::default())?,
510        })
511    }
512
513    /// Evaluate performance gates against test results
514    pub fn evaluate_gates(
515        &mut self,
516        test_results: &[CiCdTestResult],
517        statistics: &TestSuiteStatistics,
518    ) -> Result<GateResult> {
519        let evaluation_id = uuid::Uuid::new_v4().to_string();
520        let start_time = SystemTime::now();
521
522        // Extract metrics from test results
523        let current_metrics = self.extract_metrics_from_results(test_results)?;
524
525        // Update trend analyzer with new data
526        self.update_trend_analysis(&current_metrics)?;
527
528        // Evaluate individual gates
529        let mut gate_results = Vec::new();
530        let mut overall_status = OverallGateStatus::AllPassed;
531
532        for (metric_type, gate_config) in &self.config.metric_gates {
533            if !gate_config.enabled {
534                continue;
535            }
536
537            if let Some(current_value) = current_metrics.get(metric_type) {
538                let gate_result =
539                    self.evaluate_individual_gate(metric_type, *current_value, gate_config)?;
540
541                if gate_result.status == GateStatus::Failed {
542                    overall_status = match overall_status {
543                        OverallGateStatus::AllPassed => OverallGateStatus::SomeFailed,
544                        OverallGateStatus::SomeFailed => OverallGateStatus::SomeFailed,
545                        _ => OverallGateStatus::AllFailed,
546                    };
547                }
548
549                gate_results.push(gate_result);
550            }
551        }
552
553        // Create evaluation summary
554        let summary = self.create_evaluation_summary(&gate_results);
555
556        // Handle gate failures
557        let actions_taken = if overall_status != OverallGateStatus::AllPassed {
558            self.handle_gate_failures(&gate_results)?
559        } else {
560            Vec::new()
561        };
562
563        // Create evaluation result
564        let evaluation_result = GateEvaluationResult {
565            evaluation_id: evaluation_id.clone(),
566            timestamp: start_time,
567            overall_status: overall_status.clone(),
568            gate_results: gate_results.clone(),
569            summary: summary.clone(),
570            actions_taken,
571            evaluation_duration: SystemTime::now()
572                .duration_since(start_time)
573                .unwrap_or_default(),
574        };
575
576        // Store evaluation result
577        self.evaluation_history.push(evaluation_result);
578
579        // Update gate states
580        self.update_gate_states(&gate_results)?;
581
582        // Generate alerts if necessary
583        self.generate_alerts(&gate_results)?;
584
585        Ok(GateResult {
586            status: overall_status,
587            gate_results,
588            summary,
589            timestamp: start_time,
590        })
591    }
592
593    /// Extract performance metrics from test results
594    fn extract_metrics_from_results(
595        &self,
596        test_results: &[CiCdTestResult],
597    ) -> Result<HashMap<MetricType, f64>> {
598        let mut metrics = HashMap::new();
599
600        // Calculate aggregate metrics
601        if !test_results.is_empty() {
602            // Execution time metrics
603            let execution_times: Vec<f64> = test_results
604                .iter()
605                .filter_map(|r| r.duration)
606                .map(|d| d.as_secs_f64())
607                .collect();
608
609            if !execution_times.is_empty() {
610                let avg_execution_time =
611                    execution_times.iter().sum::<f64>() / execution_times.len() as f64;
612                metrics.insert(MetricType::ExecutionTime, avg_execution_time);
613            }
614
615            // Memory usage metrics
616            let memory_usage: Vec<f64> = test_results
617                .iter()
618                .map(|r| r.resource_usage.peak_memory_mb)
619                .collect();
620
621            if !memory_usage.is_empty() {
622                let avg_memory = memory_usage.iter().sum::<f64>() / memory_usage.len() as f64;
623                metrics.insert(MetricType::MemoryUsage, avg_memory);
624            }
625
626            // CPU usage metrics
627            let cpu_usage: Vec<f64> = test_results
628                .iter()
629                .map(|r| r.resource_usage.peak_cpu_percent)
630                .collect();
631
632            if !cpu_usage.is_empty() {
633                let avg_cpu = cpu_usage.iter().sum::<f64>() / cpu_usage.len() as f64;
634                metrics.insert(MetricType::CpuUsage, avg_cpu);
635            }
636
637            // Throughput metrics (simplified calculation)
638            let total_duration: f64 = execution_times.iter().sum();
639            if total_duration > 0.0 {
640                let throughput = test_results.len() as f64 / total_duration;
641                metrics.insert(MetricType::Throughput, throughput);
642            }
643        }
644
645        Ok(metrics)
646    }
647
648    /// Evaluate an individual performance gate
649    fn evaluate_individual_gate(
650        &self,
651        metric_type: &MetricType,
652        current_value: f64,
653        gate_config: &MetricGate,
654    ) -> Result<IndividualGateResult> {
655        let baseline_value = self.get_baseline_value(metric_type);
656        let threshold_value = self.calculate_threshold(baseline_value, gate_config);
657
658        let status = match gate_config.gate_type {
659            GateType::Absolute => {
660                self.evaluate_absolute_gate(current_value, threshold_value, &gate_config.operator)
661            }
662            GateType::Relative => self.evaluate_relative_gate(
663                current_value,
664                baseline_value,
665                gate_config.threshold,
666                &gate_config.operator,
667            ),
668            GateType::Statistical => {
669                self.evaluate_statistical_gate(metric_type, current_value, baseline_value)?
670            }
671            GateType::Trend => self.evaluate_trend_gate(metric_type, current_value)?,
672        };
673
674        let percentage_deviation = if baseline_value != 0.0 {
675            ((current_value - baseline_value) / baseline_value) * 100.0
676        } else {
677            0.0
678        };
679
680        let failure_reason = if status == GateStatus::Failed {
681            Some(self.create_failure_reason(
682                metric_type,
683                current_value,
684                threshold_value,
685                &gate_config.operator,
686            ))
687        } else {
688            None
689        };
690
691        Ok(IndividualGateResult {
692            metric_type: metric_type.clone(),
693            status,
694            current_value,
695            threshold_value,
696            percentage_deviation,
697            severity: gate_config.severity.clone(),
698            failure_reason,
699            details: self.create_evaluation_details(metric_type, current_value, baseline_value)?,
700        })
701    }
702
703    /// Evaluate absolute threshold gate
704    fn evaluate_absolute_gate(
705        &self,
706        current_value: f64,
707        threshold: f64,
708        operator: &ComparisonOperator,
709    ) -> GateStatus {
710        let passed = match operator {
711            ComparisonOperator::LessThan => current_value < threshold,
712            ComparisonOperator::LessThanOrEqual => current_value <= threshold,
713            ComparisonOperator::GreaterThan => current_value > threshold,
714            ComparisonOperator::GreaterThanOrEqual => current_value >= threshold,
715            ComparisonOperator::Equal => (current_value - threshold).abs() < f64::EPSILON,
716            ComparisonOperator::NotEqual => (current_value - threshold).abs() >= f64::EPSILON,
717        };
718
719        if passed {
720            GateStatus::Passed
721        } else {
722            GateStatus::Failed
723        }
724    }
725
726    /// Evaluate relative threshold gate
727    fn evaluate_relative_gate(
728        &self,
729        current_value: f64,
730        baseline_value: f64,
731        threshold: f64,
732        operator: &ComparisonOperator,
733    ) -> GateStatus {
734        if baseline_value == 0.0 {
735            return GateStatus::Error;
736        }
737
738        let percentage_change = ((current_value - baseline_value) / baseline_value).abs();
739
740        let passed = match operator {
741            ComparisonOperator::LessThanOrEqual => percentage_change <= threshold,
742            ComparisonOperator::LessThan => percentage_change < threshold,
743            _ => false, // Other operators not typically used for relative gates
744        };
745
746        if passed {
747            GateStatus::Passed
748        } else {
749            GateStatus::Failed
750        }
751    }
752
753    /// Evaluate statistical significance gate
754    fn evaluate_statistical_gate(
755        &self,
756        metric_type: &MetricType,
757        current_value: f64,
758        baseline_value: f64,
759    ) -> Result<GateStatus> {
760        // Simplified statistical test - in reality, this would use proper statistical methods
761        let difference = (current_value - baseline_value).abs();
762        let relative_difference = if baseline_value != 0.0 {
763            difference / baseline_value
764        } else {
765            difference
766        };
767
768        // Simple threshold-based evaluation (would use t-test, Mann-Whitney U, etc. in practice)
769        if relative_difference > 0.1 {
770            // 10% difference threshold
771            Ok(GateStatus::Failed)
772        } else {
773            Ok(GateStatus::Passed)
774        }
775    }
776
777    /// Evaluate trend-based gate
778    fn evaluate_trend_gate(
779        &self,
780        metric_type: &MetricType,
781        current_value: f64,
782    ) -> Result<GateStatus> {
783        if let Some(trend) = self.trend_analyzer.detected_trends.get(metric_type) {
784            match trend.direction {
785                TrendDirection::Degrading if trend.strength > 0.7 => Ok(GateStatus::Failed),
786                TrendDirection::Degrading if trend.strength > 0.4 => Ok(GateStatus::Warning),
787                _ => Ok(GateStatus::Passed),
788            }
789        } else {
790            Ok(GateStatus::Passed)
791        }
792    }
793
794    /// Get baseline value for metric
795    fn get_baseline_value(&self, metric_type: &MetricType) -> f64 {
796        self.baseline_metrics
797            .get(metric_type)
798            .map(|baseline| baseline.baseline_value)
799            .unwrap_or(0.0)
800    }
801
802    /// Calculate threshold value based on gate configuration
803    fn calculate_threshold(&self, baseline_value: f64, gate_config: &MetricGate) -> f64 {
804        match gate_config.gate_type {
805            GateType::Absolute => gate_config.threshold,
806            GateType::Relative => baseline_value * (1.0 + gate_config.threshold),
807            _ => gate_config.threshold,
808        }
809    }
810
811    /// Create failure reason message
812    fn create_failure_reason(
813        &self,
814        metric_type: &MetricType,
815        current_value: f64,
816        threshold: f64,
817        operator: &ComparisonOperator,
818    ) -> String {
819        format!(
820            "{:?} value {:.2} does not satisfy {:?} {:.2}",
821            metric_type, current_value, operator, threshold
822        )
823    }
824
825    /// Create detailed evaluation information
826    fn create_evaluation_details(
827        &self,
828        metric_type: &MetricType,
829        current_value: f64,
830        baseline_value: f64,
831    ) -> Result<GateEvaluationDetails> {
832        Ok(GateEvaluationDetails {
833            evaluation_method: "threshold_comparison".to_string(),
834            statistical_tests: Vec::new(), // Would include actual test results
835            confidence_level: 0.95,
836            p_value: None,
837            effect_size: Some((current_value - baseline_value).abs()),
838            metadata: HashMap::new(),
839        })
840    }
841
842    /// Create evaluation summary
843    fn create_evaluation_summary(
844        &self,
845        gate_results: &[IndividualGateResult],
846    ) -> GateEvaluationSummary {
847        let total_gates = gate_results.len();
848        let gates_passed = gate_results
849            .iter()
850            .filter(|r| r.status == GateStatus::Passed)
851            .count();
852        let gates_failed = gate_results
853            .iter()
854            .filter(|r| r.status == GateStatus::Failed)
855            .count();
856        let gates_warnings = gate_results
857            .iter()
858            .filter(|r| r.status == GateStatus::Warning)
859            .count();
860        let gates_skipped = gate_results
861            .iter()
862            .filter(|r| r.status == GateStatus::Skipped)
863            .count();
864        let critical_failures = gate_results
865            .iter()
866            .filter(|r| r.status == GateStatus::Failed && r.severity == GateSeverity::Critical)
867            .count();
868        let improvements_detected = gate_results
869            .iter()
870            .filter(|r| r.percentage_deviation < -5.0) // 5% improvement threshold
871            .count();
872
873        let regression_severity = if critical_failures > 0 {
874            RegressionSeverity::Critical
875        } else if gates_failed > total_gates / 2 {
876            RegressionSeverity::Major
877        } else if gates_failed > 0 {
878            RegressionSeverity::Moderate
879        } else {
880            RegressionSeverity::None
881        };
882
883        GateEvaluationSummary {
884            total_gates,
885            gates_passed,
886            gates_failed,
887            gates_warnings,
888            gates_skipped,
889            critical_failures,
890            improvements_detected,
891            regression_severity,
892        }
893    }
894
895    /// Handle gate failures
896    fn handle_gate_failures(
897        &mut self,
898        gate_results: &[IndividualGateResult],
899    ) -> Result<Vec<GateAction>> {
900        let mut actions = Vec::new();
901
902        // Determine action based on configuration
903        let action_type = match self.config.failure_handling.failure_action {
904            GateFailureAction::FailBuild => GateActionType::FailBuild,
905            GateFailureAction::MarkUnstable => GateActionType::MarkUnstable,
906            GateFailureAction::LogWarning => GateActionType::LogWarning,
907            GateFailureAction::NotifyOnly => GateActionType::SendNotification,
908        };
909
910        let action = GateAction {
911            action_type,
912            description: format!(
913                "Gate failure action triggered for {} failed gates",
914                gate_results
915                    .iter()
916                    .filter(|r| r.status == GateStatus::Failed)
917                    .count()
918            ),
919            timestamp: SystemTime::now(),
920            result: ActionResult::Success, // Simplified
921        };
922
923        actions.push(action);
924
925        // Send notifications if configured
926        if self.config.failure_handling.notifications.send_email
927            || self.config.failure_handling.notifications.send_slack
928            || self.config.failure_handling.notifications.send_webhooks
929        {
930            let notification_action = GateAction {
931                action_type: GateActionType::SendNotification,
932                description: "Notification sent for gate failures".to_string(),
933                timestamp: SystemTime::now(),
934                result: ActionResult::Success,
935            };
936            actions.push(notification_action);
937        }
938
939        Ok(actions)
940    }
941
942    /// Update gate states
943    fn update_gate_states(&mut self, gate_results: &[IndividualGateResult]) -> Result<()> {
944        for result in gate_results {
945            let baseline_value = self.get_baseline_value(&result.metric_type);
946
947            let gate_state = GateState {
948                metric_type: result.metric_type.clone(),
949                status: result.status.clone(),
950                current_value: result.current_value,
951                baseline_value,
952                percentage_change: result.percentage_deviation,
953                evaluated_at: SystemTime::now(),
954                gate_config: self
955                    .config
956                    .metric_gates
957                    .get(&result.metric_type)
958                    .cloned()
959                    .unwrap_or(MetricGate {
960                        gate_type: GateType::Absolute,
961                        threshold: 0.0,
962                        operator: ComparisonOperator::LessThanOrEqual,
963                        severity: GateSeverity::Warning,
964                        enabled: true,
965                    }),
966                evaluation_details: result.details.clone(),
967            };
968
969            self.gate_states
970                .insert(result.metric_type.clone(), gate_state);
971        }
972
973        Ok(())
974    }
975
976    /// Update trend analysis with new metrics
977    fn update_trend_analysis(&mut self, metrics: &HashMap<MetricType, f64>) -> Result<()> {
978        let timestamp = SystemTime::now();
979
980        for (metric_type, value) in metrics {
981            let data_point = PerformanceDataPoint {
982                timestamp,
983                metric_type: metric_type.clone(),
984                value: *value,
985                context: HashMap::new(),
986                quality_score: 1.0,
987            };
988
989            self.trend_analyzer.add_data_point(data_point)?;
990        }
991
992        // Update detected trends
993        self.trend_analyzer.analyze_trends()?;
994
995        Ok(())
996    }
997
998    /// Generate alerts for gate failures
999    fn generate_alerts(&mut self, gate_results: &[IndividualGateResult]) -> Result<()> {
1000        for result in gate_results {
1001            if result.status == GateStatus::Failed {
1002                let alert_severity = match result.severity {
1003                    GateSeverity::Critical => AlertSeverity::Critical,
1004                    GateSeverity::Error => AlertSeverity::Error,
1005                    GateSeverity::Warning => AlertSeverity::Warning,
1006                    GateSeverity::Info => AlertSeverity::Info,
1007                };
1008
1009                let alert = PerformanceAlert {
1010                    id: uuid::Uuid::new_v4().to_string(),
1011                    alert_type: AlertType::GateFailure,
1012                    severity: alert_severity,
1013                    title: format!("Performance Gate Failed: {:?}", result.metric_type),
1014                    description: result
1015                        .failure_reason
1016                        .clone()
1017                        .unwrap_or_else(|| "Gate failed".to_string()),
1018                    affected_metrics: vec![result.metric_type.clone()],
1019                    timestamp: SystemTime::now(),
1020                    status: AlertStatus::Active,
1021                    gate_evaluation_id: None,
1022                    metadata: HashMap::new(),
1023                };
1024
1025                self.alert_manager.add_alert(alert)?;
1026            }
1027        }
1028
1029        Ok(())
1030    }
1031
1032    /// Set baseline metric
1033    pub fn set_baseline(&mut self, metric_type: MetricType, baseline: BaselineMetric) {
1034        self.baseline_metrics.insert(metric_type, baseline);
1035    }
1036
1037    /// Get gate evaluation history
1038    pub fn get_evaluation_history(&self) -> &[GateEvaluationResult] {
1039        &self.evaluation_history
1040    }
1041
1042    /// Get current gate states
1043    pub fn get_gate_states(&self) -> &HashMap<MetricType, GateState> {
1044        &self.gate_states
1045    }
1046}
1047
1048impl PerformanceTrendAnalyzer {
1049    /// Create a new trend analyzer
1050    pub fn new(config: TrendAnalysisConfig) -> Result<Self> {
1051        Ok(Self {
1052            performance_history: Vec::new(),
1053            config,
1054            detected_trends: HashMap::new(),
1055        })
1056    }
1057
1058    /// Add a performance data point
1059    pub fn add_data_point(&mut self, data_point: PerformanceDataPoint) -> Result<()> {
1060        self.performance_history.push(data_point);
1061
1062        // Keep only recent data points
1063        let max_points = self.config.window_size * 2;
1064        if self.performance_history.len() > max_points {
1065            self.performance_history
1066                .drain(0..self.performance_history.len() - max_points);
1067        }
1068
1069        Ok(())
1070    }
1071
1072    /// Analyze trends in performance data
1073    pub fn analyze_trends(&mut self) -> Result<()> {
1074        let metrics: std::collections::HashSet<MetricType> = self
1075            .performance_history
1076            .iter()
1077            .map(|dp| dp.metric_type.clone())
1078            .collect();
1079
1080        for metric_type in metrics {
1081            let metric_data: Vec<&PerformanceDataPoint> = self
1082                .performance_history
1083                .iter()
1084                .filter(|dp| dp.metric_type == metric_type)
1085                .collect();
1086
1087            if metric_data.len() >= self.config.min_data_points {
1088                let trend = self.detect_trend(&metric_data)?;
1089                self.detected_trends.insert(metric_type, trend);
1090            }
1091        }
1092
1093        Ok(())
1094    }
1095
1096    /// Detect trend in metric data
1097    fn detect_trend(&self, data: &[&PerformanceDataPoint]) -> Result<PerformanceTrend> {
1098        let values: Vec<f64> = data.iter().map(|dp| dp.value).collect();
1099
1100        // Simple linear regression for trend detection
1101        let n = values.len() as f64;
1102        let x_values: Vec<f64> = (0..values.len()).map(|i| i as f64).collect();
1103
1104        let sum_x = x_values.iter().sum::<f64>();
1105        let sum_y = values.iter().sum::<f64>();
1106        let sum_xy = x_values
1107            .iter()
1108            .zip(values.iter())
1109            .map(|(x, y)| x * y)
1110            .sum::<f64>();
1111        let sum_x2 = x_values.iter().map(|x| x * x).sum::<f64>();
1112
1113        let slope = (n * sum_xy - sum_x * sum_y) / (n * sum_x2 - sum_x * sum_x);
1114
1115        let direction = if slope > self.config.sensitivity {
1116            TrendDirection::Improving
1117        } else if slope < -self.config.sensitivity {
1118            TrendDirection::Degrading
1119        } else {
1120            TrendDirection::Stable
1121        };
1122
1123        let strength = slope.abs().min(1.0);
1124
1125        Ok(PerformanceTrend {
1126            metric_type: data[0].metric_type.clone(),
1127            direction,
1128            strength,
1129            significance: 0.8, // Simplified
1130            slope,
1131            duration: data
1132                .last()
1133                .expect("unwrap failed")
1134                .timestamp
1135                .duration_since(data[0].timestamp)
1136                .unwrap_or_default(),
1137            confidence_interval: (slope - 0.1, slope + 0.1), // Simplified
1138            last_updated: SystemTime::now(),
1139        })
1140    }
1141}
1142
1143impl AlertManager {
1144    /// Create a new alert manager
1145    pub fn new(config: AlertConfiguration) -> Result<Self> {
1146        Ok(Self {
1147            config,
1148            active_alerts: HashMap::new(),
1149            alert_history: Vec::new(),
1150            escalation_rules: Vec::new(),
1151        })
1152    }
1153
1154    /// Add a new alert
1155    pub fn add_alert(&mut self, alert: PerformanceAlert) -> Result<()> {
1156        self.active_alerts.insert(alert.id.clone(), alert.clone());
1157        self.alert_history.push(alert);
1158        Ok(())
1159    }
1160
1161    /// Acknowledge an alert
1162    pub fn acknowledge_alert(&mut self, alert_id: &str) -> Result<()> {
1163        if let Some(alert) = self.active_alerts.get_mut(alert_id) {
1164            alert.status = AlertStatus::Acknowledged;
1165        }
1166        Ok(())
1167    }
1168
1169    /// Resolve an alert
1170    pub fn resolve_alert(&mut self, alert_id: &str) -> Result<()> {
1171        if let Some(alert) = self.active_alerts.remove(alert_id) {
1172            // Move to history with resolved status
1173            let mut resolved_alert = alert;
1174            resolved_alert.status = AlertStatus::Resolved;
1175            self.alert_history.push(resolved_alert);
1176        }
1177        Ok(())
1178    }
1179
1180    /// Get active alerts
1181    pub fn get_active_alerts(&self) -> Vec<&PerformanceAlert> {
1182        self.active_alerts.values().collect()
1183    }
1184}
1185
1186// Default implementations
1187
1188impl Default for TrendAnalysisConfig {
1189    fn default() -> Self {
1190        Self {
1191            min_data_points: 10,
1192            window_size: 50,
1193            sensitivity: 0.1,
1194            confidence_level: 0.95,
1195            enable_seasonal_adjustment: false,
1196        }
1197    }
1198}
1199
1200impl Default for AlertConfiguration {
1201    fn default() -> Self {
1202        Self {
1203            enabled: true,
1204            severity_threshold: GateSeverity::Warning,
1205            cooldown_period: Duration::from_secs(300), // 5 minutes
1206            max_alerts_per_hour: 10,
1207            alert_channels: vec![AlertChannel::Log],
1208        }
1209    }
1210}
1211
1212#[cfg(test)]
1213mod tests {
1214    use super::*;
1215
1216    #[test]
1217    fn test_gate_evaluator_creation() {
1218        let config = PerformanceGatesConfig::default();
1219        let evaluator = PerformanceGateEvaluator::new(config);
1220        assert!(evaluator.is_ok());
1221    }
1222
1223    #[test]
1224    fn test_gate_status_equality() {
1225        assert_eq!(GateStatus::Passed, GateStatus::Passed);
1226        assert_ne!(GateStatus::Passed, GateStatus::Failed);
1227    }
1228
1229    #[test]
1230    fn test_trend_direction() {
1231        assert_eq!(TrendDirection::Improving, TrendDirection::Improving);
1232        assert_ne!(TrendDirection::Improving, TrendDirection::Degrading);
1233    }
1234
1235    #[test]
1236    fn test_alert_severity_ordering() {
1237        assert!(AlertSeverity::Info < AlertSeverity::Warning);
1238        assert!(AlertSeverity::Warning < AlertSeverity::Error);
1239        assert!(AlertSeverity::Error < AlertSeverity::Critical);
1240    }
1241
1242    #[test]
1243    fn test_baseline_metric() {
1244        let baseline = BaselineMetric {
1245            metric_type: MetricType::ExecutionTime,
1246            baseline_value: 1.0,
1247            variance_threshold: 0.1,
1248            confidence_interval: (0.9, 1.1),
1249            sample_size: 100,
1250            last_updated: SystemTime::now(),
1251            statistical_significance: 0.95,
1252        };
1253
1254        assert_eq!(baseline.baseline_value, 1.0);
1255        assert_eq!(baseline.sample_size, 100);
1256    }
1257}