rust_covfix/
fix.rs

1use crate::coverage::{PackageCoverage, TotalCoverage};
2use crate::error::*;
3use crate::rule::{default_rules, Rule, SourceCode};
4
5/// Fix coverage information based on source code
6///
7/// You MUST fix coverage information using this struct because
8/// Rules require coverage informations to be stored in correct format.
9/// This struct checks the information format and modify it if it is invalid.
10pub struct CoverageFixer {
11    rules: Vec<Box<dyn Rule>>,
12}
13
14impl CoverageFixer {
15    pub fn new() -> Self {
16        Self {
17            rules: default_rules(),
18        }
19    }
20
21    pub fn with_rules<I: Into<Vec<Box<dyn Rule>>>>(rules: I) -> Self {
22        Self {
23            rules: rules.into(),
24        }
25    }
26
27    /// fix coverage information
28    pub fn fix(&self, data: &mut PackageCoverage) -> Result<(), Error> {
29        if self.rules.is_empty() {
30            debugln!("Skipping fix because rules are empty");
31        }
32
33        let old = CoverageSummary::new(data);
34
35        debugln!("Fixing package coverage");
36        for file_cov in &mut data.file_coverages {
37            file_cov.line_coverages.sort_by_key(|v| v.line_number);
38            file_cov.branch_coverages.sort_by_key(|v| v.line_number);
39
40            let path = file_cov.path();
41            debugln!("Processing file {:?}", path);
42
43            let source = SourceCode::new(path)?;
44
45            for rule in self.rules.iter() {
46                rule.fix_file_coverage(&source, file_cov);
47            }
48
49            file_cov.line_coverages.retain(|v| v.count.is_some());
50            file_cov.branch_coverages.retain(|v| v.taken.is_some());
51        }
52
53        let new = CoverageSummary::new(data);
54
55        infoln!("Coverages are fixed successfully!");
56
57        infoln!(
58            "  line:   {:.2}% ({} of {} lines)    => {:.2}% ({} of {} lines)",
59            old.line_percent(),
60            old.line_executed,
61            old.line_total,
62            new.line_percent(),
63            new.line_executed,
64            new.line_total,
65        );
66
67        infoln!(
68            "  branch: {:.2}% ({} of {} branches) => {:.2}% ({} of {} branches)\n",
69            old.branch_percent(),
70            old.branch_executed,
71            old.branch_total,
72            new.branch_percent(),
73            new.branch_executed,
74            new.branch_total,
75        );
76
77        Ok(())
78    }
79}
80
81impl Default for CoverageFixer {
82    fn default() -> Self {
83        Self::new()
84    }
85}
86
87struct CoverageSummary {
88    line_executed: usize,
89    line_total: usize,
90    branch_executed: usize,
91    branch_total: usize,
92}
93
94impl CoverageSummary {
95    fn new(data: &PackageCoverage) -> Self {
96        Self {
97            line_executed: data.line_executed(),
98            line_total: data.line_total(),
99            branch_executed: data.branch_executed(),
100            branch_total: data.branch_total(),
101        }
102    }
103
104    fn line_percent(&self) -> f64 {
105        (self.line_executed as f64) / (self.line_total as f64) * 100.0
106    }
107
108    fn branch_percent(&self) -> f64 {
109        (self.branch_executed as f64) / (self.branch_total as f64) * 100.0
110    }
111}