use std::sync::atomic::{AtomicUsize, Ordering};
use std::{cell::Cell, cell::RefCell, time::Duration};
use super::*;
use crate::{Arbiter, testing::TestRunner};
#[test]
fn builder_options() {
let builder = System::build()
.name("opts")
.signals(false)
.panic_handling(false)
.enable_signals()
.disable_signals()
.stack_size(2 * 1024 * 1024)
.ping_interval(0)
.ping_threshold(10)
.thread_pool_limit(1)
.thread_pool_recv_timeout(Duration::from_millis(100))
.testing();
assert!(format!("{builder:?}").contains("opts"));
let runner = builder.build(TestRunner);
assert!(format!("{runner:?}").contains("opts"));
runner.block_on(async {
let sys = System::current();
assert_eq!(sys.name(), "opts");
assert!(sys.testing());
assert!(!sys.signals());
let mut arb = Arbiter::new();
arb.stop();
arb.join().unwrap();
assert_eq!(sys.spawn_blocking(|| 1).await, Ok(1));
});
}
#[test]
fn run_exit_codes() {
let err = System::new("test", TestRunner)
.run(|| Err(io::Error::other("init failed")))
.unwrap_err();
assert_eq!(err.to_string(), "init failed");
System::new("test", TestRunner)
.run(|| {
System::current().stop();
Ok(())
})
.unwrap();
let called = Rc::new(Cell::new(false));
let arb = Rc::new(RefCell::new(None));
let (called2, arb2) = (called.clone(), arb.clone());
let err = System::new("test", TestRunner)
.run(move || {
Arbiter::on_shutdown(move || called2.set(true));
*arb2.borrow_mut() = Some(Arbiter::new());
System::current().stop_with_code(3);
Ok(())
})
.unwrap_err();
assert_eq!(err.to_string(), "Non-zero exit code: 3");
assert!(called.get());
let mut arb = arb.borrow_mut().take().unwrap();
arb.join().unwrap();
assert!(!arb.is_running());
}
#[test]
fn exit_stops_child_arbiters() {
fn assert_stopped(arb: &Rc<RefCell<Option<Arbiter>>>) {
let arb = arb.borrow_mut().take().unwrap();
let mut arb2 = arb.clone();
let (tx, rx) = std::sync::mpsc::channel();
std::thread::spawn(move || {
let _ = tx.send(arb2.join().is_ok());
});
let stopped = rx.recv_timeout(Duration::from_secs(5));
arb.stop();
assert_eq!(stopped, Ok(true), "child arbiter is still running");
}
let arb = Rc::new(RefCell::new(None));
let arb2 = arb.clone();
let err = System::new("test", TestRunner)
.run(move || {
*arb2.borrow_mut() = Some(Arbiter::new());
Err(io::Error::other("init failed"))
})
.unwrap_err();
assert_eq!(err.to_string(), "init failed");
assert_stopped(&arb);
let arb2 = arb.clone();
System::new("test", TestRunner).block_on(async move {
*arb2.borrow_mut() = Some(Arbiter::new());
});
assert_stopped(&arb);
let arb2 = arb.clone();
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
System::new("test", TestRunner).block_on(async move {
*arb2.borrow_mut() = Some(Arbiter::new());
panic!("root future panic");
});
}));
assert!(result.is_err());
assert_stopped(&arb);
}
#[test]
fn primary_arbiter_finalized() {
fn with_on_stop(calls: &Arc<AtomicUsize>) -> Arbiter {
let calls = calls.clone();
Arbiter::current().on_stop(move || {
calls.fetch_add(1, Ordering::Relaxed);
})
}
let calls = Arc::new(AtomicUsize::new(0));
let calls2 = calls.clone();
let arb = System::new("test", TestRunner).block_on(async move { with_on_stop(&calls2) });
assert!(!arb.is_running());
assert_eq!(calls.load(Ordering::Relaxed), 1);
let calls = Arc::new(AtomicUsize::new(0));
let (calls2, slot) = (calls.clone(), Rc::new(RefCell::new(None)));
let slot2 = slot.clone();
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
System::new("test", TestRunner).block_on(async move {
*slot2.borrow_mut() = Some(with_on_stop(&calls2));
panic!("root future panic");
});
}));
assert!(result.is_err());
assert!(!slot.borrow().as_ref().unwrap().is_running());
assert_eq!(calls.load(Ordering::Relaxed), 1);
let calls = Arc::new(AtomicUsize::new(0));
let (calls2, slot) = (calls.clone(), Rc::new(RefCell::new(None)));
let slot2 = slot.clone();
System::new("test", TestRunner)
.run(move || {
*slot2.borrow_mut() = Some(with_on_stop(&calls2));
System::current().stop();
Ok(())
})
.unwrap();
assert!(!slot.borrow().as_ref().unwrap().is_running());
assert_eq!(calls.load(Ordering::Relaxed), 1);
}
#[cfg(feature = "tokio")]
#[test]
fn run_local_finalizes_primary_arbiter() {
let calls = Arc::new(AtomicUsize::new(0));
let calls2 = calls.clone();
let rt = tok_io::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap();
let arb = rt.block_on(System::new("test", TestRunner).run_local(async move {
Arbiter::current().on_stop(move || {
calls2.fetch_add(1, Ordering::Relaxed);
})
}));
assert!(!arb.is_running());
assert_eq!(calls.load(Ordering::Relaxed), 1);
}