use std::{
sync::{
atomic::{AtomicUsize, Ordering},
mpsc, Arc,
},
thread,
time::Duration,
};
#[faux::create]
pub struct Foo {}
#[faux::methods]
impl Foo {
pub fn foo(&self) {
unreachable!()
}
pub fn bar(&self) {
unreachable!()
}
}
#[test]
fn mock_multi_threaded_access() {
let mut fake = Foo::faux();
faux::when!(fake.bar).then(move |_| {});
let fake = Arc::new(fake);
let start_thread = || {
let fake = fake.clone();
std::thread::spawn(move || {
for _ in 0..10_000 {
fake.bar();
}
})
};
let thread_1 = start_thread();
let thread_2 = start_thread();
let (sender, receiver) = mpsc::channel();
thread::spawn(move || {
thread_1.join().unwrap();
thread_2.join().unwrap();
sender.send(()).unwrap();
});
receiver.recv_timeout(Duration::from_millis(100)).unwrap();
}
#[test]
fn mutex_does_not_lock_entire_mock() {
let mut fake = Foo::faux();
let call_synchronizer = Arc::new(AtomicUsize::new(0));
let foo_synchronizer = call_synchronizer.clone();
faux::when!(fake.foo).then(move |_| {
spin_until(&foo_synchronizer, 1);
foo_synchronizer.swap(2, Ordering::SeqCst);
});
let bar_synchronizer = call_synchronizer;
faux::when!(fake.bar).then(move |_| {
bar_synchronizer.swap(1, Ordering::SeqCst);
spin_until(&bar_synchronizer, 2);
});
let fake = Arc::new(fake);
let bar_thread = {
let fake = fake.clone();
std::thread::spawn(move || fake.bar())
};
let foo_thread = {
let fake = fake;
std::thread::spawn(move || fake.foo())
};
let (sender, receiver) = mpsc::channel();
thread::spawn(move || {
foo_thread.join().unwrap();
bar_thread.join().unwrap();
sender.send(()).unwrap();
});
receiver
.recv_timeout(Duration::from_millis(100))
.expect("a deadlock occurred!");
}
fn spin_until(a: &Arc<AtomicUsize>, val: usize) {
loop {
if a.load(Ordering::SeqCst) == val {
break;
}
}
}