//! Channel tests. These run under the scheduler because `recv()` needs to //! be able to park, which requires a live runtime. use smarm::{channel, run, spawn}; use std::cell::Cell; thread_local! { static OUT: Cell = const { Cell::new(0) }; } #[test] fn send_then_recv_same_actor() { OUT.with(|c| c.set(0)); run(|| { let (tx, rx) = channel::(); tx.send(42).unwrap(); let v = rx.recv().unwrap(); OUT.with(|c| c.set(v)); }); assert_eq!(OUT.with(|c| c.get()), 42); } #[test] fn recv_parks_until_send_from_other_actor() { OUT.with(|c| c.set(0)); run(|| { let (tx, rx) = channel::(); let h = spawn(move || { // This actor blocks on an empty channel. let v = rx.recv().unwrap(); OUT.with(|c| c.set(v)); }); // Parent runs, then yields to let the child block, // then sends, then joins. smarm::yield_now(); tx.send(7).unwrap(); h.join().unwrap(); }); assert_eq!(OUT.with(|c| c.get()), 7); } #[test] fn multiple_messages_arrive_in_order() { let captured: std::sync::Arc>> = std::sync::Arc::new(std::sync::Mutex::new(Vec::new())); let cap2 = captured.clone(); run(move || { let (tx, rx) = channel::(); let h = spawn(move || { for _ in 0..3 { let v = rx.recv().unwrap(); cap2.lock().unwrap().push(v); } }); for v in 1..=3i64 { tx.send(v).unwrap(); } h.join().unwrap(); }); assert_eq!(*captured.lock().unwrap(), vec![1, 2, 3]); } #[test] fn cloned_senders_both_deliver() { let captured: std::sync::Arc>> = std::sync::Arc::new(std::sync::Mutex::new(Vec::new())); let cap2 = captured.clone(); run(move || { let (tx, rx) = channel::(); let tx2 = tx.clone(); let h = spawn(move || { for _ in 0..2 { let v = rx.recv().unwrap(); cap2.lock().unwrap().push(v); } }); tx.send(10).unwrap(); tx2.send(20).unwrap(); h.join().unwrap(); }); let mut got = captured.lock().unwrap().clone(); got.sort(); assert_eq!(got, vec![10, 20]); } #[test] fn recv_returns_err_when_all_senders_dropped() { let saw_err: std::sync::Arc = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false)); let saw_err2 = saw_err.clone(); run(move || { let (tx, rx) = channel::(); let h = spawn(move || { // Receiver waits; no message will ever come. if rx.recv().is_err() { saw_err2.store(true, std::sync::atomic::Ordering::SeqCst); } }); smarm::yield_now(); drop(tx); // last sender gone; rx.recv must return Err. h.join().unwrap(); }); assert!(saw_err.load(std::sync::atomic::Ordering::SeqCst)); }