1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
use crate::coverage::{PackageCoverage, TotalCoverage};
use crate::error::*;
use crate::rule::{default_rules, Rule, SourceCode};

/// Fix coverage information based on source code
///
/// You MUST fix coverage information using this struct because
/// Rules require coverage informations to be stored in correct format.
/// This struct checks the information format and modify it if it is invalid.
pub struct CoverageFixer {
    rules: Vec<Box<dyn Rule>>,
}

impl CoverageFixer {
    pub fn new() -> Self {
        Self {
            rules: default_rules(),
        }
    }

    pub fn with_rules<I: Into<Vec<Box<dyn Rule>>>>(rules: I) -> Self {
        Self {
            rules: rules.into(),
        }
    }

    /// fix coverage information
    pub fn fix(&self, data: &mut PackageCoverage) -> Result<(), Error> {
        if self.rules.is_empty() {
            debugln!("Skipping fix because rules are empty");
        }

        let old = CoverageSummary::new(data);

        debugln!("Fixing package coverage");
        for file_cov in &mut data.file_coverages {
            file_cov.line_coverages.sort_by_key(|v| v.line_number);
            file_cov.branch_coverages.sort_by_key(|v| v.line_number);

            let path = file_cov.path();
            debugln!("Processing file {:?}", path);

            let source = SourceCode::new(path)?;

            for rule in self.rules.iter() {
                rule.fix_file_coverage(&source, file_cov);
            }

            file_cov.line_coverages.retain(|v| v.count.is_some());
            file_cov.branch_coverages.retain(|v| v.taken.is_some());
        }

        let new = CoverageSummary::new(data);

        infoln!("Coverages are fixed successfully!");

        infoln!(
            "  line:   {:.2}% ({} of {} lines)    => {:.2}% ({} of {} lines)",
            old.line_percent(),
            old.line_executed,
            old.line_total,
            new.line_percent(),
            new.line_executed,
            new.line_total,
        );

        infoln!(
            "  branch: {:.2}% ({} of {} branches) => {:.2}% ({} of {} branches)\n",
            old.branch_percent(),
            old.branch_executed,
            old.branch_total,
            new.branch_percent(),
            new.branch_executed,
            new.branch_total,
        );

        Ok(())
    }
}

impl Default for CoverageFixer {
    fn default() -> Self {
        Self::new()
    }
}

struct CoverageSummary {
    line_executed: usize,
    line_total: usize,
    branch_executed: usize,
    branch_total: usize,
}

impl CoverageSummary {
    fn new(data: &PackageCoverage) -> Self {
        Self {
            line_executed: data.line_executed(),
            line_total: data.line_total(),
            branch_executed: data.branch_executed(),
            branch_total: data.branch_total(),
        }
    }

    fn line_percent(&self) -> f64 {
        (self.line_executed as f64) / (self.line_total as f64) * 100.0
    }

    fn branch_percent(&self) -> f64 {
        (self.branch_executed as f64) / (self.branch_total as f64) * 100.0
    }
}