1use crate::error::{CoreError, CoreResult};
63use std::collections::{BTreeMap, HashMap};
64use std::path::{Path, PathBuf};
65use std::sync::{Arc, Mutex, RwLock};
66use std::time::{Duration, Instant, SystemTime};
67
68use serde::{Deserialize, Serialize};
69
70#[derive(Debug, Clone, Serialize, Deserialize)]
72pub struct CoverageConfig {
73 pub coverage_types: Vec<CoverageType>,
75 pub coverage_threshold: f64,
77 pub branch_threshold: f64,
79 pub integration_threshold: f64,
81 pub report_formats: Vec<ReportFormat>,
83 pub output_directory: PathBuf,
85 pub include_systemcode: bool,
87 pub exclude_patterns: Vec<String>,
89 pub include_patterns: Vec<String>,
91 pub real_time_updates: bool,
93 pub samplingrate: f64,
95 pub enable_history: bool,
97 pub history_retention: Duration,
99 pub enable_diff_coverage: bool,
101 pub diffbase: Option<String>,
103}
104
105impl Default for CoverageConfig {
106 fn default() -> Self {
107 Self {
108 coverage_types: vec![CoverageType::Line, CoverageType::Branch],
109 coverage_threshold: 80.0,
110 branch_threshold: 70.0,
111 integration_threshold: 60.0,
112 report_formats: vec![ReportFormat::Html, ReportFormat::Json],
113 output_directory: PathBuf::from("coverage_reports"),
114 include_systemcode: false,
115 exclude_patterns: vec![
116 "*/tests/*".to_string(),
117 "*/benches/*".to_string(),
118 "*/examples/*".to_string(),
119 ],
120 include_patterns: vec![],
121 real_time_updates: true,
122 samplingrate: 1.0,
123 enable_history: true,
124 history_retention: Duration::from_secs(30 * 24 * 60 * 60), enable_diff_coverage: false,
126 diffbase: None,
127 }
128 }
129}
130
131impl CoverageConfig {
132 pub fn production() -> Self {
134 Self {
135 coverage_threshold: 85.0,
136 branch_threshold: 75.0,
137 integration_threshold: 70.0,
138 samplingrate: 0.1, real_time_updates: false,
140 ..Default::default()
141 }
142 }
143
144 pub fn development() -> Self {
146 Self {
147 coverage_types: vec![
148 CoverageType::Line,
149 CoverageType::Branch,
150 CoverageType::Function,
151 CoverageType::Integration,
152 ],
153 coverage_threshold: 75.0,
154 real_time_updates: true,
155 samplingrate: 1.0,
156 ..Default::default()
157 }
158 }
159
160 pub fn with_coverage_types(mut self, types: Vec<CoverageType>) -> Self {
162 self.coverage_types = types;
163 self
164 }
165
166 pub fn with_threshold(mut self, threshold: f64) -> Self {
168 self.coverage_threshold = threshold;
169 self
170 }
171
172 pub fn with_branch_threshold(mut self, threshold: f64) -> Self {
174 self.branch_threshold = threshold;
175 self
176 }
177
178 pub fn with_report_format(mut self, format: ReportFormat) -> Self {
180 self.report_formats = vec![format];
181 self
182 }
183
184 pub fn with_output_directory<P: AsRef<Path>>(mut self, path: P) -> Self {
186 self.output_directory = path.as_ref().to_path_buf();
187 self
188 }
189
190 pub fn with_diff_coverage(mut self, base: &str) -> Self {
192 self.enable_diff_coverage = true;
193 self.diffbase = Some(base.to_string());
194 self
195 }
196
197 pub fn with_exclude_patterns(mut self, patterns: Vec<&str>) -> Self {
199 self.exclude_patterns = patterns.into_iter().map(|s| s.to_string()).collect();
200 self
201 }
202}
203
204#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
206pub enum CoverageType {
207 Line,
209 Branch,
211 Function,
213 Statement,
215 Integration,
217 Path,
219 Condition,
221}
222
223#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
225pub enum ReportFormat {
226 Html,
228 Json,
230 Xml,
232 Lcov,
234 Text,
236 Csv,
238}
239
240#[derive(Debug, Clone, Serialize, Deserialize)]
242pub struct FileCoverage {
243 pub file_path: PathBuf,
245 pub total_lines: u32,
247 pub covered_lines: u32,
249 pub line_hits: BTreeMap<u32, u32>,
251 pub branches: Vec<BranchCoverage>,
253 pub functions: Vec<FunctionCoverage>,
255 pub integrations: Vec<IntegrationPoint>,
257 pub modified_time: SystemTime,
259 pub collected_at: SystemTime,
261}
262
263impl FileCoverage {
264 pub fn line_coverage_percentage(&self) -> f64 {
266 if self.total_lines == 0 {
267 100.0
268 } else {
269 (self.covered_lines as f64 / self.total_lines as f64) * 100.0
270 }
271 }
272
273 pub fn branch_coverage_percentage(&self) -> f64 {
275 if self.branches.is_empty() {
276 100.0
277 } else {
278 let covered_branches = self.branches.iter().filter(|b| b.is_covered()).count();
279 (covered_branches as f64 / self.branches.len() as f64) * 100.0
280 }
281 }
282
283 pub fn function_coverage_percentage(&self) -> f64 {
285 if self.functions.is_empty() {
286 100.0
287 } else {
288 let covered_functions = self
289 .functions
290 .iter()
291 .filter(|f| f.execution_count > 0)
292 .count();
293 (covered_functions as f64 / self.functions.len() as f64) * 100.0
294 }
295 }
296
297 pub fn uncovered_lines(&self) -> Vec<u32> {
299 (1..=self.total_lines)
300 .filter(|line| !self.line_hits.contains_key(line))
301 .collect()
302 }
303
304 pub fn hot_spots(&self, threshold: u32) -> Vec<(u32, u32)> {
306 self.line_hits
307 .iter()
308 .filter(|(_, &count)| count >= threshold)
309 .map(|(&line, &count)| (line, count))
310 .collect()
311 }
312}
313
314#[derive(Debug, Clone, Serialize, Deserialize)]
316pub struct BranchCoverage {
317 pub line_number: u32,
319 pub branch_id: String,
321 pub true_count: u32,
323 pub false_count: u32,
325 pub branch_type: BranchType,
327 pub source_snippet: String,
329}
330
331impl BranchCoverage {
332 pub fn is_covered(&self) -> bool {
334 self.true_count > 0 && self.false_count > 0
335 }
336
337 pub fn total_executions(&self) -> u32 {
339 self.true_count + self.false_count
340 }
341
342 pub fn balance_score(&self) -> f64 {
344 if !self.is_covered() {
345 0.0
346 } else {
347 let total = self.total_executions() as f64;
348 let min_count = self.true_count.min(self.false_count) as f64;
349 min_count / total * 2.0 }
351 }
352}
353
354#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
356pub enum BranchType {
357 IfElse,
359 Match,
361 While,
363 For,
365 Ternary,
367 Logical,
369 Other,
371}
372
373#[derive(Debug, Clone, Serialize, Deserialize)]
375pub struct FunctionCoverage {
376 pub function_name: String,
378 pub start_line: u32,
380 pub end_line: u32,
382 pub execution_count: u32,
384 pub complexity: u32,
386 pub parameter_count: u32,
388 pub return_complexity: u32,
390}
391
392impl FunctionCoverage {
393 pub fn coverage_score(&self) -> f64 {
395 if self.execution_count == 0 {
396 0.0
397 } else {
398 let execution_factor = (self.execution_count as f64).ln().min(5.0) / 5.0;
400 let complexity_factor = 1.0 / (1.0 + self.complexity as f64 / 10.0);
401 execution_factor * complexity_factor
402 }
403 }
404}
405
406#[derive(Debug, Clone, Serialize, Deserialize)]
408pub struct IntegrationPoint {
409 pub id: String,
411 pub source_module: String,
413 pub target_module: String,
415 pub integration_type: IntegrationType,
417 pub execution_count: u32,
419 pub line_number: u32,
421 pub success_rate: f64,
423}
424
425#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
427pub enum IntegrationType {
428 FunctionCall,
430 MethodCall,
432 TraitImpl,
434 ModuleImport,
436 DatabaseCall,
438 NetworkCall,
440 FileSystemOp,
442 IpcCall,
444}
445
446#[derive(Debug, Clone, Serialize, Deserialize)]
448pub struct CoverageReport {
449 pub generated_at: SystemTime,
451 pub config: CoverageConfig,
453 pub overall_stats: CoverageStatistics,
455 pub file_coverage: HashMap<PathBuf, FileCoverage>,
457 pub trends: Option<CoverageTrends>,
459 pub quality_gates: QualityGateResults,
461 pub performance_impact: PerformanceImpact,
463 pub recommendations: Vec<CoverageRecommendation>,
465}
466
467impl CoverageReport {
468 pub fn overall_coverage_percentage(&self) -> f64 {
470 self.overall_stats.line_coverage_percentage
471 }
472
473 pub fn meets_quality_gates(&self) -> bool {
475 self.quality_gates.overall_passed
476 }
477
478 pub fn files_below_threshold(&self) -> Vec<(&Path, f64)> {
480 self.file_coverage
481 .iter()
482 .filter_map(|(path, coverage)| {
483 let percentage = coverage.line_coverage_percentage();
484 if percentage < self.config.coverage_threshold {
485 Some((path.as_path(), percentage))
486 } else {
487 None
488 }
489 })
490 .collect()
491 }
492
493 pub fn critical_uncovered_functions(&self) -> Vec<(&FunctionCoverage, &Path)> {
495 let mut uncovered: Vec<_> = self
496 .file_coverage
497 .iter()
498 .flat_map(|(path, file_cov)| {
499 file_cov
500 .functions
501 .iter()
502 .filter(|f| f.execution_count == 0)
503 .map(|f| (f, path.as_path()))
504 })
505 .collect();
506
507 uncovered.sort_by(|a, b| b.0.complexity.cmp(&a.0.complexity));
508 uncovered.into_iter().take(10).collect()
509 }
510}
511
512#[derive(Debug, Clone, Serialize, Deserialize)]
514pub struct CoverageStatistics {
515 pub total_lines: u32,
517 pub covered_lines: u32,
519 pub line_coverage_percentage: f64,
521 pub total_branches: u32,
523 pub covered_branches: u32,
525 pub branch_coverage_percentage: f64,
527 pub total_functions: u32,
529 pub covered_functions: u32,
531 pub function_coverage_percentage: f64,
533 pub total_integrations: u32,
535 pub covered_integrations: u32,
537 pub integration_coverage_percentage: f64,
539 pub files_analyzed: u32,
541}
542
543#[derive(Debug, Clone, Serialize, Deserialize)]
545pub struct CoverageTrends {
546 pub history: Vec<CoverageDataPoint>,
548 pub trend_direction: TrendDirection,
550 pub change_rate: f64,
552 pub predicted_coverage: Option<f64>,
554}
555
556#[derive(Debug, Clone, Serialize, Deserialize)]
558pub struct CoverageDataPoint {
559 pub timestamp: SystemTime,
561 pub coverage_percentage: f64,
563 pub branch_coverage_percentage: f64,
565 pub version: Option<String>,
567 pub test_count: u32,
569}
570
571#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
573pub enum TrendDirection {
574 Improving,
576 Stable,
578 Declining,
580 Unknown,
582}
583
584#[derive(Debug, Clone, Serialize, Deserialize)]
586pub struct QualityGateResults {
587 pub overall_passed: bool,
589 pub line_coverage_passed: bool,
591 pub branch_coverage_passed: bool,
593 pub integration_coverage_passed: bool,
595 pub failures: Vec<QualityGateFailure>,
597}
598
599#[derive(Debug, Clone, Serialize, Deserialize)]
601pub struct QualityGateFailure {
602 pub gate_type: String,
604 pub threshold: f64,
606 pub actual_value: f64,
608 pub severity: FailureSeverity,
610 pub suggestions: Vec<String>,
612}
613
614#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
616pub enum FailureSeverity {
617 Minor,
619 Moderate,
621 Major,
623 Critical,
625}
626
627#[derive(Debug, Clone, Serialize, Deserialize)]
629pub struct PerformanceImpact {
630 pub execution_overhead_percent: f64,
632 pub memory_overhead_bytes: u64,
634 pub collection_duration: Duration,
636 pub instrumentation_points: u32,
638 pub sampling_effectiveness: f64,
640}
641
642#[derive(Debug, Clone, Serialize, Deserialize)]
644pub struct CoverageRecommendation {
645 pub recommendation_type: RecommendationType,
647 pub priority: RecommendationPriority,
649 pub description: String,
651 pub expected_impact: f64,
653 pub effort_estimate: f64,
655 pub affected_items: Vec<String>,
657}
658
659#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
661pub enum RecommendationType {
662 AddUnitTests,
664 AddIntegrationTests,
666 TestEdgeCases,
668 ImproveBranchCoverage,
670 AddPropertyTests,
672 TestComplexFunctions,
674 RemoveDeadCode,
676}
677
678#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
680pub enum RecommendationPriority {
681 Low,
683 Medium,
685 High,
687 Critical,
689}
690
691pub struct CoverageAnalyzer {
693 config: CoverageConfig,
695 collection_state: Arc<Mutex<CollectionState>>,
697 file_coverage: Arc<RwLock<HashMap<PathBuf, FileCoverage>>>,
699 history: Arc<Mutex<Vec<CoverageDataPoint>>>,
701 collection_start: Option<Instant>,
703 performance_tracker: PerformanceTracker,
705}
706
707#[derive(Debug, Clone, Copy, PartialEq, Eq)]
709pub enum CollectionState {
710 Idle,
712 Collecting,
714 Paused,
716 Completed,
718 Error,
720}
721
722#[derive(Debug, Default)]
724struct PerformanceTracker {
725 baseline_memory: u64,
727 execution_timer: Option<Instant>,
729 instrumentation_count: u32,
731}
732
733impl CoverageAnalyzer {
734 pub fn new(config: CoverageConfig) -> CoreResult<Self> {
736 if !config.output_directory.exists() {
738 std::fs::create_dir_all(&config.output_directory).map_err(|e| {
739 CoreError::from(std::io::Error::other(format!(
740 "Failed to create output directory: {e}"
741 )))
742 })?;
743 }
744
745 Ok(Self {
746 config,
747 collection_state: Arc::new(Mutex::new(CollectionState::Idle)),
748 file_coverage: Arc::new(RwLock::new(HashMap::new())),
749 history: Arc::new(Mutex::new(Vec::new())),
750 collection_start: None,
751 performance_tracker: PerformanceTracker::default(),
752 })
753 }
754
755 pub fn start_collection(&mut self) -> CoreResult<()> {
757 if let Ok(mut state) = self.collection_state.lock() {
758 match *state {
759 CollectionState::Collecting => {
760 return Err(CoreError::from(std::io::Error::other(
761 "Coverage collection already in progress",
762 )));
763 }
764 _ => *state = CollectionState::Collecting,
765 }
766 }
767
768 self.collection_start = Some(Instant::now());
769 self.performance_tracker.execution_timer = Some(Instant::now());
770 self.performance_tracker.baseline_memory = self.get_current_memory_usage();
771
772 self.initialize_instrumentation()?;
774
775 Ok(())
776 }
777
778 pub fn stop_and_generate_report(&mut self) -> CoreResult<CoverageReport> {
780 if let Ok(mut state) = self.collection_state.lock() {
782 *state = CollectionState::Completed;
783 }
784
785 let performance_impact = self.calculate_performance_impact();
787
788 let overall_stats = self.calculate_overall_statistics()?;
790
791 let quality_gates = self.evaluate_quality_gates(&overall_stats);
793
794 let recommendations = self.generate_recommendations(&overall_stats)?;
796
797 let trends = if self.config.enable_history {
799 self.calculate_trends()?
800 } else {
801 None
802 };
803
804 let report = CoverageReport {
806 generated_at: SystemTime::now(),
807 config: self.config.clone(),
808 overall_stats,
809 file_coverage: self.file_coverage.read().expect("Operation failed").clone(),
810 trends,
811 quality_gates,
812 performance_impact,
813 recommendations,
814 };
815
816 if self.config.enable_history {
818 self.save_historical_data_point(&report)?;
819 }
820
821 self.generate_output_reports(&report)?;
823
824 Ok(report)
825 }
826
827 pub fn record_line_execution(&self, file_path: &Path, linenumber: u32) -> CoreResult<()> {
829 if let Ok(mut coverage) = self.file_coverage.write() {
830 let file_coverage =
831 coverage
832 .entry(file_path.to_path_buf())
833 .or_insert_with(|| FileCoverage {
834 file_path: file_path.to_path_buf(),
835 total_lines: 0,
836 covered_lines: 0,
837 line_hits: BTreeMap::new(),
838 branches: Vec::new(),
839 functions: Vec::new(),
840 integrations: Vec::new(),
841 modified_time: SystemTime::now(),
842 collected_at: SystemTime::now(),
843 });
844
845 *file_coverage.line_hits.entry(linenumber).or_insert(0) += 1;
846 }
847
848 Ok(())
849 }
850
851 pub fn record_branch_execution(
853 &self,
854 file_path: &Path,
855 line_number: u32,
856 branch_id: &str,
857 taken: bool,
858 ) -> CoreResult<()> {
859 if let Ok(mut coverage) = self.file_coverage.write() {
860 let file_coverage =
861 coverage
862 .entry(file_path.to_path_buf())
863 .or_insert_with(|| FileCoverage {
864 file_path: file_path.to_path_buf(),
865 total_lines: 0,
866 covered_lines: 0,
867 line_hits: BTreeMap::new(),
868 branches: Vec::new(),
869 functions: Vec::new(),
870 integrations: Vec::new(),
871 modified_time: SystemTime::now(),
872 collected_at: SystemTime::now(),
873 });
874
875 if let Some(branch) = file_coverage
877 .branches
878 .iter_mut()
879 .find(|b| b.branch_id == branch_id)
880 {
881 if taken {
882 branch.true_count += 1;
883 } else {
884 branch.false_count += 1;
885 }
886 } else {
887 let mut branch = BranchCoverage {
888 line_number,
889 branch_id: branch_id.to_string(),
890 true_count: 0,
891 false_count: 0,
892 branch_type: BranchType::Other,
893 source_snippet: String::new(),
894 };
895
896 if taken {
897 branch.true_count = 1;
898 } else {
899 branch.false_count = 1;
900 }
901
902 file_coverage.branches.push(branch);
903 }
904 }
905
906 Ok(())
907 }
908
909 pub fn record_function_execution(
911 &self,
912 file_path: &Path,
913 function_name: &str,
914 start_line: u32,
915 end_line: u32,
916 ) -> CoreResult<()> {
917 if let Ok(mut coverage) = self.file_coverage.write() {
918 let file_coverage =
919 coverage
920 .entry(file_path.to_path_buf())
921 .or_insert_with(|| FileCoverage {
922 file_path: file_path.to_path_buf(),
923 total_lines: 0,
924 covered_lines: 0,
925 line_hits: BTreeMap::new(),
926 branches: Vec::new(),
927 functions: Vec::new(),
928 integrations: Vec::new(),
929 modified_time: SystemTime::now(),
930 collected_at: SystemTime::now(),
931 });
932
933 if let Some(function) = file_coverage
935 .functions
936 .iter_mut()
937 .find(|f| f.function_name == function_name)
938 {
939 function.execution_count += 1;
940 } else {
941 let function = FunctionCoverage {
942 function_name: function_name.to_string(),
943 start_line,
944 end_line,
945 execution_count: 1,
946 complexity: self.calculate_function_complexity(start_line, end_line),
947 parameter_count: 0, return_complexity: 1,
949 };
950
951 file_coverage.functions.push(function);
952 }
953 }
954
955 Ok(())
956 }
957
958 fn initialize_instrumentation(&mut self) -> CoreResult<()> {
960 self.performance_tracker.instrumentation_count = 1000; Ok(())
964 }
965
966 fn calculate_overall_statistics(&self) -> CoreResult<CoverageStatistics> {
968 let coverage = self.file_coverage.read().expect("Operation failed");
969
970 let mut stats = CoverageStatistics {
971 total_lines: 0,
972 covered_lines: 0,
973 line_coverage_percentage: 0.0,
974 total_branches: 0,
975 covered_branches: 0,
976 branch_coverage_percentage: 0.0,
977 total_functions: 0,
978 covered_functions: 0,
979 function_coverage_percentage: 0.0,
980 total_integrations: 0,
981 covered_integrations: 0,
982 integration_coverage_percentage: 0.0,
983 files_analyzed: coverage.len() as u32,
984 };
985
986 for file_cov in coverage.values() {
987 stats.total_lines += file_cov.total_lines;
988 stats.covered_lines += file_cov.covered_lines;
989
990 stats.total_branches += file_cov.branches.len() as u32;
991 stats.covered_branches +=
992 file_cov.branches.iter().filter(|b| b.is_covered()).count() as u32;
993
994 stats.total_functions += file_cov.functions.len() as u32;
995 stats.covered_functions += file_cov
996 .functions
997 .iter()
998 .filter(|f| f.execution_count > 0)
999 .count() as u32;
1000
1001 stats.total_integrations += file_cov.integrations.len() as u32;
1002 stats.covered_integrations += file_cov
1003 .integrations
1004 .iter()
1005 .filter(|i| i.execution_count > 0)
1006 .count() as u32;
1007 }
1008
1009 stats.line_coverage_percentage = if stats.total_lines > 0 {
1011 (stats.covered_lines as f64 / stats.total_lines as f64) * 100.0
1012 } else {
1013 100.0
1014 };
1015
1016 stats.branch_coverage_percentage = if stats.total_branches > 0 {
1017 (stats.covered_branches as f64 / stats.total_branches as f64) * 100.0
1018 } else {
1019 100.0
1020 };
1021
1022 stats.function_coverage_percentage = if stats.total_functions > 0 {
1023 (stats.covered_functions as f64 / stats.total_functions as f64) * 100.0
1024 } else {
1025 100.0
1026 };
1027
1028 stats.integration_coverage_percentage = if stats.total_integrations > 0 {
1029 (stats.covered_integrations as f64 / stats.total_integrations as f64) * 100.0
1030 } else {
1031 100.0
1032 };
1033
1034 Ok(stats)
1035 }
1036
1037 fn evaluate_quality_gates(&self, stats: &CoverageStatistics) -> QualityGateResults {
1039 let mut results = QualityGateResults {
1040 overall_passed: true,
1041 line_coverage_passed: true,
1042 branch_coverage_passed: true,
1043 integration_coverage_passed: true,
1044 failures: Vec::new(),
1045 };
1046
1047 if stats.line_coverage_percentage < self.config.coverage_threshold {
1049 results.line_coverage_passed = false;
1050 results.overall_passed = false;
1051
1052 let severity = if stats.line_coverage_percentage < self.config.coverage_threshold - 20.0
1053 {
1054 FailureSeverity::Critical
1055 } else if stats.line_coverage_percentage < self.config.coverage_threshold - 10.0 {
1056 FailureSeverity::Major
1057 } else if stats.line_coverage_percentage < self.config.coverage_threshold - 5.0 {
1058 FailureSeverity::Moderate
1059 } else {
1060 FailureSeverity::Minor
1061 };
1062
1063 results.failures.push(QualityGateFailure {
1064 gate_type: "Line Coverage".to_string(),
1065 threshold: self.config.coverage_threshold,
1066 actual_value: stats.line_coverage_percentage,
1067 severity,
1068 suggestions: vec![
1069 "Add unit tests for uncovered lines".to_string(),
1070 "Focus on complex functions with low coverage".to_string(),
1071 "Consider removing dead code".to_string(),
1072 ],
1073 });
1074 }
1075
1076 if stats.branch_coverage_percentage < self.config.branch_threshold {
1078 results.branch_coverage_passed = false;
1079 results.overall_passed = false;
1080
1081 results.failures.push(QualityGateFailure {
1082 gate_type: "Branch Coverage".to_string(),
1083 threshold: self.config.branch_threshold,
1084 actual_value: stats.branch_coverage_percentage,
1085 severity: FailureSeverity::Moderate,
1086 suggestions: vec![
1087 "Add tests for both true and false branches".to_string(),
1088 "Test edge cases and error conditions".to_string(),
1089 "Use property-based testing for complex conditions".to_string(),
1090 ],
1091 });
1092 }
1093
1094 if stats.integration_coverage_percentage < self.config.integration_threshold {
1096 results.integration_coverage_passed = false;
1097 results.overall_passed = false;
1098
1099 results.failures.push(QualityGateFailure {
1100 gate_type: "Integration Coverage".to_string(),
1101 threshold: self.config.integration_threshold,
1102 actual_value: stats.integration_coverage_percentage,
1103 severity: FailureSeverity::Moderate,
1104 suggestions: vec![
1105 "Add integration tests between modules".to_string(),
1106 "Test external dependencies and APIs".to_string(),
1107 "Include database and network interactions".to_string(),
1108 ],
1109 });
1110 }
1111
1112 results
1113 }
1114
1115 fn generate_recommendations(
1117 &self,
1118 stats: &CoverageStatistics,
1119 ) -> CoreResult<Vec<CoverageRecommendation>> {
1120 let mut recommendations = Vec::new();
1121 let coverage = self.file_coverage.read().expect("Operation failed");
1122
1123 for (path, file_cov) in coverage.iter() {
1125 let coverage_pct = file_cov.line_coverage_percentage();
1126 if coverage_pct < self.config.coverage_threshold {
1127 recommendations.push(CoverageRecommendation {
1128 recommendation_type: RecommendationType::AddUnitTests,
1129 priority: if coverage_pct < 50.0 {
1130 RecommendationPriority::High
1131 } else {
1132 RecommendationPriority::Medium
1133 },
1134 description: format!(
1135 "Add unit tests for {} (current coverage: {:.1}%)",
1136 path.display(),
1137 coverage_pct
1138 ),
1139 expected_impact: self.config.coverage_threshold - coverage_pct,
1140 effort_estimate: (file_cov.uncovered_lines().len() as f64) * 0.25, affected_items: vec![path.to_string_lossy().to_string()],
1142 });
1143 }
1144 }
1145
1146 if stats.branch_coverage_percentage < self.config.branch_threshold {
1148 recommendations.push(CoverageRecommendation {
1149 recommendation_type: RecommendationType::ImproveBranchCoverage,
1150 priority: RecommendationPriority::High,
1151 description: "Improve branch coverage by testing all conditional paths".to_string(),
1152 expected_impact: self.config.branch_threshold - stats.branch_coverage_percentage,
1153 effort_estimate: 8.0, affected_items: vec!["Multiple files with uncovered branches".to_string()],
1155 });
1156 }
1157
1158 let critical_functions = self.find_complex_uncovered_functions();
1160 if !critical_functions.is_empty() {
1161 recommendations.push(CoverageRecommendation {
1162 recommendation_type: RecommendationType::TestComplexFunctions,
1163 priority: RecommendationPriority::Critical,
1164 description: "Add tests for complex functions with high cyclomatic complexity"
1165 .to_string(),
1166 expected_impact: 15.0, effort_estimate: critical_functions.len() as f64 * 2.0, affected_items: critical_functions
1169 .into_iter()
1170 .map(|f| f.function_name.clone())
1171 .collect(),
1172 });
1173 }
1174
1175 recommendations.sort_by(|a, b| b.priority.cmp(&a.priority));
1177
1178 Ok(recommendations)
1179 }
1180
1181 fn calculate_trends(&self) -> CoreResult<Option<CoverageTrends>> {
1183 let history = self.history.lock().expect("Operation failed");
1184
1185 if history.len() < 2 {
1186 return Ok(None);
1187 }
1188
1189 let recent_points: Vec<_> = history.iter().rev().take(5).collect();
1191 let trend_direction = if recent_points.len() >= 2 {
1192 let first = recent_points
1193 .last()
1194 .expect("Operation failed")
1195 .coverage_percentage;
1196 let last = recent_points
1197 .first()
1198 .expect("Operation failed")
1199 .coverage_percentage;
1200 let change = last - first;
1201
1202 if change > 1.0 {
1203 TrendDirection::Improving
1204 } else if change < -1.0 {
1205 TrendDirection::Declining
1206 } else {
1207 TrendDirection::Stable
1208 }
1209 } else {
1210 TrendDirection::Unknown
1211 };
1212
1213 let change_rate = if recent_points.len() >= 2 {
1215 let first = recent_points.last().expect("Operation failed");
1216 let last = recent_points.first().expect("Operation failed");
1217
1218 let time_diff = last
1219 .timestamp
1220 .duration_since(first.timestamp)
1221 .unwrap_or(Duration::from_secs(1))
1222 .as_secs_f64()
1223 / (24.0 * 60.0 * 60.0); let coverage_diff = last.coverage_percentage - first.coverage_percentage;
1226
1227 if time_diff > 0.0 {
1228 coverage_diff / time_diff
1229 } else {
1230 0.0
1231 }
1232 } else {
1233 0.0
1234 };
1235
1236 let predicted_coverage = if change_rate.abs() > 0.1 {
1238 let last_coverage = recent_points
1239 .first()
1240 .expect("Operation failed")
1241 .coverage_percentage;
1242 Some((last_coverage + change_rate * 7.0).clamp(0.0, 100.0)) } else {
1244 None
1245 };
1246
1247 Ok(Some(CoverageTrends {
1248 history: history.clone(),
1249 trend_direction,
1250 change_rate,
1251 predicted_coverage,
1252 }))
1253 }
1254
1255 fn save_historical_data_point(&self, report: &CoverageReport) -> CoreResult<()> {
1257 if let Ok(mut history) = self.history.lock() {
1258 let data_point = CoverageDataPoint {
1259 timestamp: SystemTime::now(),
1260 coverage_percentage: report.overall_stats.line_coverage_percentage,
1261 branch_coverage_percentage: report.overall_stats.branch_coverage_percentage,
1262 version: None, test_count: 0, };
1265
1266 history.push(data_point);
1267
1268 let cutoff = SystemTime::now() - self.config.history_retention;
1270 history.retain(|point| point.timestamp >= cutoff);
1271 }
1272
1273 Ok(())
1274 }
1275
1276 fn generate_output_reports(&self, report: &CoverageReport) -> CoreResult<()> {
1278 for format in &self.config.report_formats {
1279 match format {
1280 ReportFormat::Html => self.generate_html_report(report)?,
1281 ReportFormat::Json => self.generate_json_report(report)?,
1282 ReportFormat::Xml => self.generate_xml_report(report)?,
1283 ReportFormat::Lcov => self.generate_lcov_report(report)?,
1284 ReportFormat::Text => self.generatetext_report(report)?,
1285 ReportFormat::Csv => self.generate_csv_report(report)?,
1286 }
1287 }
1288
1289 Ok(())
1290 }
1291
1292 fn generate_html_report(&self, report: &CoverageReport) -> CoreResult<()> {
1294 let html_content = self.create_html_content(report);
1295 let output_path = self.config.output_directory.join("coverage_report.html");
1296
1297 std::fs::write(output_path, html_content).map_err(|e| {
1298 CoreError::from(std::io::Error::other(format!(
1299 "Failed to write HTML report: {e}"
1300 )))
1301 })?;
1302
1303 Ok(())
1304 }
1305
1306 fn generate_json_report(&self, report: &CoverageReport) -> CoreResult<()> {
1308 {
1309 let json_content = serde_json::to_string_pretty(report).map_err(|e| {
1310 CoreError::from(std::io::Error::other(format!(
1311 "Failed to serialize JSON report: {e}"
1312 )))
1313 })?;
1314
1315 let output_path = self.config.output_directory.join("coverage_report.json");
1316 std::fs::write(output_path, json_content).map_err(|e| {
1317 CoreError::from(std::io::Error::other(format!(
1318 "Failed to write JSON report: {e}"
1319 )))
1320 })?;
1321 }
1322
1323 #[cfg(not(feature = "serde"))]
1324 {
1325 let _ = report; return Err(CoreError::from(std::io::Error::other(
1327 "JSON report requires serde feature",
1328 )));
1329 }
1330
1331 Ok(())
1332 }
1333
1334 fn generate_xml_report(&self, report: &CoverageReport) -> CoreResult<()> {
1336 let xml_content = self.create_xml_content(report);
1337 let output_path = self.config.output_directory.join("coverage_report.xml");
1338
1339 std::fs::write(output_path, xml_content).map_err(|e| {
1340 CoreError::from(std::io::Error::other(format!(
1341 "Failed to write XML report: {e}"
1342 )))
1343 })?;
1344
1345 Ok(())
1346 }
1347
1348 fn generate_lcov_report(&self, report: &CoverageReport) -> CoreResult<()> {
1350 let lcov_content = self.create_lcov_content(report);
1351 let output_path = self.config.output_directory.join("coverage.lcov");
1352
1353 std::fs::write(output_path, lcov_content).map_err(|e| {
1354 CoreError::from(std::io::Error::other(format!(
1355 "Failed to write LCOV report: {e}"
1356 )))
1357 })?;
1358
1359 Ok(())
1360 }
1361
1362 fn generatetext_report(&self, report: &CoverageReport) -> CoreResult<()> {
1364 let text_content = self.createtext_content(report);
1365 let output_path = self.config.output_directory.join("coverage_summary.txt");
1366
1367 std::fs::write(output_path, text_content).map_err(|e| {
1368 CoreError::from(std::io::Error::other(format!(
1369 "Failed to write text report: {e}"
1370 )))
1371 })?;
1372
1373 Ok(())
1374 }
1375
1376 fn generate_csv_report(&self, report: &CoverageReport) -> CoreResult<()> {
1378 let csv_content = self.create_csv_content(report);
1379 let output_path = self.config.output_directory.join("coverage_data.csv");
1380
1381 std::fs::write(output_path, csv_content).map_err(|e| {
1382 CoreError::from(std::io::Error::other(format!(
1383 "Failed to write CSV report: {e}"
1384 )))
1385 })?;
1386
1387 Ok(())
1388 }
1389
1390 fn create_html_content(&self, report: &CoverageReport) -> String {
1392 format!(
1393 r#"<!DOCTYPE html>
1394<html>
1395<head>
1396 <title>Coverage Report</title>
1397 <style>
1398 body {{ font-family: Arial, sans-serif; margin: 20px; }}
1399 .header {{ background: #f0f0f0; padding: 20px; border-radius: 5px; }}
1400 .stats {{ display: flex; gap: 20px; margin: 20px 0; }}
1401 .stat-box {{ background: #e8f4f8; padding: 15px; border-radius: 5px; text-align: center; }}
1402 .coverage-bar {{ background: #ddd; height: 20px; border-radius: 10px; overflow: hidden; }}
1403 .coverage-fill {{ background: #4caf50; height: 100%; transition: width 0.3s; }}
1404 .low-coverage {{ background: #f44336; }}
1405 .medium-coverage {{ background: #ff9800; }}
1406 .high-coverage {{ background: #4caf50; }}
1407 table {{ width: 100%; border-collapse: collapse; margin: 20px 0; }}
1408 th, td {{ border: 1px solid #ddd; padding: 8px; text-align: left; }}
1409 th {{ background-color: #f2f2f2; }}
1410 .recommendations {{ background: #fff3cd; padding: 15px; border-radius: 5px; margin: 20px 0; }}
1411 </style>
1412</head>
1413<body>
1414 <div class= header>
1415 <h1>Coverage Report</h1>
1416 <p>Generated at: {}</p>
1417 <p>Overall Coverage: {:.2}%</p>
1418 </div>
1419
1420 <div class= stats>
1421 <div class="stat-box">
1422 <h3>Line Coverage</h3>
1423 <div class="coverage-bar">
1424 <div class="coverage-fill {}" style="width: {:.1}%"></div>
1425 </div>
1426 <p>{:.2}% ({}/{})</p>
1427 </div>
1428 <div class="stat-box">
1429 <h3>Branch Coverage</h3>
1430 <div class="coverage-bar">
1431 <div class="coverage-fill {}" style="width: {:.1}%"></div>
1432 </div>
1433 <p>{:.2}% ({}/{})</p>
1434 </div>
1435 <div class="stat-box">
1436 <h3>Function Coverage</h3>
1437 <div class="coverage-bar">
1438 <div class="coverage-fill {}" style="width: {:.1}%"></div>
1439 </div>
1440 <p>{:.2}% ({}/{})</p>
1441 </div>
1442 </div>
1443
1444 <h2>Quality Gates</h2>
1445 <p>Status: {}</p>
1446
1447 <h2>Recommendations</h2>
1448 <div class= recommendations>
1449 <ul>
1450 {}
1451 </ul>
1452 </div>
1453
1454 <h2>File Coverage Details</h2>
1455 <table>
1456 <tr>
1457 <th>File</th>
1458 <th>Line Coverage</th>
1459 <th>Branch Coverage</th>
1460 <th>Function Coverage</th>
1461 </tr>
1462 {}
1463 </table>
1464</body>
1465</html>"#,
1466 chrono::DateTime::<chrono::Utc>::from(report.generated_at)
1467 .format("%Y-%m-%d %H:%M:%S UTC"),
1468 report.overall_stats.line_coverage_percentage,
1469 self.get_coverage_class(report.overall_stats.line_coverage_percentage),
1471 report.overall_stats.line_coverage_percentage,
1472 report.overall_stats.line_coverage_percentage,
1473 report.overall_stats.covered_lines,
1474 report.overall_stats.total_lines,
1475 self.get_coverage_class(report.overall_stats.branch_coverage_percentage),
1477 report.overall_stats.branch_coverage_percentage,
1478 report.overall_stats.branch_coverage_percentage,
1479 report.overall_stats.covered_branches,
1480 report.overall_stats.total_branches,
1481 self.get_coverage_class(report.overall_stats.function_coverage_percentage),
1483 report.overall_stats.function_coverage_percentage,
1484 report.overall_stats.function_coverage_percentage,
1485 report.overall_stats.covered_functions,
1486 report.overall_stats.total_functions,
1487 if report.quality_gates.overall_passed {
1489 "✅ PASSED"
1490 } else {
1491 "❌ FAILED"
1492 },
1493 report
1495 .recommendations
1496 .iter()
1497 .take(5)
1498 .map(|r| format!("<li>{description}</li>", description = r.description))
1499 .collect::<Vec<_>>()
1500 .join("\n"),
1501 report
1503 .file_coverage
1504 .iter()
1505 .map(|(path, cov)| format!(
1506 "<tr><td>{}</td><td>{:.1}%</td><td>{:.1}%</td><td>{:.1}%</td></tr>",
1507 path.display(),
1508 cov.line_coverage_percentage(),
1509 cov.branch_coverage_percentage(),
1510 cov.function_coverage_percentage()
1511 ))
1512 .collect::<Vec<_>>()
1513 .join("\n")
1514 )
1515 }
1516
1517 fn create_xml_content(&self, report: &CoverageReport) -> String {
1519 format!(
1520 r#"<?xml _version="1.0" encoding="UTF-8"?>
1521<coverage _version="1.0" timestamp="{}">
1522 <project name="scirs2-core">
1523 <metrics>
1524 <lines-covered>{}</lines-covered>
1525 <lines-valid>{}</lines-valid>
1526 <line-coverage>{:.6}</line-coverage>
1527 <branches-covered>{}</branches-covered>
1528 <branches-valid>{}</branches-valid>
1529 <branch-coverage>{:.6}</branch-coverage>
1530 </metrics>
1531 <packages>
1532 {}
1533 </packages>
1534 </project>
1535</coverage>"#,
1536 chrono::DateTime::<chrono::Utc>::from(report.generated_at).timestamp(),
1537 report.overall_stats.covered_lines,
1538 report.overall_stats.total_lines,
1539 report.overall_stats.line_coverage_percentage / 100.0,
1540 report.overall_stats.covered_branches,
1541 report.overall_stats.total_branches,
1542 report.overall_stats.branch_coverage_percentage / 100.0,
1543 report
1544 .file_coverage
1545 .iter()
1546 .map(|(path, cov)| format!(
1547 r#"<package name="{}">
1548 <classes>
1549 <class name="{}" filename="{}">
1550 <metrics line-coverage="{:.6}" branch-coverage="{:.6}" />
1551 <lines>
1552 {}
1553 </lines>
1554 </class>
1555 </classes>
1556 </package>"#,
1557 path.parent().unwrap_or(Path::new("")).display(),
1558 path.file_stem().unwrap_or_default().to_string_lossy(),
1559 path.display(),
1560 cov.line_coverage_percentage() / 100.0,
1561 cov.branch_coverage_percentage() / 100.0,
1562 cov.line_hits
1563 .iter()
1564 .map(|(&line, &hits)| format!(r#"<line number="{line}" hits="{hits}" />"#))
1565 .collect::<Vec<_>>()
1566 .join("\n ")
1567 ))
1568 .collect::<Vec<_>>()
1569 .join("\n ")
1570 )
1571 }
1572
1573 fn create_lcov_content(&self, report: &CoverageReport) -> String {
1575 let mut lcov_content = String::new();
1576
1577 for (path, cov) in &report.file_coverage {
1578 lcov_content.push_str(&format!("SF:{path}\n", path = path.display()));
1579
1580 for func in &cov.functions {
1582 lcov_content.push_str(&format!(
1583 "FN:{start_line},{function_name}\n",
1584 start_line = func.start_line,
1585 function_name = func.function_name
1586 ));
1587 }
1588
1589 for func in &cov.functions {
1590 lcov_content.push_str(&format!(
1591 "FNDA:{},{}\n",
1592 func.execution_count, func.function_name
1593 ));
1594 }
1595
1596 lcov_content.push_str(&format!("FNF:{count}\n", count = cov.functions.len()));
1597 lcov_content.push_str(&format!(
1598 "FNH:{}\n",
1599 cov.functions
1600 .iter()
1601 .filter(|f| f.execution_count > 0)
1602 .count()
1603 ));
1604
1605 for branch in &cov.branches {
1607 lcov_content.push_str(&format!(
1608 "BA:{},0,{}\n",
1609 branch.line_number, branch.true_count
1610 ));
1611 lcov_content.push_str(&format!(
1612 "BA:{},1,{}\n",
1613 branch.line_number, branch.false_count
1614 ));
1615 }
1616
1617 lcov_content.push_str(&format!("BRF:{}\n", cov.branches.len() * 2));
1618 lcov_content.push_str(&format!(
1619 "BRH:{}\n",
1620 cov.branches
1621 .iter()
1622 .map(|b| if b.is_covered() {
1623 2
1624 } else if b.true_count > 0 || b.false_count > 0 {
1625 1
1626 } else {
1627 0
1628 })
1629 .sum::<u32>()
1630 ));
1631
1632 for (&line, &hits) in &cov.line_hits {
1634 lcov_content.push_str(&format!("DA:{line},{hits}\n"));
1635 }
1636
1637 lcov_content.push_str(&format!("LF:{}\n", cov.total_lines));
1638 lcov_content.push_str(&format!("LH:{}\n", cov.covered_lines));
1639 lcov_content.push_str("end_of_record\n");
1640 }
1641
1642 lcov_content
1643 }
1644
1645 fn createtext_content(&self, report: &CoverageReport) -> String {
1647 let mut content = String::new();
1648
1649 content.push_str("===== COVERAGE REPORT =====\n\n");
1650 content.push_str(&format!(
1651 "Generated: {}\n",
1652 chrono::DateTime::<chrono::Utc>::from(report.generated_at)
1653 .format("%Y-%m-%d %H:%M:%S UTC")
1654 ));
1655 content.push_str(&format!(
1656 "Files Analyzed: {}\n\n",
1657 report.overall_stats.files_analyzed
1658 ));
1659
1660 content.push_str("OVERALL STATISTICS:\n");
1661 content.push_str(&format!(
1662 " Line Coverage: {:.2}% ({}/{})\n",
1663 report.overall_stats.line_coverage_percentage,
1664 report.overall_stats.covered_lines,
1665 report.overall_stats.total_lines
1666 ));
1667 content.push_str(&format!(
1668 " Branch Coverage: {:.2}% ({}/{})\n",
1669 report.overall_stats.branch_coverage_percentage,
1670 report.overall_stats.covered_branches,
1671 report.overall_stats.total_branches
1672 ));
1673 content.push_str(&format!(
1674 " Function Coverage: {:.2}% ({}/{})\n",
1675 report.overall_stats.function_coverage_percentage,
1676 report.overall_stats.covered_functions,
1677 report.overall_stats.total_functions
1678 ));
1679
1680 content.push_str("\nQUALITY GATES:\n");
1681 content.push_str(&format!(
1682 " Overall Status: {}\n",
1683 if report.quality_gates.overall_passed {
1684 "✅ PASSED"
1685 } else {
1686 "❌ FAILED"
1687 }
1688 ));
1689
1690 for failure in &report.quality_gates.failures {
1691 content.push_str(&format!(
1692 " ❌ {}: {:.2}% (threshold: {:.2}%)\n",
1693 failure.gate_type, failure.actual_value, failure.threshold
1694 ));
1695 }
1696
1697 if !report.recommendations.is_empty() {
1698 content.push_str("\nRECOMMENDATIONS:\n");
1699 for (i, rec) in report.recommendations.iter().take(5).enumerate() {
1700 content.push_str(&format!(
1701 " {}. [{}] {}\n",
1702 i + 1,
1703 format!("{0:?}", rec.priority).to_uppercase(),
1704 rec.description
1705 ));
1706 }
1707 }
1708
1709 content.push_str("\nFILE DETAILS:\n");
1710 let mut files: Vec<_> = report.file_coverage.iter().collect();
1711 files.sort_by(|a, b| {
1712 a.1.line_coverage_percentage()
1713 .partial_cmp(&b.1.line_coverage_percentage())
1714 .unwrap_or(std::cmp::Ordering::Equal)
1715 });
1716
1717 for (path, cov) in files.iter().take(20) {
1718 content.push_str(&format!(
1719 " {:<50} {:>8.1}% {:>8.1}% {:>8.1}%\n",
1720 path.display()
1721 .to_string()
1722 .chars()
1723 .take(50)
1724 .collect::<String>(),
1725 cov.line_coverage_percentage(),
1726 cov.branch_coverage_percentage(),
1727 cov.function_coverage_percentage()
1728 ));
1729 }
1730
1731 content
1732 }
1733
1734 fn create_csv_content(&self, report: &CoverageReport) -> String {
1736 let mut csv_content = String::new();
1737
1738 csv_content.push_str("File,Line Coverage %,Branch Coverage %,Function Coverage %,Total Lines,Covered Lines,Total Branches,Covered Branches,Total Functions,Covered Functions\n");
1739
1740 for (path, cov) in &report.file_coverage {
1741 csv_content.push_str(&format!(
1742 "{},{:.2},{:.2},{:.2},{},{},{},{},{},{}\n",
1743 path.display(),
1744 cov.line_coverage_percentage(),
1745 cov.branch_coverage_percentage(),
1746 cov.function_coverage_percentage(),
1747 cov.total_lines,
1748 cov.covered_lines,
1749 cov.branches.len(),
1750 cov.branches.iter().filter(|b| b.is_covered()).count(),
1751 cov.functions.len(),
1752 cov.functions
1753 .iter()
1754 .filter(|f| f.execution_count > 0)
1755 .count()
1756 ));
1757 }
1758
1759 csv_content
1760 }
1761
1762 fn get_coverage_class(&self, percentage: f64) -> &'static str {
1764 if percentage >= 80.0 {
1765 "high-coverage"
1766 } else if percentage >= 50.0 {
1767 "medium-coverage"
1768 } else {
1769 "low-coverage"
1770 }
1771 }
1772
1773 fn calculate_complexity(start_line: u32, endline: u32) -> u32 {
1775 let line_count = endline.saturating_sub(start_line) + 1;
1778 (line_count / 10).max(1) }
1780
1781 fn calculate_function_complexity(&self, start_line: u32, endline: u32) -> u32 {
1783 Self::calculate_complexity(start_line, endline)
1784 }
1785
1786 fn find_complex_uncovered_functions(&self) -> Vec<FunctionCoverage> {
1788 let coverage = self.file_coverage.read().expect("Operation failed");
1789
1790 let mut complex_functions: Vec<_> = coverage
1791 .values()
1792 .flat_map(|file_cov| {
1793 file_cov
1794 .functions
1795 .iter()
1796 .filter(|f| f.execution_count == 0 && f.complexity > 5)
1797 .cloned()
1798 })
1799 .collect();
1800
1801 complex_functions.sort_by(|a, b| b.complexity.cmp(&a.complexity));
1802 complex_functions.into_iter().take(10).collect()
1803 }
1804
1805 fn calculate_performance_impact(&self) -> PerformanceImpact {
1807 let execution_time = self
1808 .performance_tracker
1809 .execution_timer
1810 .map(|start| start.elapsed())
1811 .unwrap_or(Duration::from_secs(0));
1812
1813 let memory_overhead =
1814 self.get_current_memory_usage() - self.performance_tracker.baseline_memory;
1815
1816 PerformanceImpact {
1817 execution_overhead_percent: 5.0, memory_overhead_bytes: memory_overhead,
1819 collection_duration: execution_time,
1820 instrumentation_points: self.performance_tracker.instrumentation_count,
1821 sampling_effectiveness: self.config.samplingrate,
1822 }
1823 }
1824
1825 fn get_current_memory_usage(&self) -> u64 {
1827 1024 * 1024 * 10 }
1830}
1831
1832#[cfg(test)]
1833#[path = "coverage_tests.rs"]
1834mod tests;