phink_lib/cover/
coverage.rs1use crate::{
2 cli::config::{
3 PFiles::CoverageTracePath,
4 PhinkFiles,
5 },
6 cover::trace::CoverageTrace,
7};
8use std::{
9 fmt,
10 fmt::{
11 Debug,
12 Formatter,
13 },
14 fs::OpenOptions,
15 hint::black_box,
16 io::Write,
17 path::PathBuf,
18};
19
20#[derive(Clone, Default)]
25pub struct InputCoverage {
26 all_cov_id: Vec<u64>,
28 trace: Vec<CoverageTrace>,
30}
31
32impl Debug for InputCoverage {
33 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
34 f.debug_struct("Coverage")
35 .field("", &self.all_cov_id)
36 .finish()
37 }
38}
39
40impl InputCoverage {
41 pub fn new() -> InputCoverage {
42 InputCoverage {
43 all_cov_id: Vec::new(),
44 trace: vec![],
45 }
46 }
47 pub fn coverage_len(&self) -> usize {
48 self.all_cov_id.len()
49 }
50
51 pub fn messages_coverage(&self) -> &Vec<u64> {
52 &self.all_cov_id
53 }
54
55 pub fn concatened_trace(&self) -> String {
56 self.trace
57 .iter()
58 .map(|coverage_trace| coverage_trace.as_string())
59 .collect::<Vec<String>>()
60 .join(" | ")
61 .replace("\n", " ")
62 }
63
64 pub fn add_cov(&mut self, coverage: CoverageTrace) {
65 let parsed = coverage.parse_coverage();
66 self.trace.push(coverage);
67 for id in parsed {
68 self.all_cov_id.push(id);
69 }
70 }
71
72 pub fn save(&self, output: &PathBuf) -> std::io::Result<()> {
73 let mut trace_strings: Vec<String> = self
74 .messages_coverage()
75 .iter()
76 .map(|id| id.to_string())
77 .collect();
78
79 trace_strings.sort_unstable();
80
81 let mut file = OpenOptions::new()
82 .append(true)
83 .create(true)
84 .open(PhinkFiles::new_by_ref(output).path(CoverageTracePath))?;
85 writeln!(file, "{}", trace_strings.join("\n"))?;
87 Ok(())
88 }
89
90 #[allow(clippy::identity_op)]
94 pub fn redirect_coverage(&self, flat: &[u64]) {
95 seq_macro::seq!(cov_id in 0_u64 .. 1_000_u64 {
96 if black_box(flat.contains(&cov_id)) {
97 let cov = black_box(cov_id.saturating_add(1));
98 print!("(DEBUG)-cov:{cov}");
99 }
100 });
101 }
102}
103#[cfg(test)]
104mod tests {
105 use super::*;
106 use tempfile::TempDir;
107
108 #[test]
109 fn test_coverage_len() {
110 let mut coverage = InputCoverage::new();
111 coverage.all_cov_id.push(1);
112 coverage.all_cov_id.push(2);
113 assert_eq!(coverage.coverage_len(), 2);
114 }
115
116 #[test]
117 fn test_messages_coverage() {
118 let mut coverage = InputCoverage::new();
119 coverage.all_cov_id.push(1);
120 coverage.all_cov_id.push(2);
121 let messages = coverage.messages_coverage();
122 assert_eq!(messages.len(), 2);
123 assert!(messages.contains(&1));
124 assert!(messages.contains(&2));
125 }
126
127 #[test]
128 fn test_add_cov() {
129 let mut coverage = InputCoverage::new();
130 let trace = CoverageTrace::from("COV=1 COV=2 COV=3".as_bytes().to_vec());
131 coverage.add_cov(trace);
132 assert_eq!(coverage.coverage_len(), 3);
133 assert!(coverage.messages_coverage().contains(&1));
134 assert!(coverage.messages_coverage().contains(&2));
135 assert!(coverage.messages_coverage().contains(&3));
136 }
137
138 #[test]
139 #[ignore] fn test_add_cov_deduplication() {
141 let mut coverage = InputCoverage::new();
142 let trace1 = CoverageTrace::from("COV=1 COV=2 COV=3".as_bytes().to_vec());
143 let trace2 = CoverageTrace::from("COV=2 COV=3 COV=4".as_bytes().to_vec());
144 coverage.add_cov(trace1);
145 coverage.add_cov(trace2);
146 assert_eq!(coverage.coverage_len(), 4);
147 }
148
149 #[test]
150 fn test_debug_format() {
151 let mut coverage = InputCoverage::new();
152 coverage.all_cov_id.push(1);
153 coverage.all_cov_id.push(2);
154 let debug_output = format!("{:?}", coverage);
155 assert!(debug_output.contains("Coverage"));
156 assert!(debug_output.contains("1"));
157 assert!(debug_output.contains("2"));
158 }
159
160 #[test]
161 fn test_save() {
162 let mut coverage = InputCoverage::new();
163 coverage.all_cov_id.push(1);
164 coverage.all_cov_id.push(2);
165 coverage.all_cov_id.push(3);
166
167 let output = TempDir::new().unwrap().into_path();
168 let cov_trace_path = PhinkFiles::new(output.clone())
169 .make_all()
170 .path(CoverageTracePath);
171
172 coverage.save(&output).unwrap();
173
174 let content = std::fs::read_to_string(cov_trace_path).unwrap();
175 let lines: Vec<String> = content.lines().map(String::from).collect();
176
177 assert_eq!(
178 lines,
179 Vec::from_iter(vec!["1".to_string(), "2".to_string(), "3".to_string()])
180 );
181 }
182}