use std::collections::HashMap;
use std::time::{Duration, SystemTime};
pub struct TestPerformanceMonitor {
pub metrics: TestPerformanceMetrics,
pub benchmarks: HashMap<String, BenchmarkComparison>,
pub trends: PerformanceTrends,
pub alert_system: PerformanceAlertSystem,
}
impl TestPerformanceMonitor {
#[must_use]
pub fn new() -> Self {
Self {
metrics: TestPerformanceMetrics::default(),
benchmarks: HashMap::new(),
trends: PerformanceTrends::default(),
alert_system: PerformanceAlertSystem::new(),
}
}
pub fn record_execution_time(&mut self, duration: Duration) {
self.metrics.execution_time_distribution.push(duration);
self.trends
.execution_time_trend
.push((SystemTime::now(), duration));
if !self.metrics.execution_time_distribution.is_empty() {
let sum: Duration = self.metrics.execution_time_distribution.iter().sum();
self.metrics.avg_execution_time =
sum / self.metrics.execution_time_distribution.len() as u32;
}
if self.metrics.execution_time_distribution.len() > 1000 {
self.metrics.execution_time_distribution.drain(0..1);
}
if self.trends.execution_time_trend.len() > 1000 {
self.trends.execution_time_trend.drain(0..1);
}
}
pub fn update_success_rate(&mut self, success: bool) {
let current_count = self.metrics.execution_time_distribution.len();
if current_count == 0 {
self.metrics.success_rate = if success { 1.0 } else { 0.0 };
} else {
let current_successes = (self.metrics.success_rate * current_count as f64) as usize;
let new_successes = if success {
current_successes + 1
} else {
current_successes
};
self.metrics.success_rate = new_successes as f64 / (current_count + 1) as f64;
}
self.trends
.success_rate_trend
.push((SystemTime::now(), self.metrics.success_rate));
if self.trends.success_rate_trend.len() > 1000 {
self.trends.success_rate_trend.drain(0..1);
}
}
pub fn set_benchmark(&mut self, name: String, baseline: PerformanceBaseline) {
let comparison = BenchmarkComparison {
baseline,
current: self.metrics.clone(),
delta: PerformanceDelta {
execution_time_change: 0.0,
success_rate_change: 0.0,
resource_usage_change: 0.0,
overall_change: 0.0,
},
timestamp: SystemTime::now(),
};
self.benchmarks.insert(name, comparison);
}
#[must_use]
pub const fn get_metrics(&self) -> &TestPerformanceMetrics {
&self.metrics
}
#[must_use]
pub const fn get_trends(&self) -> &PerformanceTrends {
&self.trends
}
pub fn analyze_trends(&mut self) {
let window_size = 10;
if self.trends.execution_time_trend.len() >= window_size {
let recent: Vec<_> = self
.trends
.execution_time_trend
.iter()
.rev()
.take(window_size)
.collect();
let first_avg = recent
.iter()
.rev()
.take(window_size / 2)
.map(|(_, d)| d.as_secs_f64())
.sum::<f64>()
/ (window_size / 2) as f64;
let second_avg = recent
.iter()
.take(window_size / 2)
.map(|(_, d)| d.as_secs_f64())
.sum::<f64>()
/ (window_size / 2) as f64;
let change = (second_avg - first_avg) / first_avg;
self.trends.trend_analysis.execution_time_direction = if change < -0.1 {
TrendDirection::Improving
} else if change > 0.1 {
TrendDirection::Degrading
} else {
TrendDirection::Stable
};
}
}
pub fn check_alerts(&mut self) {
self.alert_system.check_alerts(&self.metrics);
}
}
#[derive(Debug, Clone)]
pub struct TestPerformanceMetrics {
pub avg_execution_time: Duration,
pub execution_time_distribution: Vec<Duration>,
pub success_rate: f64,
pub resource_efficiency: f64,
pub throughput_rate: f64,
}
impl Default for TestPerformanceMetrics {
fn default() -> Self {
Self {
avg_execution_time: Duration::from_secs(0),
execution_time_distribution: vec![],
success_rate: 0.0,
resource_efficiency: 0.0,
throughput_rate: 0.0,
}
}
}
#[derive(Debug, Clone)]
pub struct BenchmarkComparison {
pub baseline: PerformanceBaseline,
pub current: TestPerformanceMetrics,
pub delta: PerformanceDelta,
pub timestamp: SystemTime,
}
#[derive(Debug, Clone)]
pub struct PerformanceBaseline {
pub execution_time: Duration,
pub success_rate: f64,
pub resource_usage: f64,
pub timestamp: SystemTime,
}
#[derive(Debug, Clone)]
pub struct PerformanceDelta {
pub execution_time_change: f64,
pub success_rate_change: f64,
pub resource_usage_change: f64,
pub overall_change: f64,
}
#[derive(Debug, Clone)]
pub struct PerformanceTrends {
pub execution_time_trend: Vec<(SystemTime, Duration)>,
pub success_rate_trend: Vec<(SystemTime, f64)>,
pub resource_usage_trend: Vec<(SystemTime, f64)>,
pub trend_analysis: TrendAnalysis,
}
impl Default for PerformanceTrends {
fn default() -> Self {
Self {
execution_time_trend: vec![],
success_rate_trend: vec![],
resource_usage_trend: vec![],
trend_analysis: TrendAnalysis::default(),
}
}
}
#[derive(Debug, Clone)]
pub struct TrendAnalysis {
pub execution_time_direction: TrendDirection,
pub success_rate_direction: TrendDirection,
pub resource_usage_direction: TrendDirection,
pub confidence: f64,
}
impl Default for TrendAnalysis {
fn default() -> Self {
Self {
execution_time_direction: TrendDirection::Stable,
success_rate_direction: TrendDirection::Stable,
resource_usage_direction: TrendDirection::Stable,
confidence: 0.0,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TrendDirection {
Improving,
Stable,
Degrading,
Volatile,
Unknown,
}
pub struct PerformanceAlertSystem {
pub alert_rules: Vec<AlertRule>,
pub active_alerts: HashMap<String, PerformanceAlert>,
pub alert_history: Vec<PerformanceAlert>,
}
impl PerformanceAlertSystem {
#[must_use]
pub fn new() -> Self {
Self {
alert_rules: vec![],
active_alerts: HashMap::new(),
alert_history: vec![],
}
}
pub fn add_rule(&mut self, rule: AlertRule) {
self.alert_rules.push(rule);
}
pub fn remove_rule(&mut self, rule_name: &str) {
self.alert_rules.retain(|r| r.name != rule_name);
}
pub fn check_alerts(&mut self, metrics: &TestPerformanceMetrics) {
let triggered_rules: Vec<String> = self
.alert_rules
.iter()
.filter_map(|rule| {
let should_alert = match &rule.condition {
AlertCondition::ThresholdExceeded(threshold) => {
metrics.avg_execution_time.as_secs_f64() > *threshold
}
AlertCondition::ThresholdBelow(threshold) => {
metrics.success_rate < *threshold
}
AlertCondition::PercentageChange(_percentage) => {
false }
AlertCondition::Custom(_) => false,
};
should_alert.then(|| rule.name.clone())
})
.collect();
for rule_name in triggered_rules {
self.trigger_alert(&rule_name, metrics);
}
}
fn trigger_alert(&mut self, rule_name: &str, metrics: &TestPerformanceMetrics) {
let rule = self.alert_rules.iter().find(|r| r.name == rule_name);
if let Some(rule) = rule {
let alert = PerformanceAlert {
id: format!(
"alert_{}_{}",
rule_name,
SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap_or(Duration::ZERO)
.as_secs()
),
rule_name: rule_name.to_string(),
message: format!("Performance alert triggered for rule: {rule_name}"),
severity: rule.severity.clone(),
timestamp: SystemTime::now(),
metric_value: metrics.avg_execution_time.as_secs_f64(),
threshold_value: 0.0, status: AlertStatus::Active,
};
self.active_alerts.insert(alert.id.clone(), alert.clone());
self.alert_history.push(alert);
if self.alert_history.len() > 1000 {
self.alert_history.drain(0..1);
}
}
}
pub fn acknowledge_alert(&mut self, alert_id: &str) -> Result<(), String> {
let alert = self
.active_alerts
.get_mut(alert_id)
.ok_or_else(|| format!("Alert {alert_id} not found"))?;
alert.status = AlertStatus::Acknowledged;
Ok(())
}
pub fn resolve_alert(&mut self, alert_id: &str) -> Result<(), String> {
let alert = self
.active_alerts
.remove(alert_id)
.ok_or_else(|| format!("Alert {alert_id} not found"))?;
for hist_alert in &mut self.alert_history {
if hist_alert.id == alert_id {
hist_alert.status = AlertStatus::Resolved;
}
}
Ok(())
}
#[must_use]
pub fn get_active_alerts(&self) -> Vec<&PerformanceAlert> {
self.active_alerts.values().collect()
}
#[must_use]
pub fn get_alert_history(&self) -> &[PerformanceAlert] {
&self.alert_history
}
pub fn clear_all_alerts(&mut self) {
self.active_alerts.clear();
self.alert_history.clear();
}
}
#[derive(Debug, Clone)]
pub struct AlertRule {
pub name: String,
pub metric: String,
pub condition: AlertCondition,
pub severity: AlertSeverity,
pub actions: Vec<AlertAction>,
}
#[derive(Debug, Clone)]
pub enum AlertCondition {
ThresholdExceeded(f64),
ThresholdBelow(f64),
PercentageChange(f64),
Custom(String),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AlertSeverity {
Info,
Warning,
Error,
Critical,
}
#[derive(Debug, Clone)]
pub enum AlertAction {
Log,
Email(String),
ExecuteScript(String),
Custom(String),
}
#[derive(Debug, Clone)]
pub struct PerformanceAlert {
pub id: String,
pub rule_name: String,
pub message: String,
pub severity: AlertSeverity,
pub timestamp: SystemTime,
pub metric_value: f64,
pub threshold_value: f64,
pub status: AlertStatus,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AlertStatus {
Active,
Resolved,
Acknowledged,
Suppressed,
}