code complexity & repetition analysis tool
at main 119 lines 3.7 kB view raw
1use serde::{Deserialize, Serialize}; 2 3/// Coverage report for the entire codebase 4#[derive(Debug, Clone, Serialize, Deserialize)] 5pub struct CoverageReport { 6 pub files: Vec<FileCoverage>, 7 pub totals: CoverageSummary, 8} 9 10impl CoverageReport { 11 pub fn new(files: Vec<FileCoverage>) -> Self { 12 let totals = CoverageSummary::from_files(&files); 13 Self { files, totals } 14 } 15} 16 17/// Coverage data for a single file 18#[derive(Debug, Clone, Serialize, Deserialize)] 19pub struct FileCoverage { 20 pub path: String, 21 pub lines: std::collections::BTreeMap<u32, u64>, 22 pub miss_ranges: Vec<(u32, u32)>, 23 pub summary: CoverageSummary, 24} 25 26impl FileCoverage { 27 pub fn new(path: String, lines: std::collections::BTreeMap<u32, u64>) -> Self { 28 let summary = CoverageSummary::from_lines(&lines); 29 let miss_ranges = super::misses::compute_miss_ranges(&lines); 30 Self { path, lines, miss_ranges, summary } 31 } 32} 33 34/// Coverage summary statistics 35#[derive(Debug, Clone, Serialize, Deserialize)] 36pub struct CoverageSummary { 37 pub total: usize, 38 pub hit: usize, 39 pub miss: usize, 40 pub rate: f64, 41} 42 43impl CoverageSummary { 44 pub fn from_lines(lines: &std::collections::BTreeMap<u32, u64>) -> Self { 45 let total = lines.len(); 46 let hit = lines.values().filter(|&&c| c > 0).count(); 47 let miss = lines.values().filter(|&&c| c == 0).count(); 48 let rate = if total > 0 { (hit as f64 / total as f64) * 100.0 } else { 0.0 }; 49 50 Self { total, hit, miss, rate } 51 } 52 53 pub fn from_files(files: &[FileCoverage]) -> Self { 54 let total: usize = files.iter().map(|f| f.summary.total).sum(); 55 let hit: usize = files.iter().map(|f| f.summary.hit).sum(); 56 let miss: usize = files.iter().map(|f| f.summary.miss).sum(); 57 let rate = if total > 0 { (hit as f64 / total as f64) * 100.0 } else { 0.0 }; 58 59 Self { total, hit, miss, rate } 60 } 61} 62 63#[cfg(test)] 64mod tests { 65 use super::*; 66 67 #[test] 68 fn test_coverage_summary_full() { 69 let mut lines = std::collections::BTreeMap::new(); 70 lines.insert(1, 10); 71 lines.insert(2, 5); 72 lines.insert(3, 1); 73 74 let summary = CoverageSummary::from_lines(&lines); 75 assert_eq!(summary.total, 3); 76 assert_eq!(summary.hit, 3); 77 assert_eq!(summary.miss, 0); 78 assert_eq!(summary.rate, 100.0); 79 } 80 81 #[test] 82 fn test_coverage_summary_partial() { 83 let mut lines = std::collections::BTreeMap::new(); 84 lines.insert(1, 10); 85 lines.insert(2, 0); 86 lines.insert(3, 5); 87 88 let summary = CoverageSummary::from_lines(&lines); 89 assert_eq!(summary.total, 3); 90 assert_eq!(summary.hit, 2); 91 assert_eq!(summary.miss, 1); 92 assert!((summary.rate - 66.66666666666666).abs() < 0.0001); 93 } 94 95 #[test] 96 fn test_coverage_summary_empty() { 97 let lines = std::collections::BTreeMap::new(); 98 let summary = CoverageSummary::from_lines(&lines); 99 assert_eq!(summary.total, 0); 100 assert_eq!(summary.hit, 0); 101 assert_eq!(summary.miss, 0); 102 assert_eq!(summary.rate, 0.0); 103 } 104 105 #[test] 106 fn test_file_coverage() { 107 let mut lines = std::collections::BTreeMap::new(); 108 lines.insert(1, 10); 109 lines.insert(2, 0); 110 lines.insert(3, 5); 111 112 let file = FileCoverage::new("test.rs".to_string(), lines.clone()); 113 assert_eq!(file.path, "test.rs"); 114 assert_eq!(file.summary.total, 3); 115 assert_eq!(file.summary.hit, 2); 116 assert_eq!(file.summary.miss, 1); 117 assert_eq!(file.miss_ranges, vec![(2, 2)]); 118 } 119}