use std::time::{Duration, Instant};
use traceforge::{
cover, coverage::ExecutionObserver, monitor_types::EndCondition, thread, Config, CoverageInfo,
ExecutionId, Nondet, SchedulePolicy, TypeNondet,
};
const FOO: &'static str = "FOO";
const HERE: &'static str = "REACHED_HERE";
const MSG_IS_42: &'static str = "MSG_IS_42";
const MSG_IS_0: &'static str = "MSG_IS_0";
#[test]
fn cover_basic_1() {
let stats = traceforge::verify(
Config::builder()
.with_policy(SchedulePolicy::Arbitrary)
.build(),
|| {
let h = thread::spawn(move || {
let r: i32 = traceforge::recv_msg_block();
cover!(MSG_IS_42, r == 42);
cover!(MSG_IS_0, r == 0)
});
if <bool>::nondet() {
traceforge::send_msg(h.thread().id(), 42);
} else {
cover!(HERE);
traceforge::send_msg(h.thread().id(), 11);
}
},
);
assert_eq!(stats.execs, 2);
assert!(stats.coverage.is_covered(HERE.to_string()));
assert_eq!(stats.coverage.covered(HERE.to_string()), 1);
assert_eq!(stats.coverage.covered(MSG_IS_42.to_string()), 1);
assert_eq!(stats.coverage.covered(MSG_IS_0.to_string()), 0);
}
#[test]
fn cover_basic_2() {
let stats = traceforge::verify(
Config::builder()
.with_policy(SchedulePolicy::Arbitrary)
.with_verbose(0)
.build(),
|| {
let h = thread::spawn(move || {
let _: Option<i32> = traceforge::recv_msg();
});
cover!(FOO);
let r = 0_usize..4_usize;
let v = r.nondet();
cover!("V==0", v == 1);
cover!(FOO);
if v == 0 {
cover!("V==0", v == 0); traceforge::send_msg(h.thread().id(), 42);
} else if v == 1 {
cover!("V==0", v == 1); traceforge::send_msg(h.thread().id(), 17);
} else {
traceforge::send_msg(h.thread().id(), 33);
}
},
);
assert_eq!(stats.execs, 8);
}
#[derive(Clone, Default)]
struct CoverageCheck {
eids: Vec<traceforge::ExecutionId>,
}
impl CoverageCheck {
pub fn stats(&mut self) {
assert_eq!(self.eids.len(), 1);
print!("Msg 42 was received in the following executions");
for eid in &self.eids {
print!(" {eid} ");
}
println!();
}
}
impl ExecutionObserver for CoverageCheck {
fn after(
&mut self,
eid: traceforge::ExecutionId,
_econdition: &EndCondition,
c: traceforge::CoverageInfo,
) -> () {
if c.is_covered(MSG_IS_42.to_owned()) {
self.eids.push(eid);
}
}
fn at_end_of_exploration(&mut self) -> () {
self.stats();
}
fn before(&mut self, _eid: traceforge::ExecutionId) -> () {}
}
#[test]
fn cover_basic_3() {
let covcheck = CoverageCheck::default();
let config = Config::builder()
.with_policy(SchedulePolicy::Arbitrary)
.with_callback(Box::new(covcheck))
.with_callback(Box::new(TimeMetric::new()))
.build();
let stats = traceforge::verify(config, || {
let h = thread::spawn(move || {
let r: i32 = traceforge::recv_msg_block();
cover!(MSG_IS_42, r == 42);
});
if <bool>::nondet() {
traceforge::send_msg(h.thread().id(), 42);
} else {
traceforge::send_msg(h.thread().id(), 11);
}
});
assert_eq!(stats.execs, 2);
assert_eq!(stats.coverage.covered(MSG_IS_42.to_string()), 1);
}
struct TimeMetric {
max: Duration,
min: Duration,
avg: Duration,
num_iter: u32,
now: Instant,
}
impl TimeMetric {
pub fn new() -> Self {
Self {
max: Duration::ZERO,
min: Duration::MAX,
avg: Duration::ZERO,
num_iter: 0,
now: Instant::now(),
}
}
}
impl ExecutionObserver for TimeMetric {
fn before(&mut self, _eid: traceforge::ExecutionId) -> () {
self.now = Instant::now();
self.num_iter += 1;
}
fn after(&mut self, _eid: ExecutionId, econd: &EndCondition, _c: CoverageInfo) {
match econd {
EndCondition::MonitorTerminated => {}
EndCondition::AllThreadsCompleted => {
let elapsed = self.now.elapsed();
if self.max < elapsed {
self.max = elapsed;
}
if elapsed < self.min {
self.min = elapsed;
}
self.avg = ((self.num_iter - 1) * self.avg + elapsed) / self.num_iter;
}
EndCondition::Deadlock => {}
EndCondition::FailedAssumption => {}
}
}
fn at_end_of_exploration(&mut self) -> () {
println!(
"There were {} iterations. Max={:?}, min={:?}, avg={:?}",
self.num_iter, self.max, self.min, self.avg
);
}
}