forked from model-checking/kani
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcoverage.rs
124 lines (111 loc) · 3.98 KB
/
coverage.rs
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
112
113
114
115
116
117
118
119
120
121
122
123
124
use anyhow::Result;
use console::style;
use serde_derive::{Deserialize, Serialize};
use std::fmt::{self, Write};
use std::{collections::BTreeMap, fmt::Display};
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "UPPERCASE")]
pub enum CheckStatus {
Failure,
Covered, // for `code_coverage` properties only
Satisfied, // for `cover` properties only
Success,
Undetermined,
Unreachable,
Uncovered, // for `code_coverage` properties only
Unsatisfiable, // for `cover` properties only
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct CoverageResults {
pub data: BTreeMap<String, Vec<CoverageCheck>>,
}
impl CoverageResults {
pub fn new(data: BTreeMap<String, Vec<CoverageCheck>>) -> Self {
Self { data }
}
}
pub fn fmt_coverage_results(coverage_results: &CoverageResults) -> Result<String> {
let mut fmt_string = String::new();
for (file, checks) in coverage_results.data.iter() {
let mut checks_by_function: BTreeMap<String, Vec<CoverageCheck>> = BTreeMap::new();
// // Group checks by function
for check in checks {
// Insert the check into the vector corresponding to its function
checks_by_function
.entry(check.function.clone())
.or_insert_with(Vec::new)
.push(check.clone());
}
for (function, checks) in checks_by_function {
writeln!(fmt_string, "{file} ({function})")?;
let mut sorted_checks: Vec<CoverageCheck> = checks.to_vec();
sorted_checks.sort_by(|a, b| a.region.start.cmp(&b.region.start));
for check in sorted_checks.iter() {
writeln!(fmt_string, " * {} {}", check.region, check.status)?;
}
writeln!(fmt_string, "")?;
}
}
Ok(fmt_string)
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CoverageCheck {
pub function: String,
term: CoverageTerm,
pub region: CoverageRegion,
status: CheckStatus,
}
impl CoverageCheck {
pub fn new(
function: String,
term: CoverageTerm,
region: CoverageRegion,
status: CheckStatus,
) -> Self {
Self { function, term, region, status }
}
pub fn is_covered(&self) -> bool {
self.status == CheckStatus::Covered
}
}
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct CoverageRegion {
pub file: String,
pub start: (u32, u32),
pub end: (u32, u32),
}
impl Display for CoverageRegion {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}:{} - {}:{}", self.start.0, self.start.1, self.end.0, self.end.1)
}
}
impl CoverageRegion {
pub fn from_str(str: String) -> Self {
let str_splits: Vec<&str> = str.split([':', '-']).map(|s| s.trim()).collect();
assert_eq!(str_splits.len(), 5, "{str:?}");
let file = str_splits[0].to_string();
let start = (str_splits[1].parse().unwrap(), str_splits[2].parse().unwrap());
let end = (str_splits[3].parse().unwrap(), str_splits[4].parse().unwrap());
Self { file, start, end }
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum CoverageTerm {
Counter(u32),
Expression(u32),
}
impl std::fmt::Display for CheckStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let check_str = match self {
CheckStatus::Satisfied => style("SATISFIED").green(),
CheckStatus::Success => style("SUCCESS").green(),
CheckStatus::Covered => style("COVERED").green(),
CheckStatus::Uncovered => style("UNCOVERED").red(),
CheckStatus::Failure => style("FAILURE").red(),
CheckStatus::Unreachable => style("UNREACHABLE").yellow(),
CheckStatus::Undetermined => style("UNDETERMINED").yellow(),
CheckStatus::Unsatisfiable => style("UNSATISFIABLE").yellow(),
};
write!(f, "{check_str}")
}
}