#![forbid(unsafe_code)]
use shimforge::{Session, mock};
use std::sync::{Arc, Barrier};
use std::thread;
#[inline(never)]
fn retry_limit() -> u32 {
std::hint::black_box(3)
}
#[inline(never)]
fn batch_size() -> u32 {
std::hint::black_box(64)
}
#[inline(never)]
fn tracing_enabled() -> bool {
std::hint::black_box(false)
}
#[inline(never)]
fn descend(depth: u32) -> u64 {
if depth == 0 {
return 1;
}
let frame = std::hint::black_box([0u8; 256]);
let total = descend(depth - 1);
u64::from(std::hint::black_box(frame[0])) + total
}
#[test]
fn deep_recursion_still_works_after_a_mock_is_installed() {
let mut session = Session::new();
let limit = mock!(session, retry_limit, fn() -> u32);
limit.expect().returns(9);
assert_eq!(retry_limit(), 9);
assert_eq!(descend(1024), 1);
assert_eq!(retry_limit(), 9);
}
#[test]
fn threads_that_patch_at_the_same_time_keep_their_stacks() {
let workers = 8;
let start = Arc::new(Barrier::new(workers));
let handles: Vec<_> = (0..workers)
.map(|index| {
let start = Arc::clone(&start);
thread::Builder::new()
.stack_size(1 << 20)
.spawn(move || {
let mut session = Session::new();
match index % 3 {
0 => {
let limit = mock!(session, retry_limit, fn() -> u32);
limit.expect().returns(11);
assert_eq!(retry_limit(), 11);
}
1 => {
let size = mock!(session, batch_size, fn() -> u32);
size.expect().returns(256);
assert_eq!(batch_size(), 256);
}
_ => {
let tracing = mock!(session, tracing_enabled, fn() -> bool);
tracing.expect().returns(true);
assert!(tracing_enabled());
}
}
start.wait();
assert_eq!(descend(768), 1);
})
.unwrap()
})
.collect();
for handle in handles {
handle.join().unwrap();
}
assert_eq!(retry_limit(), 3);
assert_eq!(batch_size(), 64);
assert!(!tracing_enabled());
}