jugar_probar/coverage/
jidoka.rs1use super::BlockId;
13use std::collections::HashSet;
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17pub enum JidokaAction {
18 Stop,
20 LogAndContinue,
22 Warn,
24}
25
26#[derive(Debug, Clone)]
28pub enum CoverageViolation {
29 UninstrumentedExecution {
31 block_id: BlockId,
33 },
34 CounterOverflow {
36 block_id: BlockId,
38 },
39 ImpossibleEdge {
41 from: BlockId,
43 to: BlockId,
45 },
46 CoverageRegression {
48 expected: f64,
50 actual: f64,
52 },
53}
54
55impl CoverageViolation {
56 #[must_use]
58 pub fn action(&self) -> JidokaAction {
59 match self {
60 Self::UninstrumentedExecution { .. } | Self::ImpossibleEdge { .. } => {
62 JidokaAction::Stop
63 }
64
65 Self::CounterOverflow { .. } | Self::CoverageRegression { .. } => {
67 JidokaAction::LogAndContinue
68 }
69 }
70 }
71
72 #[must_use]
74 pub fn affected_block(&self) -> Option<BlockId> {
75 match self {
76 Self::UninstrumentedExecution { block_id } | Self::CounterOverflow { block_id } => {
77 Some(*block_id)
78 }
79 Self::ImpossibleEdge { from, .. } => Some(*from),
80 Self::CoverageRegression { .. } => None,
81 }
82 }
83
84 #[must_use]
86 pub fn description(&self) -> String {
87 match self {
88 Self::UninstrumentedExecution { block_id } => {
89 format!("Block {} executed but not instrumented", block_id.as_u32())
90 }
91 Self::CounterOverflow { block_id } => {
92 format!("Counter overflow for block {}", block_id.as_u32())
93 }
94 Self::ImpossibleEdge { from, to } => {
95 format!(
96 "Impossible edge {} -> {} executed",
97 from.as_u32(),
98 to.as_u32()
99 )
100 }
101 Self::CoverageRegression { expected, actual } => {
102 format!(
103 "Coverage regression: expected {:.1}%, got {:.1}%",
104 expected, actual
105 )
106 }
107 }
108 }
109}
110
111#[derive(Debug, Default)]
116pub struct TaintedBlocks {
117 tainted: HashSet<BlockId>,
119 violations: Vec<(BlockId, CoverageViolation)>,
121 all_violations: Vec<CoverageViolation>,
123}
124
125impl TaintedBlocks {
126 #[must_use]
128 pub fn new() -> Self {
129 Self::default()
130 }
131
132 pub fn taint(&mut self, block: BlockId, violation: CoverageViolation) {
134 let _ = self.tainted.insert(block);
135 self.violations.push((block, violation.clone()));
136 self.all_violations.push(violation);
137 }
138
139 pub fn record_violation(&mut self, violation: CoverageViolation) {
141 if let Some(block) = violation.affected_block() {
142 self.taint(block, violation);
143 } else {
144 self.all_violations.push(violation);
145 }
146 }
147
148 #[must_use]
150 pub fn is_tainted(&self, block: BlockId) -> bool {
151 self.tainted.contains(&block)
152 }
153
154 #[must_use]
156 pub fn tainted_count(&self) -> usize {
157 self.tainted.len()
158 }
159
160 #[must_use]
162 pub fn violation_count(&self) -> usize {
163 self.all_violations.len()
164 }
165
166 #[must_use]
168 pub fn tainted_blocks(&self) -> Vec<BlockId> {
169 self.tainted.iter().copied().collect()
170 }
171
172 #[must_use]
174 pub fn violations_for(&self, block: BlockId) -> Vec<&CoverageViolation> {
175 self.violations
176 .iter()
177 .filter(|(b, _)| *b == block)
178 .map(|(_, v)| v)
179 .collect()
180 }
181
182 #[must_use]
184 pub fn all_violations(&self) -> &[CoverageViolation] {
185 &self.all_violations
186 }
187
188 pub fn clear(&mut self) {
190 self.tainted.clear();
191 self.violations.clear();
192 self.all_violations.clear();
193 }
194}