ferrous_forge/test_coverage/
utils.rs1use super::types::FileCoverage;
4use crate::{Error, Result};
5use serde::Deserialize;
6use std::collections::HashMap;
7
8#[derive(Deserialize)]
10pub struct TarpaulinOutput {
11 #[serde(rename = "coverage")]
13 pub line_coverage: f64,
14 #[serde(rename = "linesCovered")]
16 pub lines_covered: u32,
17 #[serde(rename = "linesTotal")]
19 pub lines_total: u32,
20 #[serde(rename = "branchesCovered")]
22 pub branches_covered: Option<u32>,
23 #[serde(rename = "branchesTotal")]
25 pub branches_total: Option<u32>,
26 #[serde(rename = "files")]
28 pub files: HashMap<String, TarpaulinFile>,
29}
30
31#[derive(Deserialize)]
33pub struct TarpaulinFile {
34 #[serde(rename = "coverage")]
36 pub line_coverage: f64,
37 #[serde(rename = "linesCovered")]
39 pub lines_covered: u32,
40 #[serde(rename = "linesTotal")]
42 pub lines_total: u32,
43}
44
45pub struct FunctionStats {
47 pub coverage: f64,
49 pub tested: u32,
51 pub total: u32,
53}
54
55pub fn parse_tarpaulin_json(output: &str) -> Result<TarpaulinOutput> {
62 serde_json::from_str(output)
63 .map_err(|e| Error::process(format!("Failed to parse tarpaulin output: {}", e)))
64}
65
66pub fn process_file_coverage(
68 files: &HashMap<String, TarpaulinFile>,
69) -> (HashMap<String, FileCoverage>, FunctionStats) {
70 let mut file_coverage = HashMap::new();
71 let mut total_functions_tested = 0;
72 let mut total_functions = 0;
73
74 for (file_path, file_data) in files {
75 let (estimated_functions, estimated_functions_tested) =
76 estimate_function_coverage(file_data);
77
78 total_functions += estimated_functions;
79 total_functions_tested += estimated_functions_tested;
80
81 let coverage = create_file_coverage(
82 file_path,
83 file_data,
84 estimated_functions,
85 estimated_functions_tested,
86 );
87 file_coverage.insert(file_path.clone(), coverage);
88 }
89
90 let function_coverage =
91 calculate_function_coverage_percentage(total_functions_tested, total_functions);
92
93 (
94 file_coverage,
95 FunctionStats {
96 coverage: function_coverage,
97 tested: total_functions_tested,
98 total: total_functions,
99 },
100 )
101}
102
103fn estimate_function_coverage(file_data: &TarpaulinFile) -> (u32, u32) {
105 let estimated_functions = (file_data.lines_total / 10).max(1);
106 let estimated_functions_tested =
107 ((file_data.line_coverage / 100.0) * estimated_functions as f64) as u32;
108 (estimated_functions, estimated_functions_tested)
109}
110
111fn create_file_coverage(
113 file_path: &str,
114 file_data: &TarpaulinFile,
115 estimated_functions: u32,
116 estimated_functions_tested: u32,
117) -> FileCoverage {
118 FileCoverage {
119 file_path: file_path.to_string(),
120 line_coverage: file_data.line_coverage,
121 function_coverage: calculate_function_coverage_percentage(
122 estimated_functions_tested,
123 estimated_functions,
124 ),
125 lines_tested: file_data.lines_covered,
126 total_lines: file_data.lines_total,
127 functions_tested: estimated_functions_tested,
128 total_functions: estimated_functions,
129 }
130}
131
132pub fn calculate_function_coverage_percentage(tested: u32, total: u32) -> f64 {
134 if total == 0 {
135 0.0
136 } else {
137 (tested as f64 / total as f64) * 100.0
138 }
139}
140
141pub fn calculate_branch_coverage(data: &TarpaulinOutput) -> f64 {
143 match (data.branches_covered, data.branches_total) {
144 (Some(covered), Some(total)) if total > 0 => (covered as f64 / total as f64) * 100.0,
145 _ => 0.0,
146 }
147}