use crate::deterministic::random::Random;
use parking_lot::Mutex;
use std::collections::HashMap;
use once_cell::sync::Lazy;
#[derive(Debug)]
#[doc(hidden)]
pub struct Buggifier {
pub buggified_lines: Mutex<HashMap<String, bool>>,
pub random: Mutex<Option<Random>>,
}
#[doc(hidden)]
pub static BUGGIFIER_INSTANCE: Lazy<Buggifier> = Lazy::new(|| Buggifier::default());
#[doc(hidden)]
pub fn handle_buggify(line: String, probability: f64) -> bool {
let mut lock = BUGGIFIER_INSTANCE.random.lock();
match (*lock).as_mut() {
None => false,
Some(deterministic_random) => {
let mut already_buggified = BUGGIFIER_INSTANCE.buggified_lines.lock();
if !already_buggified.contains_key(&line)
&& deterministic_random.random_boolean(probability)
{
tracing::info!("buggifying line {}", line);
already_buggified.insert(line, true);
return true;
}
false
}
}
}
impl Default for Buggifier {
fn default() -> Self {
Buggifier {
buggified_lines: Mutex::new(HashMap::new()),
random: Default::default(),
}
}
}
pub fn is_buggify_enabled() -> bool {
BUGGIFIER_INSTANCE.random.lock().is_some()
}
pub fn enable_buggify(r: Random) {
tracing::info!("enabling buggify");
let mut data = BUGGIFIER_INSTANCE.random.lock();
*data = Some(r.clone());
}
pub fn disable_buggify() {
tracing::info!("disabling buggify");
let mut data = BUGGIFIER_INSTANCE.random.lock();
*data = None;
let mut map = BUGGIFIER_INSTANCE.buggified_lines.lock();
map.clear();
}
#[macro_export]
macro_rules! buggify {
($probability:expr) => {{
let line = format!("{}:{}", file!(), line!());
handle_buggify(line, $probability)
}};
() => {{
let line = format!("{}:{}", file!(), line!());
handle_buggify(line, 0.05)
}};
}
#[cfg(test)]
mod tests {
use crate::buggify::BUGGIFIER_INSTANCE;
use crate::buggify::*;
use crate::deterministic::random::Random;
#[test]
fn test_macro() {
assert!(!is_buggify_enabled());
assert!(!buggify!(1.0), "should not buggified");
{
let data = BUGGIFIER_INSTANCE.random.lock();
assert!((*data).is_none());
let map = BUGGIFIER_INSTANCE.buggified_lines.lock();
assert!((*map).is_empty());
}
let random = Random::new_with_seed(42);
enable_buggify(random);
assert!(is_buggify_enabled(), "should be activated");
for i in 0..100 {
let result = if i == 8 { true } else { false };
assert_eq!(buggify!(), result, "{} should have been {}", i, result);
}
{
dbg!(&BUGGIFIER_INSTANCE);
let data = BUGGIFIER_INSTANCE.random.lock();
assert!((*data).is_some());
let map = BUGGIFIER_INSTANCE.buggified_lines.lock();
assert_eq!((*map).len(), 1);
for key in (*map).keys() {
assert!(key.starts_with("simulation/src/buggify.rs:"));
}
for value in (*map).values() {
assert!(value);
}
}
disable_buggify();
assert!(!buggify!(1.0), "should not buggified");
}
}