use super::BlockId;
use std::collections::HashSet;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum JidokaAction {
Stop,
LogAndContinue,
Warn,
}
#[derive(Debug, Clone)]
pub enum CoverageViolation {
UninstrumentedExecution {
block_id: BlockId,
},
CounterOverflow {
block_id: BlockId,
},
ImpossibleEdge {
from: BlockId,
to: BlockId,
},
CoverageRegression {
expected: f64,
actual: f64,
},
}
impl CoverageViolation {
#[must_use]
pub fn action(&self) -> JidokaAction {
match self {
Self::UninstrumentedExecution { .. } | Self::ImpossibleEdge { .. } => {
JidokaAction::Stop
}
Self::CounterOverflow { .. } | Self::CoverageRegression { .. } => {
JidokaAction::LogAndContinue
}
}
}
#[must_use]
pub fn affected_block(&self) -> Option<BlockId> {
match self {
Self::UninstrumentedExecution { block_id } | Self::CounterOverflow { block_id } => {
Some(*block_id)
}
Self::ImpossibleEdge { from, .. } => Some(*from),
Self::CoverageRegression { .. } => None,
}
}
#[must_use]
pub fn description(&self) -> String {
match self {
Self::UninstrumentedExecution { block_id } => {
format!("Block {} executed but not instrumented", block_id.as_u32())
}
Self::CounterOverflow { block_id } => {
format!("Counter overflow for block {}", block_id.as_u32())
}
Self::ImpossibleEdge { from, to } => {
format!(
"Impossible edge {} -> {} executed",
from.as_u32(),
to.as_u32()
)
}
Self::CoverageRegression { expected, actual } => {
format!(
"Coverage regression: expected {:.1}%, got {:.1}%",
expected, actual
)
}
}
}
}
#[derive(Debug, Default)]
pub struct TaintedBlocks {
tainted: HashSet<BlockId>,
violations: Vec<(BlockId, CoverageViolation)>,
all_violations: Vec<CoverageViolation>,
}
impl TaintedBlocks {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn taint(&mut self, block: BlockId, violation: CoverageViolation) {
let _ = self.tainted.insert(block);
self.violations.push((block, violation.clone()));
self.all_violations.push(violation);
}
pub fn record_violation(&mut self, violation: CoverageViolation) {
if let Some(block) = violation.affected_block() {
self.taint(block, violation);
} else {
self.all_violations.push(violation);
}
}
#[must_use]
pub fn is_tainted(&self, block: BlockId) -> bool {
self.tainted.contains(&block)
}
#[must_use]
pub fn tainted_count(&self) -> usize {
self.tainted.len()
}
#[must_use]
pub fn violation_count(&self) -> usize {
self.all_violations.len()
}
#[must_use]
pub fn tainted_blocks(&self) -> Vec<BlockId> {
self.tainted.iter().copied().collect()
}
#[must_use]
pub fn violations_for(&self, block: BlockId) -> Vec<&CoverageViolation> {
self.violations
.iter()
.filter(|(b, _)| *b == block)
.map(|(_, v)| v)
.collect()
}
#[must_use]
pub fn all_violations(&self) -> &[CoverageViolation] {
&self.all_violations
}
pub fn clear(&mut self) {
self.tainted.clear();
self.violations.clear();
self.all_violations.clear();
}
}