use crate::Env;
use crate::system::r#virtual::{Executor, FileBody, Inode, SystemState, VirtualSystem};
use assert_matches::assert_matches;
use futures_executor::LocalSpawner;
use futures_util::task::LocalSpawnExt as _;
use std::cell::{Cell, RefCell};
use std::pin::Pin;
use std::rc::Rc;
use std::str::from_utf8;
impl Executor for LocalSpawner {
fn spawn(
&self,
task: Pin<Box<dyn Future<Output = ()>>>,
) -> Result<(), Box<dyn std::error::Error>> {
Ok(self.spawn_local(task)?)
}
}
#[deprecated(
since = "0.13.2",
note = "LocalSpawner can be used directly as an Executor, so this adapter is no longer needed"
)]
#[derive(Clone, Debug)]
pub struct LocalExecutor(pub LocalSpawner);
#[allow(deprecated)]
impl Executor for LocalExecutor {
fn spawn(
&self,
task: Pin<Box<dyn Future<Output = ()>>>,
) -> Result<(), Box<dyn std::error::Error>> {
Ok(self.0.spawn_local(task)?)
}
}
pub fn in_virtual_system<F, Fut, T>(task: F) -> T
where
F: FnOnce(Env<VirtualSystem>, Rc<RefCell<SystemState>>) -> Fut,
Fut: Future<Output = T> + 'static,
T: 'static,
{
let system = VirtualSystem::new();
let state = Rc::clone(&system.state);
let global_executor = yash_executor::Executor::new();
state.borrow_mut().executor = Some(Rc::new(global_executor.spawner()));
let env = Env::with_system(system);
let concurrent = env.system.clone();
let task = task(env, Rc::clone(&state));
let result = Rc::new(Cell::new(None));
let result_passer = Rc::clone(&result);
let runner = async move {
let run_task_and_set_result = async move { result_passer.set(Some(task.await)) };
concurrent.run_virtual(run_task_and_set_result).await
};
unsafe { global_executor.spawn_pinned(Box::pin(runner)) };
loop {
global_executor.run_until_stalled();
if let Some(result) = result.take() {
return result;
}
let mut state = state.borrow_mut();
if let Some(next_wake_time) = state.scheduled_wakers.next_wake_time() {
state.advance_time(next_wake_time);
}
assert_ne!(
global_executor.wake_count(),
0,
"deadlock detected: at least one task should be woken up to make progress"
);
}
}
pub fn stub_tty(state: &RefCell<SystemState>) {
state
.borrow_mut()
.file_system
.save("/dev/tty", Rc::new(RefCell::new(Inode::new([]))))
.unwrap();
}
pub fn assert_stdout<F, T>(state: &RefCell<SystemState>, f: F) -> T
where
F: FnOnce(&str) -> T,
{
let stdout = state.borrow().file_system.get("/dev/stdout").unwrap();
let stdout = stdout.borrow();
assert_matches!(&stdout.body, FileBody::Regular { content, .. } => {
f(from_utf8(content).unwrap())
})
}
pub fn assert_stderr<F, T>(state: &RefCell<SystemState>, f: F) -> T
where
F: FnOnce(&str) -> T,
{
let stderr = state.borrow().file_system.get("/dev/stderr").unwrap();
let stderr = stderr.borrow();
assert_matches!(&stderr.body, FileBody::Regular { content, .. } => {
f(from_utf8(content).unwrap())
})
}
pub mod function;
mod wake_flag;
pub use wake_flag::WakeFlag;