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> {
57 serde_json::from_str(output)
58 .map_err(|e| Error::process(format!("Failed to parse tarpaulin output: {}", e)))
59}
60
61pub fn process_file_coverage(
63 files: &HashMap<String, TarpaulinFile>,
64) -> (HashMap<String, FileCoverage>, FunctionStats) {
65 let mut file_coverage = HashMap::new();
66 let mut total_functions_tested = 0;
67 let mut total_functions = 0;
68
69 for (file_path, file_data) in files {
70 let (estimated_functions, estimated_functions_tested) =
71 estimate_function_coverage(file_data);
72
73 total_functions += estimated_functions;
74 total_functions_tested += estimated_functions_tested;
75
76 let coverage = create_file_coverage(
77 file_path,
78 file_data,
79 estimated_functions,
80 estimated_functions_tested,
81 );
82 file_coverage.insert(file_path.clone(), coverage);
83 }
84
85 let function_coverage =
86 calculate_function_coverage_percentage(total_functions_tested, total_functions);
87
88 (
89 file_coverage,
90 FunctionStats {
91 coverage: function_coverage,
92 tested: total_functions_tested,
93 total: total_functions,
94 },
95 )
96}
97
98fn estimate_function_coverage(file_data: &TarpaulinFile) -> (u32, u32) {
100 let estimated_functions = (file_data.lines_total / 10).max(1);
101 let estimated_functions_tested =
102 ((file_data.line_coverage / 100.0) * estimated_functions as f64) as u32;
103 (estimated_functions, estimated_functions_tested)
104}
105
106fn create_file_coverage(
108 file_path: &str,
109 file_data: &TarpaulinFile,
110 estimated_functions: u32,
111 estimated_functions_tested: u32,
112) -> FileCoverage {
113 FileCoverage {
114 file_path: file_path.to_string(),
115 line_coverage: file_data.line_coverage,
116 function_coverage: calculate_function_coverage_percentage(
117 estimated_functions_tested,
118 estimated_functions,
119 ),
120 lines_tested: file_data.lines_covered,
121 total_lines: file_data.lines_total,
122 functions_tested: estimated_functions_tested,
123 total_functions: estimated_functions,
124 }
125}
126
127pub fn calculate_function_coverage_percentage(tested: u32, total: u32) -> f64 {
129 if total == 0 {
130 0.0
131 } else {
132 (tested as f64 / total as f64) * 100.0
133 }
134}
135
136pub fn calculate_branch_coverage(data: &TarpaulinOutput) -> f64 {
138 match (data.branches_covered, data.branches_total) {
139 (Some(covered), Some(total)) if total > 0 => (covered as f64 / total as f64) * 100.0,
140 _ => 0.0,
141 }
142}