#![ cfg( feature = "enabled" ) ]
#![ cfg( feature = "markdown_reports" ) ]
#![ allow( clippy ::uninlined_format_args ) ]
#![ allow( clippy ::format_push_string ) ]
#![ allow( clippy ::cast_lossless ) ]
#![ allow( clippy ::cast_possible_truncation ) ]
#![ allow( clippy ::cast_precision_loss ) ]
#![ allow( clippy ::std_instead_of_core ) ]
#![ allow( clippy ::needless_raw_string_hashes ) ]
#![ allow( clippy ::too_many_lines ) ]
use benchkit ::prelude :: *;
use std ::collections ::HashMap;
use std ::time ::Duration;
#[ derive( Debug, Clone, Copy, PartialEq ) ]
#[ allow( dead_code ) ] enum CiExitCode
{
Success = 0,
PerformanceRegression = 1,
InsufficientData = 2,
ValidationFailure = 3,
SystemError = 4,
}
#[ derive( Debug, Clone ) ]
struct CiCdConfig
{
environment: String,
regression_threshold: f64,
significance_level: f64,
min_reliability: f64,
baseline_strategy: BaselineStrategy,
}
impl CiCdConfig
{
fn development() -> Self
{
Self
{
environment: "development".to_string(),
regression_threshold: 0.15, significance_level: 0.10, min_reliability: 70.0, baseline_strategy: BaselineStrategy ::PreviousRun,
}
}
fn staging() -> Self
{
Self
{
environment: "staging".to_string(),
regression_threshold: 0.10, significance_level: 0.05, min_reliability: 85.0, baseline_strategy: BaselineStrategy ::RollingAverage,
}
}
fn production() -> Self
{
Self
{
environment: "production".to_string(),
regression_threshold: 0.05, significance_level: 0.01, min_reliability: 95.0, baseline_strategy: BaselineStrategy ::FixedBaseline,
}
}
}
fn create_baseline_results() -> HashMap< String, BenchmarkResult >
{
let mut baseline = HashMap ::new();
let api_times = vec![
Duration ::from_millis( 45 ), Duration ::from_millis( 48 ), Duration ::from_millis( 42 ),
Duration ::from_millis( 47 ), Duration ::from_millis( 44 ), Duration ::from_millis( 46 ),
Duration ::from_millis( 49 ), Duration ::from_millis( 43 ), Duration ::from_millis( 47 ),
Duration ::from_millis( 45 ), Duration ::from_millis( 48 ), Duration ::from_millis( 44 )
];
baseline.insert( "api_response_time".to_string(), BenchmarkResult ::new( "api_response_time", api_times ) );
let db_times = vec![
Duration ::from_micros( 850 ), Duration ::from_micros( 870 ), Duration ::from_micros( 830 ),
Duration ::from_micros( 860 ), Duration ::from_micros( 845 ), Duration ::from_micros( 875 ),
Duration ::from_micros( 825 ), Duration ::from_micros( 865 ), Duration ::from_micros( 840 ),
Duration ::from_micros( 855 ), Duration ::from_micros( 880 ), Duration ::from_micros( 835 )
];
baseline.insert( "database_query".to_string(), BenchmarkResult ::new( "database_query", db_times ) );
let memory_times = vec![
Duration ::from_nanos( 120 ), Duration ::from_nanos( 125 ), Duration ::from_nanos( 115 ),
Duration ::from_nanos( 122 ), Duration ::from_nanos( 118 ), Duration ::from_nanos( 127 ),
Duration ::from_nanos( 113 ), Duration ::from_nanos( 124 ), Duration ::from_nanos( 119 ),
Duration ::from_nanos( 121 ), Duration ::from_nanos( 126 ), Duration ::from_nanos( 116 )
];
baseline.insert( "memory_allocation".to_string(), BenchmarkResult ::new( "memory_allocation", memory_times ) );
baseline
}
fn create_pr_results_with_regression() -> HashMap< String, BenchmarkResult >
{
let mut pr_results = HashMap ::new();
let api_times = vec![
Duration ::from_millis( 52 ), Duration ::from_millis( 55 ), Duration ::from_millis( 49 ),
Duration ::from_millis( 54 ), Duration ::from_millis( 51 ), Duration ::from_millis( 53 ),
Duration ::from_millis( 56 ), Duration ::from_millis( 50 ), Duration ::from_millis( 54 ),
Duration ::from_millis( 52 ), Duration ::from_millis( 55 ), Duration ::from_millis( 51 )
];
pr_results.insert( "api_response_time".to_string(), BenchmarkResult ::new( "api_response_time", api_times ) );
let db_times = vec![
Duration ::from_micros( 810 ), Duration ::from_micros( 825 ), Duration ::from_micros( 795 ),
Duration ::from_micros( 815 ), Duration ::from_micros( 805 ), Duration ::from_micros( 830 ),
Duration ::from_micros( 790 ), Duration ::from_micros( 820 ), Duration ::from_micros( 800 ),
Duration ::from_micros( 812 ), Duration ::from_micros( 828 ), Duration ::from_micros( 798 )
];
pr_results.insert( "database_query".to_string(), BenchmarkResult ::new( "database_query", db_times ) );
let memory_times = vec![
Duration ::from_nanos( 119 ), Duration ::from_nanos( 124 ), Duration ::from_nanos( 114 ),
Duration ::from_nanos( 121 ), Duration ::from_nanos( 117 ), Duration ::from_nanos( 126 ),
Duration ::from_nanos( 112 ), Duration ::from_nanos( 123 ), Duration ::from_nanos( 118 ),
Duration ::from_nanos( 120 ), Duration ::from_nanos( 125 ), Duration ::from_nanos( 115 )
];
pr_results.insert( "memory_allocation".to_string(), BenchmarkResult ::new( "memory_allocation", memory_times ) );
pr_results
}
fn create_pr_results_good() -> HashMap< String, BenchmarkResult >
{
let mut pr_results = HashMap ::new();
let api_times = vec![
Duration ::from_millis( 43 ), Duration ::from_millis( 46 ), Duration ::from_millis( 40 ),
Duration ::from_millis( 45 ), Duration ::from_millis( 42 ), Duration ::from_millis( 44 ),
Duration ::from_millis( 47 ), Duration ::from_millis( 41 ), Duration ::from_millis( 45 ),
Duration ::from_millis( 43 ), Duration ::from_millis( 46 ), Duration ::from_millis( 42 )
];
pr_results.insert( "api_response_time".to_string(), BenchmarkResult ::new( "api_response_time", api_times ) );
let db_times = vec![
Duration ::from_micros( 720 ), Duration ::from_micros( 740 ), Duration ::from_micros( 700 ),
Duration ::from_micros( 730 ), Duration ::from_micros( 715 ), Duration ::from_micros( 745 ),
Duration ::from_micros( 695 ), Duration ::from_micros( 735 ), Duration ::from_micros( 710 ),
Duration ::from_micros( 725 ), Duration ::from_micros( 750 ), Duration ::from_micros( 705 )
];
pr_results.insert( "database_query".to_string(), BenchmarkResult ::new( "database_query", db_times ) );
let memory_times = vec![
Duration ::from_nanos( 118 ), Duration ::from_nanos( 123 ), Duration ::from_nanos( 113 ),
Duration ::from_nanos( 120 ), Duration ::from_nanos( 116 ), Duration ::from_nanos( 125 ),
Duration ::from_nanos( 111 ), Duration ::from_nanos( 122 ), Duration ::from_nanos( 117 ),
Duration ::from_nanos( 119 ), Duration ::from_nanos( 124 ), Duration ::from_nanos( 114 )
];
pr_results.insert( "memory_allocation".to_string(), BenchmarkResult ::new( "memory_allocation", memory_times ) );
pr_results
}
fn run_performance_validation( config: &CiCdConfig, pr_results: &HashMap< String, BenchmarkResult >, baseline_results: &HashMap< String, BenchmarkResult > ) -> ( CiExitCode, String )
{
println!( "🚀 RUNNING PERFORMANCE VALIDATION" );
println!( " Environment: {}", config.environment );
println!( " Regression Threshold: {}%", ( config.regression_threshold * 100.0 ) as i32 );
println!( " Significance Level: {}%", ( config.significance_level * 100.0 ) as i32 );
let validator = BenchmarkValidator ::new()
.min_samples( 8 )
.max_coefficient_variation( 0.20 );
let pr_validation = ValidatedResults ::new( pr_results.clone(), validator.clone() );
let baseline_validation = ValidatedResults ::new( baseline_results.clone(), validator );
if pr_validation.reliability_rate() < config.min_reliability
{
let message = format!( "❌ PR benchmark quality insufficient: {:.1}% < {:.1}%", pr_validation.reliability_rate(), config.min_reliability );
return ( CiExitCode ::InsufficientData, message );
}
if baseline_validation.reliability_rate() < config.min_reliability
{
let message = format!( "❌ Baseline benchmark quality insufficient: {:.1}% < {:.1}%", baseline_validation.reliability_rate(), config.min_reliability );
return ( CiExitCode ::InsufficientData, message );
}
println!( " ✅ Data quality validation passed" );
let historical = HistoricalResults ::new().with_baseline( baseline_results.clone() );
let analyzer = RegressionAnalyzer ::new()
.with_baseline_strategy( config.baseline_strategy.clone() )
.with_significance_threshold( config.significance_level );
let regression_report = analyzer.analyze( pr_results, &historical );
let mut regressions = Vec ::new();
let mut improvements = Vec ::new();
let mut stable = Vec ::new();
for operation in pr_results.keys()
{
if let Some( trend ) = regression_report.get_trend_for( operation )
{
match trend
{
PerformanceTrend ::Degrading =>
{
if regression_report.is_statistically_significant( operation )
{
regressions.push( operation.clone() );
}
else
{
stable.push( operation.clone() );
}
},
PerformanceTrend ::Improving =>
{
improvements.push( operation.clone() );
},
PerformanceTrend ::Stable =>
{
stable.push( operation.clone() );
}
}
}
}
if !regressions.is_empty()
{
let message = format!( "❌ Performance regressions detected in: {}", regressions.join( ", " ) );
println!( " {}", message );
return ( CiExitCode ::PerformanceRegression, message );
}
let mut message = String ::new();
if !improvements.is_empty()
{
message.push_str( &format!( "🎉 Performance improvements in: {}", improvements.join( ", " ) ) );
}
if !stable.is_empty()
{
if !message.is_empty() { message.push_str( "; " ); }
message.push_str( &format!( "✅ Stable performance in: {}", stable.join( ", " ) ) );
}
if message.is_empty()
{
message = "✅ Performance validation passed".to_string();
}
println!( " {}", message );
( CiExitCode ::Success, message )
}
fn generate_github_actions_report( pr_results: &HashMap< String, BenchmarkResult >, baseline_results: &HashMap< String, BenchmarkResult > ) -> String
{
let historical = HistoricalResults ::new().with_baseline( baseline_results.clone() );
let analyzer = RegressionAnalyzer ::new().with_baseline_strategy( BaselineStrategy ::FixedBaseline );
let regression_report = analyzer.analyze( pr_results, &historical );
let mut report = String ::new();
report.push_str( "## 🚀 Performance Analysis Report\n\n" );
report.push_str( "| Benchmark | Trend | Status | Notes |\n" );
report.push_str( "|-----------|--------|--------|-------|\n" );
for operation in pr_results.keys()
{
let trend_icon = match regression_report.get_trend_for( operation )
{
Some( PerformanceTrend ::Improving ) => "🟢 ↗️",
Some( PerformanceTrend ::Degrading ) => "🔴 ↘️",
Some( PerformanceTrend ::Stable ) => "🟡 ➡️",
None => "⚪ ?",
};
let status = if regression_report.is_statistically_significant( operation )
{
"Significant"
}
else
{
"Normal variation"
};
let notes = match operation.as_str()
{
"api_response_time" => "Critical user-facing metric",
"database_query" => "Backend performance indicator",
"memory_allocation" => "Resource utilization metric",
_ => "Performance metric",
};
report.push_str( &format!( "| {} | {} | {} | {} |\n", operation, trend_icon, status, notes ) );
}
report.push_str( "\n### Summary\n\n" );
if regression_report.has_significant_changes()
{
report.push_str( "⚠️ **Significant performance changes detected.** Please review before merging.\n\n" );
}
else
{
report.push_str( "✅ **No significant performance regressions detected.** Safe to merge.\n\n" );
}
report.push_str( ®ression_report.format_markdown() );
report
}
fn demonstrate_development_pr_validation()
{
println!( "🔧 DEVELOPMENT ENVIRONMENT PR VALIDATION" );
println!( "=========================================" );
println!( "Simulating a typical development PR with lenient thresholds for iteration speed.\n" );
let config = CiCdConfig ::development();
let baseline = create_baseline_results();
let pr_results = create_pr_results_with_regression();
let ( exit_code, message ) = run_performance_validation( &config, &pr_results, &baseline );
match exit_code
{
CiExitCode ::Success => println!( "🟢 CI/CD Result: PASSED - Continue development" ),
CiExitCode ::PerformanceRegression => println!( "🟡 CI/CD Result: WARNING - Monitor performance but allow merge" ),
_ => println!( "🔴 CI/CD Result: FAILED - {}", message ),
}
println!( "💡 Development Strategy: Fast iteration with performance awareness\n" );
}
fn demonstrate_staging_pr_validation()
{
println!( "🎭 STAGING ENVIRONMENT PR VALIDATION" );
println!( "====================================" );
println!( "Simulating staging validation with moderate performance requirements.\n" );
let config = CiCdConfig ::staging();
let baseline = create_baseline_results();
println!( "📊 Testing PR with performance regression: " );
let pr_with_regression = create_pr_results_with_regression();
let ( exit_code, message ) = run_performance_validation( &config, &pr_with_regression, &baseline );
match exit_code
{
CiExitCode ::Success => println!( "🟢 Staging Result: PASSED" ),
CiExitCode ::PerformanceRegression => println!( "🔴 Staging Result: BLOCKED - {}", message ),
_ => println!( "🟡 Staging Result: REVIEW NEEDED - {}", message ),
}
println!();
println!( "📊 Testing PR with good performance: " );
let pr_good = create_pr_results_good();
let ( exit_code, message ) = run_performance_validation( &config, &pr_good, &baseline );
match exit_code
{
CiExitCode ::Success => println!( "🟢 Staging Result: PASSED - {}", message ),
_ => println!( "🔴 Staging Result: UNEXPECTED - {}", message ),
}
println!( "💡 Staging Strategy: Balanced performance gates before production\n" );
}
fn demonstrate_production_deployment_validation()
{
println!( "🏭 PRODUCTION DEPLOYMENT VALIDATION" );
println!( "===================================" );
println!( "Simulating strict production deployment with minimal regression tolerance.\n" );
let config = CiCdConfig ::production();
let baseline = create_baseline_results();
let pr_results = create_pr_results_good();
let ( exit_code, message ) = run_performance_validation( &config, &pr_results, &baseline );
match exit_code
{
CiExitCode ::Success => println!( "🟢 Production Result: APPROVED FOR DEPLOYMENT" ),
CiExitCode ::PerformanceRegression => println!( "🚨 Production Result: DEPLOYMENT BLOCKED - Critical regression detected" ),
CiExitCode ::InsufficientData => println!( "⏸️ Production Result: DEPLOYMENT PAUSED - Insufficient benchmark data" ),
_ => println!( "❌ Production Result: DEPLOYMENT FAILED - {}", message ),
}
println!( "💡 Production Strategy: Zero tolerance for performance regressions\n" );
}
fn demonstrate_automated_documentation_updates()
{
println!( "📝 AUTOMATED DOCUMENTATION UPDATES" );
println!( "==================================" );
println!( "Demonstrating automatic performance documentation updates in CI/CD.\n" );
let baseline = create_baseline_results();
let pr_results = create_pr_results_good();
let github_report = generate_github_actions_report( &pr_results, &baseline );
println!( "📄 GENERATED GITHUB ACTIONS REPORT: " );
println!( "------------------------------------" );
println!( "{}", github_report );
println!( "🔄 SIMULATING DOCUMENTATION UPDATE: " );
println!( " ✅ Would update README.md performance section" );
println!( " ✅ Would create PR comment with performance analysis" );
println!( " ✅ Would update performance tracking dashboard" );
println!( " ✅ Would notify team channels if regressions detected" );
println!( "💡 Integration Options: " );
println!( " - GitHub Actions: Use performance report as PR comment" );
println!( " - GitLab CI: Update merge request with performance status" );
println!( " - Jenkins: Archive performance reports as build artifacts" );
println!( " - Slack/Teams: Send notifications for significant changes\n" );
}
fn demonstrate_multi_environment_pipeline()
{
println!( "🌍 MULTI-ENVIRONMENT PIPELINE DEMONSTRATION" );
println!( "============================================" );
println!( "Simulating performance validation across development → staging → production.\n" );
let baseline = create_baseline_results();
let pr_results = create_pr_results_with_regression();
let dev_config = CiCdConfig ::development();
let ( dev_exit, dev_message ) = run_performance_validation( &dev_config, &pr_results, &baseline );
println!( "🔧 Development: {} - {}", if dev_exit == CiExitCode ::Success { "PASS" } else { "WARN" }, dev_message );
if dev_exit == CiExitCode ::Success
{
let staging_config = CiCdConfig ::staging();
let ( staging_exit, staging_message ) = run_performance_validation( &staging_config, &pr_results, &baseline );
println!( "🎭 Staging: {} - {}", if staging_exit == CiExitCode ::Success { "PASS" } else { "FAIL" }, staging_message );
if staging_exit == CiExitCode ::Success
{
let prod_config = CiCdConfig ::production();
let ( prod_exit, prod_message ) = run_performance_validation( &prod_config, &pr_results, &baseline );
println!( "🏭 Production: {} - {}", if prod_exit == CiExitCode ::Success { "PASS" } else { "FAIL" }, prod_message );
}
else
{
println!( "🏭 Production: SKIPPED - Staging validation failed" );
}
}
else
{
println!( "🎭 Staging: SKIPPED - Development validation failed" );
println!( "🏭 Production: SKIPPED - Pipeline halted" );
}
println!( "\n💡 Pipeline Strategy: Progressive validation with increasing strictness" );
println!( " - Development: Fast feedback, lenient thresholds" );
println!( " - Staging: Balanced validation, moderate thresholds" );
println!( " - Production: Strict validation, zero regression tolerance\n" );
}
fn main()
{
println!( "🏗️ BENCHKIT CI/CD REGRESSION DETECTION COMPREHENSIVE DEMO" );
println!( "===========================================================" );
println!( "This example demonstrates every aspect of using benchkit in CI/CD pipelines: \n" );
demonstrate_development_pr_validation();
demonstrate_staging_pr_validation();
demonstrate_production_deployment_validation();
demonstrate_automated_documentation_updates();
demonstrate_multi_environment_pipeline();
println!( "✨ SUMMARY OF DEMONSTRATED CI/CD CAPABILITIES: " );
println!( "==============================================" );
println!( "✅ Multi-environment validation (dev, staging, production)" );
println!( "✅ Configurable regression thresholds per environment" );
println!( "✅ Automated performance gate decisions (pass/fail/warn)" );
println!( "✅ Data quality validation before regression analysis" );
println!( "✅ GitHub Actions compatible reporting" );
println!( "✅ Automated documentation updates" );
println!( "✅ Progressive validation pipeline with halt-on-failure" );
println!( "✅ Statistical significance testing for reliable decisions" );
println!( "\n🎯 CI/CD INTEGRATION PATTERNS: " );
println!( "==============================" );
println!( "📋 GitHub Actions: Use as action step with performance reports" );
println!( "📋 GitLab CI: Integrate with merge request validation" );
println!( "📋 Jenkins: Add as pipeline stage with artifact archival" );
println!( "📋 Azure DevOps: Use in build validation with PR comments" );
println!( "\n🚀 Ready for production CI/CD integration with automated performance regression detection!" );
}
#[ cfg( not( feature = "enabled" ) ) ]
fn main()
{
println!( "This example requires the 'enabled' feature." );
println!( "Run with: cargo run --example cicd_regression_detection --features enabled" );
}