summaryrefslogtreecommitdiffstats
path: root/third_party/rust/crossbeam-channel/tests/select_macro.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
commit43a97878ce14b72f0981164f87f2e35e14151312 (patch)
tree620249daf56c0258faa40cbdcf9cfba06de2a846 /third_party/rust/crossbeam-channel/tests/select_macro.rs
parentInitial commit. (diff)
downloadfirefox-43a97878ce14b72f0981164f87f2e35e14151312.tar.xz
firefox-43a97878ce14b72f0981164f87f2e35e14151312.zip
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/crossbeam-channel/tests/select_macro.rs')
-rw-r--r--third_party/rust/crossbeam-channel/tests/select_macro.rs1480
1 files changed, 1480 insertions, 0 deletions
diff --git a/third_party/rust/crossbeam-channel/tests/select_macro.rs b/third_party/rust/crossbeam-channel/tests/select_macro.rs
new file mode 100644
index 0000000000..119454cd68
--- /dev/null
+++ b/third_party/rust/crossbeam-channel/tests/select_macro.rs
@@ -0,0 +1,1480 @@
+//! Tests for the `select!` macro.
+
+#![forbid(unsafe_code)] // select! is safe.
+#![allow(clippy::drop_copy, clippy::match_single_binding)]
+
+use std::any::Any;
+use std::cell::Cell;
+use std::ops::Deref;
+use std::thread;
+use std::time::{Duration, Instant};
+
+use crossbeam_channel::{after, bounded, never, select, tick, unbounded};
+use crossbeam_channel::{Receiver, RecvError, SendError, Sender, TryRecvError};
+use crossbeam_utils::thread::scope;
+
+fn ms(ms: u64) -> Duration {
+ Duration::from_millis(ms)
+}
+
+#[test]
+fn smoke1() {
+ let (s1, r1) = unbounded::<usize>();
+ let (s2, r2) = unbounded::<usize>();
+
+ s1.send(1).unwrap();
+
+ select! {
+ recv(r1) -> v => assert_eq!(v, Ok(1)),
+ recv(r2) -> _ => panic!(),
+ }
+
+ s2.send(2).unwrap();
+
+ select! {
+ recv(r1) -> _ => panic!(),
+ recv(r2) -> v => assert_eq!(v, Ok(2)),
+ }
+}
+
+#[test]
+fn smoke2() {
+ let (_s1, r1) = unbounded::<i32>();
+ let (_s2, r2) = unbounded::<i32>();
+ let (_s3, r3) = unbounded::<i32>();
+ let (_s4, r4) = unbounded::<i32>();
+ let (s5, r5) = unbounded::<i32>();
+
+ s5.send(5).unwrap();
+
+ select! {
+ recv(r1) -> _ => panic!(),
+ recv(r2) -> _ => panic!(),
+ recv(r3) -> _ => panic!(),
+ recv(r4) -> _ => panic!(),
+ recv(r5) -> v => assert_eq!(v, Ok(5)),
+ }
+}
+
+#[test]
+fn disconnected() {
+ let (s1, r1) = unbounded::<i32>();
+ let (s2, r2) = unbounded::<i32>();
+
+ scope(|scope| {
+ scope.spawn(|_| {
+ drop(s1);
+ thread::sleep(ms(500));
+ s2.send(5).unwrap();
+ });
+
+ select! {
+ recv(r1) -> v => assert!(v.is_err()),
+ recv(r2) -> _ => panic!(),
+ default(ms(1000)) => panic!(),
+ }
+
+ r2.recv().unwrap();
+ })
+ .unwrap();
+
+ select! {
+ recv(r1) -> v => assert!(v.is_err()),
+ recv(r2) -> _ => panic!(),
+ default(ms(1000)) => panic!(),
+ }
+
+ scope(|scope| {
+ scope.spawn(|_| {
+ thread::sleep(ms(500));
+ drop(s2);
+ });
+
+ select! {
+ recv(r2) -> v => assert!(v.is_err()),
+ default(ms(1000)) => panic!(),
+ }
+ })
+ .unwrap();
+}
+
+#[test]
+fn default() {
+ let (s1, r1) = unbounded::<i32>();
+ let (s2, r2) = unbounded::<i32>();
+
+ select! {
+ recv(r1) -> _ => panic!(),
+ recv(r2) -> _ => panic!(),
+ default => {}
+ }
+
+ drop(s1);
+
+ select! {
+ recv(r1) -> v => assert!(v.is_err()),
+ recv(r2) -> _ => panic!(),
+ default => panic!(),
+ }
+
+ s2.send(2).unwrap();
+
+ select! {
+ recv(r2) -> v => assert_eq!(v, Ok(2)),
+ default => panic!(),
+ }
+
+ select! {
+ recv(r2) -> _ => panic!(),
+ default => {},
+ }
+
+ select! {
+ default => {},
+ }
+}
+
+#[test]
+fn timeout() {
+ let (_s1, r1) = unbounded::<i32>();
+ let (s2, r2) = unbounded::<i32>();
+
+ scope(|scope| {
+ scope.spawn(|_| {
+ thread::sleep(ms(1500));
+ s2.send(2).unwrap();
+ });
+
+ select! {
+ recv(r1) -> _ => panic!(),
+ recv(r2) -> _ => panic!(),
+ default(ms(1000)) => {},
+ }
+
+ select! {
+ recv(r1) -> _ => panic!(),
+ recv(r2) -> v => assert_eq!(v, Ok(2)),
+ default(ms(1000)) => panic!(),
+ }
+ })
+ .unwrap();
+
+ scope(|scope| {
+ let (s, r) = unbounded::<i32>();
+
+ scope.spawn(move |_| {
+ thread::sleep(ms(500));
+ drop(s);
+ });
+
+ select! {
+ default(ms(1000)) => {
+ select! {
+ recv(r) -> v => assert!(v.is_err()),
+ default => panic!(),
+ }
+ }
+ }
+ })
+ .unwrap();
+}
+
+#[test]
+fn default_when_disconnected() {
+ let (_, r) = unbounded::<i32>();
+
+ select! {
+ recv(r) -> res => assert!(res.is_err()),
+ default => panic!(),
+ }
+
+ let (_, r) = unbounded::<i32>();
+
+ select! {
+ recv(r) -> res => assert!(res.is_err()),
+ default(ms(1000)) => panic!(),
+ }
+
+ let (s, _) = bounded::<i32>(0);
+
+ select! {
+ send(s, 0) -> res => assert!(res.is_err()),
+ default => panic!(),
+ }
+
+ let (s, _) = bounded::<i32>(0);
+
+ select! {
+ send(s, 0) -> res => assert!(res.is_err()),
+ default(ms(1000)) => panic!(),
+ }
+}
+
+#[test]
+fn default_only() {
+ let start = Instant::now();
+ select! {
+ default => {}
+ }
+ let now = Instant::now();
+ assert!(now - start <= ms(50));
+
+ let start = Instant::now();
+ select! {
+ default(ms(500)) => {}
+ }
+ let now = Instant::now();
+ assert!(now - start >= ms(450));
+ assert!(now - start <= ms(550));
+}
+
+#[test]
+fn unblocks() {
+ let (s1, r1) = bounded::<i32>(0);
+ let (s2, r2) = bounded::<i32>(0);
+
+ scope(|scope| {
+ scope.spawn(|_| {
+ thread::sleep(ms(500));
+ s2.send(2).unwrap();
+ });
+
+ select! {
+ recv(r1) -> _ => panic!(),
+ recv(r2) -> v => assert_eq!(v, Ok(2)),
+ default(ms(1000)) => panic!(),
+ }
+ })
+ .unwrap();
+
+ scope(|scope| {
+ scope.spawn(|_| {
+ thread::sleep(ms(500));
+ assert_eq!(r1.recv().unwrap(), 1);
+ });
+
+ select! {
+ send(s1, 1) -> _ => {},
+ send(s2, 2) -> _ => panic!(),
+ default(ms(1000)) => panic!(),
+ }
+ })
+ .unwrap();
+}
+
+#[test]
+fn both_ready() {
+ let (s1, r1) = bounded(0);
+ let (s2, r2) = bounded(0);
+
+ scope(|scope| {
+ scope.spawn(|_| {
+ thread::sleep(ms(500));
+ s1.send(1).unwrap();
+ assert_eq!(r2.recv().unwrap(), 2);
+ });
+
+ for _ in 0..2 {
+ select! {
+ recv(r1) -> v => assert_eq!(v, Ok(1)),
+ send(s2, 2) -> _ => {},
+ }
+ }
+ })
+ .unwrap();
+}
+
+#[test]
+fn loop_try() {
+ const RUNS: usize = 20;
+
+ for _ in 0..RUNS {
+ let (s1, r1) = bounded::<i32>(0);
+ let (s2, r2) = bounded::<i32>(0);
+ let (s_end, r_end) = bounded::<()>(0);
+
+ scope(|scope| {
+ scope.spawn(|_| loop {
+ select! {
+ send(s1, 1) -> _ => break,
+ default => {}
+ }
+
+ select! {
+ recv(r_end) -> _ => break,
+ default => {}
+ }
+ });
+
+ scope.spawn(|_| loop {
+ if let Ok(x) = r2.try_recv() {
+ assert_eq!(x, 2);
+ break;
+ }
+
+ select! {
+ recv(r_end) -> _ => break,
+ default => {}
+ }
+ });
+
+ scope.spawn(|_| {
+ thread::sleep(ms(500));
+
+ select! {
+ recv(r1) -> v => assert_eq!(v, Ok(1)),
+ send(s2, 2) -> _ => {},
+ default(ms(500)) => panic!(),
+ }
+
+ drop(s_end);
+ });
+ })
+ .unwrap();
+ }
+}
+
+#[test]
+fn cloning1() {
+ scope(|scope| {
+ let (s1, r1) = unbounded::<i32>();
+ let (_s2, r2) = unbounded::<i32>();
+ let (s3, r3) = unbounded::<()>();
+
+ scope.spawn(move |_| {
+ r3.recv().unwrap();
+ drop(s1.clone());
+ assert_eq!(r3.try_recv(), Err(TryRecvError::Empty));
+ s1.send(1).unwrap();
+ r3.recv().unwrap();
+ });
+
+ s3.send(()).unwrap();
+
+ select! {
+ recv(r1) -> _ => {},
+ recv(r2) -> _ => {},
+ }
+
+ s3.send(()).unwrap();
+ })
+ .unwrap();
+}
+
+#[test]
+fn cloning2() {
+ let (s1, r1) = unbounded::<()>();
+ let (s2, r2) = unbounded::<()>();
+ let (_s3, _r3) = unbounded::<()>();
+
+ scope(|scope| {
+ scope.spawn(move |_| {
+ select! {
+ recv(r1) -> _ => panic!(),
+ recv(r2) -> _ => {},
+ }
+ });
+
+ thread::sleep(ms(500));
+ drop(s1.clone());
+ s2.send(()).unwrap();
+ })
+ .unwrap();
+}
+
+#[test]
+fn preflight1() {
+ let (s, r) = unbounded();
+ s.send(()).unwrap();
+
+ select! {
+ recv(r) -> _ => {}
+ }
+}
+
+#[test]
+fn preflight2() {
+ let (s, r) = unbounded();
+ drop(s.clone());
+ s.send(()).unwrap();
+ drop(s);
+
+ select! {
+ recv(r) -> v => assert!(v.is_ok()),
+ }
+ assert_eq!(r.try_recv(), Err(TryRecvError::Disconnected));
+}
+
+#[test]
+fn preflight3() {
+ let (s, r) = unbounded();
+ drop(s.clone());
+ s.send(()).unwrap();
+ drop(s);
+ r.recv().unwrap();
+
+ select! {
+ recv(r) -> v => assert!(v.is_err())
+ }
+}
+
+#[test]
+fn duplicate_operations() {
+ let (s, r) = unbounded::<i32>();
+ let mut hit = [false; 4];
+
+ while hit.iter().any(|hit| !hit) {
+ select! {
+ recv(r) -> _ => hit[0] = true,
+ recv(r) -> _ => hit[1] = true,
+ send(s, 0) -> _ => hit[2] = true,
+ send(s, 0) -> _ => hit[3] = true,
+ }
+ }
+}
+
+#[test]
+fn nesting() {
+ let (s, r) = unbounded::<i32>();
+
+ select! {
+ send(s, 0) -> _ => {
+ select! {
+ recv(r) -> v => {
+ assert_eq!(v, Ok(0));
+ select! {
+ send(s, 1) -> _ => {
+ select! {
+ recv(r) -> v => {
+ assert_eq!(v, Ok(1));
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+#[test]
+#[should_panic(expected = "send panicked")]
+fn panic_sender() {
+ fn get() -> Sender<i32> {
+ panic!("send panicked")
+ }
+
+ #[allow(unreachable_code)]
+ {
+ select! {
+ send(get(), panic!()) -> _ => {}
+ }
+ }
+}
+
+#[test]
+#[should_panic(expected = "recv panicked")]
+fn panic_receiver() {
+ fn get() -> Receiver<i32> {
+ panic!("recv panicked")
+ }
+
+ select! {
+ recv(get()) -> _ => {}
+ }
+}
+
+#[test]
+fn stress_recv() {
+ #[cfg(miri)]
+ const COUNT: usize = 50;
+ #[cfg(not(miri))]
+ const COUNT: usize = 10_000;
+
+ let (s1, r1) = unbounded();
+ let (s2, r2) = bounded(5);
+ let (s3, r3) = bounded(100);
+
+ scope(|scope| {
+ scope.spawn(|_| {
+ for i in 0..COUNT {
+ s1.send(i).unwrap();
+ r3.recv().unwrap();
+
+ s2.send(i).unwrap();
+ r3.recv().unwrap();
+ }
+ });
+
+ for i in 0..COUNT {
+ for _ in 0..2 {
+ select! {
+ recv(r1) -> v => assert_eq!(v, Ok(i)),
+ recv(r2) -> v => assert_eq!(v, Ok(i)),
+ }
+
+ s3.send(()).unwrap();
+ }
+ }
+ })
+ .unwrap();
+}
+
+#[test]
+fn stress_send() {
+ #[cfg(miri)]
+ const COUNT: usize = 100;
+ #[cfg(not(miri))]
+ const COUNT: usize = 10_000;
+
+ let (s1, r1) = bounded(0);
+ let (s2, r2) = bounded(0);
+ let (s3, r3) = bounded(100);
+
+ scope(|scope| {
+ scope.spawn(|_| {
+ for i in 0..COUNT {
+ assert_eq!(r1.recv().unwrap(), i);
+ assert_eq!(r2.recv().unwrap(), i);
+ r3.recv().unwrap();
+ }
+ });
+
+ for i in 0..COUNT {
+ for _ in 0..2 {
+ select! {
+ send(s1, i) -> _ => {},
+ send(s2, i) -> _ => {},
+ }
+ }
+ s3.send(()).unwrap();
+ }
+ })
+ .unwrap();
+}
+
+#[test]
+fn stress_mixed() {
+ #[cfg(miri)]
+ const COUNT: usize = 100;
+ #[cfg(not(miri))]
+ const COUNT: usize = 10_000;
+
+ let (s1, r1) = bounded(0);
+ let (s2, r2) = bounded(0);
+ let (s3, r3) = bounded(100);
+
+ scope(|scope| {
+ scope.spawn(|_| {
+ for i in 0..COUNT {
+ s1.send(i).unwrap();
+ assert_eq!(r2.recv().unwrap(), i);
+ r3.recv().unwrap();
+ }
+ });
+
+ for i in 0..COUNT {
+ for _ in 0..2 {
+ select! {
+ recv(r1) -> v => assert_eq!(v, Ok(i)),
+ send(s2, i) -> _ => {},
+ }
+ }
+ s3.send(()).unwrap();
+ }
+ })
+ .unwrap();
+}
+
+#[test]
+fn stress_timeout_two_threads() {
+ const COUNT: usize = 20;
+
+ let (s, r) = bounded(2);
+
+ scope(|scope| {
+ scope.spawn(|_| {
+ for i in 0..COUNT {
+ if i % 2 == 0 {
+ thread::sleep(ms(500));
+ }
+
+ loop {
+ select! {
+ send(s, i) -> _ => break,
+ default(ms(100)) => {}
+ }
+ }
+ }
+ });
+
+ scope.spawn(|_| {
+ for i in 0..COUNT {
+ if i % 2 == 0 {
+ thread::sleep(ms(500));
+ }
+
+ loop {
+ select! {
+ recv(r) -> v => {
+ assert_eq!(v, Ok(i));
+ break;
+ }
+ default(ms(100)) => {}
+ }
+ }
+ }
+ });
+ })
+ .unwrap();
+}
+
+#[test]
+fn send_recv_same_channel() {
+ let (s, r) = bounded::<i32>(0);
+ select! {
+ send(s, 0) -> _ => panic!(),
+ recv(r) -> _ => panic!(),
+ default(ms(500)) => {}
+ }
+
+ let (s, r) = unbounded::<i32>();
+ select! {
+ send(s, 0) -> _ => {},
+ recv(r) -> _ => panic!(),
+ default(ms(500)) => panic!(),
+ }
+}
+
+#[test]
+fn matching() {
+ const THREADS: usize = 44;
+
+ let (s, r) = &bounded::<usize>(0);
+
+ scope(|scope| {
+ for i in 0..THREADS {
+ scope.spawn(move |_| {
+ select! {
+ recv(r) -> v => assert_ne!(v.unwrap(), i),
+ send(s, i) -> _ => {},
+ }
+ });
+ }
+ })
+ .unwrap();
+
+ assert_eq!(r.try_recv(), Err(TryRecvError::Empty));
+}
+
+#[test]
+fn matching_with_leftover() {
+ const THREADS: usize = 55;
+
+ let (s, r) = &bounded::<usize>(0);
+
+ scope(|scope| {
+ for i in 0..THREADS {
+ scope.spawn(move |_| {
+ select! {
+ recv(r) -> v => assert_ne!(v.unwrap(), i),
+ send(s, i) -> _ => {},
+ }
+ });
+ }
+ s.send(!0).unwrap();
+ })
+ .unwrap();
+
+ assert_eq!(r.try_recv(), Err(TryRecvError::Empty));
+}
+
+#[test]
+fn channel_through_channel() {
+ #[cfg(miri)]
+ const COUNT: usize = 100;
+ #[cfg(not(miri))]
+ const COUNT: usize = 1000;
+
+ type T = Box<dyn Any + Send>;
+
+ for cap in 0..3 {
+ let (s, r) = bounded::<T>(cap);
+
+ scope(|scope| {
+ scope.spawn(move |_| {
+ let mut s = s;
+
+ for _ in 0..COUNT {
+ let (new_s, new_r) = bounded(cap);
+ let new_r: T = Box::new(Some(new_r));
+
+ select! {
+ send(s, new_r) -> _ => {}
+ }
+
+ s = new_s;
+ }
+ });
+
+ scope.spawn(move |_| {
+ let mut r = r;
+
+ for _ in 0..COUNT {
+ r = select! {
+ recv(r) -> msg => {
+ msg.unwrap()
+ .downcast_mut::<Option<Receiver<T>>>()
+ .unwrap()
+ .take()
+ .unwrap()
+ }
+ }
+ }
+ });
+ })
+ .unwrap();
+ }
+}
+
+#[test]
+fn linearizable_default() {
+ #[cfg(miri)]
+ const COUNT: usize = 100;
+ #[cfg(not(miri))]
+ const COUNT: usize = 100_000;
+
+ for step in 0..2 {
+ let (start_s, start_r) = bounded::<()>(0);
+ let (end_s, end_r) = bounded::<()>(0);
+
+ let ((s1, r1), (s2, r2)) = if step == 0 {
+ (bounded::<i32>(1), bounded::<i32>(1))
+ } else {
+ (unbounded::<i32>(), unbounded::<i32>())
+ };
+
+ scope(|scope| {
+ scope.spawn(|_| {
+ for _ in 0..COUNT {
+ start_s.send(()).unwrap();
+
+ s1.send(1).unwrap();
+ select! {
+ recv(r1) -> _ => {}
+ recv(r2) -> _ => {}
+ default => unreachable!()
+ }
+
+ end_s.send(()).unwrap();
+ let _ = r2.try_recv();
+ }
+ });
+
+ for _ in 0..COUNT {
+ start_r.recv().unwrap();
+
+ s2.send(1).unwrap();
+ let _ = r1.try_recv();
+
+ end_r.recv().unwrap();
+ }
+ })
+ .unwrap();
+ }
+}
+
+#[test]
+fn linearizable_timeout() {
+ #[cfg(miri)]
+ const COUNT: usize = 100;
+ #[cfg(not(miri))]
+ const COUNT: usize = 100_000;
+
+ for step in 0..2 {
+ let (start_s, start_r) = bounded::<()>(0);
+ let (end_s, end_r) = bounded::<()>(0);
+
+ let ((s1, r1), (s2, r2)) = if step == 0 {
+ (bounded::<i32>(1), bounded::<i32>(1))
+ } else {
+ (unbounded::<i32>(), unbounded::<i32>())
+ };
+
+ scope(|scope| {
+ scope.spawn(|_| {
+ for _ in 0..COUNT {
+ start_s.send(()).unwrap();
+
+ s1.send(1).unwrap();
+ select! {
+ recv(r1) -> _ => {}
+ recv(r2) -> _ => {}
+ default(ms(0)) => unreachable!()
+ }
+
+ end_s.send(()).unwrap();
+ let _ = r2.try_recv();
+ }
+ });
+
+ for _ in 0..COUNT {
+ start_r.recv().unwrap();
+
+ s2.send(1).unwrap();
+ let _ = r1.try_recv();
+
+ end_r.recv().unwrap();
+ }
+ })
+ .unwrap();
+ }
+}
+
+#[test]
+fn fairness1() {
+ #[cfg(miri)]
+ const COUNT: usize = 100;
+ #[cfg(not(miri))]
+ const COUNT: usize = 10_000;
+
+ let (s1, r1) = bounded::<()>(COUNT);
+ let (s2, r2) = unbounded::<()>();
+
+ for _ in 0..COUNT {
+ s1.send(()).unwrap();
+ s2.send(()).unwrap();
+ }
+
+ let mut hits = [0usize; 4];
+ for _ in 0..COUNT {
+ select! {
+ recv(r1) -> _ => hits[0] += 1,
+ recv(r2) -> _ => hits[1] += 1,
+ recv(after(ms(0))) -> _ => hits[2] += 1,
+ recv(tick(ms(0))) -> _ => hits[3] += 1,
+ }
+ }
+ assert!(hits.iter().all(|x| *x >= COUNT / hits.len() / 2));
+}
+
+#[test]
+fn fairness2() {
+ #[cfg(miri)]
+ const COUNT: usize = 100;
+ #[cfg(not(miri))]
+ const COUNT: usize = 10_000;
+
+ let (s1, r1) = unbounded::<()>();
+ let (s2, r2) = bounded::<()>(1);
+ let (s3, r3) = bounded::<()>(0);
+
+ scope(|scope| {
+ scope.spawn(|_| {
+ let (hole, _r) = bounded(0);
+
+ for _ in 0..COUNT {
+ let s1 = if s1.is_empty() { &s1 } else { &hole };
+ let s2 = if s2.is_empty() { &s2 } else { &hole };
+
+ select! {
+ send(s1, ()) -> res => assert!(res.is_ok()),
+ send(s2, ()) -> res => assert!(res.is_ok()),
+ send(s3, ()) -> res => assert!(res.is_ok()),
+ }
+ }
+ });
+
+ let hits = vec![Cell::new(0usize); 3];
+ for _ in 0..COUNT {
+ select! {
+ recv(r1) -> _ => hits[0].set(hits[0].get() + 1),
+ recv(r2) -> _ => hits[1].set(hits[1].get() + 1),
+ recv(r3) -> _ => hits[2].set(hits[2].get() + 1),
+ }
+ }
+ assert!(hits.iter().all(|x| x.get() >= COUNT / hits.len() / 50));
+ })
+ .unwrap();
+}
+
+#[test]
+fn fairness_recv() {
+ #[cfg(miri)]
+ const COUNT: usize = 100;
+ #[cfg(not(miri))]
+ const COUNT: usize = 10_000;
+
+ let (s1, r1) = bounded::<()>(COUNT);
+ let (s2, r2) = unbounded::<()>();
+
+ for _ in 0..COUNT {
+ s1.send(()).unwrap();
+ s2.send(()).unwrap();
+ }
+
+ let mut hits = [0usize; 2];
+ while hits[0] + hits[1] < COUNT {
+ select! {
+ recv(r1) -> _ => hits[0] += 1,
+ recv(r2) -> _ => hits[1] += 1,
+ }
+ }
+ assert!(hits.iter().all(|x| *x >= COUNT / 4));
+}
+
+#[test]
+fn fairness_send() {
+ #[cfg(miri)]
+ const COUNT: usize = 100;
+ #[cfg(not(miri))]
+ const COUNT: usize = 10_000;
+
+ let (s1, _r1) = bounded::<()>(COUNT);
+ let (s2, _r2) = unbounded::<()>();
+
+ let mut hits = [0usize; 2];
+ for _ in 0..COUNT {
+ select! {
+ send(s1, ()) -> _ => hits[0] += 1,
+ send(s2, ()) -> _ => hits[1] += 1,
+ }
+ }
+ assert!(hits.iter().all(|x| *x >= COUNT / 4));
+}
+
+#[allow(clippy::or_fun_call)] // This is intentional.
+#[test]
+fn references() {
+ let (s, r) = unbounded::<i32>();
+ select! {
+ send(s, 0) -> _ => {}
+ recv(r) -> _ => {}
+ }
+ select! {
+ send(&&&&s, 0) -> _ => {}
+ recv(&&&&r) -> _ => {}
+ }
+ select! {
+ recv(Some(&r).unwrap_or(&never())) -> _ => {},
+ default => {}
+ }
+ select! {
+ recv(Some(r).unwrap_or(never())) -> _ => {},
+ default => {}
+ }
+}
+
+#[test]
+fn case_blocks() {
+ let (s, r) = unbounded::<i32>();
+
+ select! {
+ recv(r) -> _ => 3.0,
+ recv(r) -> _ => loop {
+ unreachable!()
+ },
+ recv(r) -> _ => match 7 + 3 {
+ _ => unreachable!()
+ },
+ default => 7.
+ };
+
+ select! {
+ recv(r) -> msg => if msg.is_ok() {
+ unreachable!()
+ },
+ default => ()
+ }
+
+ drop(s);
+}
+
+#[allow(clippy::redundant_closure_call)] // This is intentional.
+#[test]
+fn move_handles() {
+ let (s, r) = unbounded::<i32>();
+ select! {
+ recv((move || r)()) -> _ => {}
+ send((move || s)(), 0) -> _ => {}
+ }
+}
+
+#[test]
+fn infer_types() {
+ let (s, r) = unbounded();
+ select! {
+ recv(r) -> _ => {}
+ default => {}
+ }
+ s.send(()).unwrap();
+
+ let (s, r) = unbounded();
+ select! {
+ send(s, ()) -> _ => {}
+ }
+ r.recv().unwrap();
+}
+
+#[test]
+fn default_syntax() {
+ let (s, r) = bounded::<i32>(0);
+
+ select! {
+ recv(r) -> _ => panic!(),
+ default => {}
+ }
+ select! {
+ send(s, 0) -> _ => panic!(),
+ default() => {}
+ }
+ select! {
+ default => {}
+ }
+ select! {
+ default() => {}
+ }
+}
+
+#[test]
+fn same_variable_name() {
+ let (_, r) = unbounded::<i32>();
+ select! {
+ recv(r) -> r => assert!(r.is_err()),
+ }
+}
+
+#[test]
+fn handles_on_heap() {
+ let (s, r) = unbounded::<i32>();
+ let (s, r) = (Box::new(s), Box::new(r));
+
+ select! {
+ send(*s, 0) -> _ => {}
+ recv(*r) -> _ => {}
+ default => {}
+ }
+
+ drop(s);
+ drop(r);
+}
+
+#[test]
+fn once_blocks() {
+ let (s, r) = unbounded::<i32>();
+
+ let once = Box::new(());
+ select! {
+ send(s, 0) -> _ => drop(once),
+ }
+
+ let once = Box::new(());
+ select! {
+ recv(r) -> _ => drop(once),
+ }
+
+ let once1 = Box::new(());
+ let once2 = Box::new(());
+ select! {
+ send(s, 0) -> _ => drop(once1),
+ default => drop(once2),
+ }
+
+ let once1 = Box::new(());
+ let once2 = Box::new(());
+ select! {
+ recv(r) -> _ => drop(once1),
+ default => drop(once2),
+ }
+
+ let once1 = Box::new(());
+ let once2 = Box::new(());
+ select! {
+ recv(r) -> _ => drop(once1),
+ send(s, 0) -> _ => drop(once2),
+ }
+}
+
+#[test]
+fn once_receiver() {
+ let (_, r) = unbounded::<i32>();
+
+ let once = Box::new(());
+ let get = move || {
+ drop(once);
+ r
+ };
+
+ select! {
+ recv(get()) -> _ => {}
+ }
+}
+
+#[test]
+fn once_sender() {
+ let (s, _) = unbounded::<i32>();
+
+ let once = Box::new(());
+ let get = move || {
+ drop(once);
+ s
+ };
+
+ select! {
+ send(get(), 5) -> _ => {}
+ }
+}
+
+#[test]
+fn parse_nesting() {
+ let (_, r) = unbounded::<i32>();
+
+ select! {
+ recv(r) -> _ => {}
+ recv(r) -> _ => {
+ select! {
+ recv(r) -> _ => {}
+ recv(r) -> _ => {
+ select! {
+ recv(r) -> _ => {}
+ recv(r) -> _ => {
+ select! {
+ default => {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+#[test]
+fn evaluate() {
+ let (s, r) = unbounded::<i32>();
+
+ let v = select! {
+ recv(r) -> _ => "foo".into(),
+ send(s, 0) -> _ => "bar".to_owned(),
+ default => "baz".to_string(),
+ };
+ assert_eq!(v, "bar");
+
+ let v = select! {
+ recv(r) -> _ => "foo".into(),
+ default => "baz".to_string(),
+ };
+ assert_eq!(v, "foo");
+
+ let v = select! {
+ recv(r) -> _ => "foo".into(),
+ default => "baz".to_string(),
+ };
+ assert_eq!(v, "baz");
+}
+
+#[test]
+fn deref() {
+ use crossbeam_channel as cc;
+
+ struct Sender<T>(cc::Sender<T>);
+ struct Receiver<T>(cc::Receiver<T>);
+
+ impl<T> Deref for Receiver<T> {
+ type Target = cc::Receiver<T>;
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+ }
+
+ impl<T> Deref for Sender<T> {
+ type Target = cc::Sender<T>;
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+ }
+
+ let (s, r) = bounded::<i32>(0);
+ let (s, r) = (Sender(s), Receiver(r));
+
+ select! {
+ send(s, 0) -> _ => panic!(),
+ recv(r) -> _ => panic!(),
+ default => {}
+ }
+}
+
+#[test]
+fn result_types() {
+ let (s, _) = bounded::<i32>(0);
+ let (_, r) = bounded::<i32>(0);
+
+ select! {
+ recv(r) -> res => drop::<Result<i32, RecvError>>(res),
+ }
+ select! {
+ recv(r) -> res => drop::<Result<i32, RecvError>>(res),
+ default => {}
+ }
+ select! {
+ recv(r) -> res => drop::<Result<i32, RecvError>>(res),
+ default(ms(0)) => {}
+ }
+
+ select! {
+ send(s, 0) -> res => drop::<Result<(), SendError<i32>>>(res),
+ }
+ select! {
+ send(s, 0) -> res => drop::<Result<(), SendError<i32>>>(res),
+ default => {}
+ }
+ select! {
+ send(s, 0) -> res => drop::<Result<(), SendError<i32>>>(res),
+ default(ms(0)) => {}
+ }
+
+ select! {
+ send(s, 0) -> res => drop::<Result<(), SendError<i32>>>(res),
+ recv(r) -> res => drop::<Result<i32, RecvError>>(res),
+ }
+}
+
+#[test]
+fn try_recv() {
+ let (s, r) = bounded(0);
+
+ scope(|scope| {
+ scope.spawn(move |_| {
+ select! {
+ recv(r) -> _ => panic!(),
+ default => {}
+ }
+ thread::sleep(ms(1500));
+ select! {
+ recv(r) -> v => assert_eq!(v, Ok(7)),
+ default => panic!(),
+ }
+ thread::sleep(ms(500));
+ select! {
+ recv(r) -> v => assert_eq!(v, Err(RecvError)),
+ default => panic!(),
+ }
+ });
+ scope.spawn(move |_| {
+ thread::sleep(ms(1000));
+ select! {
+ send(s, 7) -> res => res.unwrap(),
+ }
+ });
+ })
+ .unwrap();
+}
+
+#[test]
+fn recv() {
+ let (s, r) = bounded(0);
+
+ scope(|scope| {
+ scope.spawn(move |_| {
+ select! {
+ recv(r) -> v => assert_eq!(v, Ok(7)),
+ }
+ thread::sleep(ms(1000));
+ select! {
+ recv(r) -> v => assert_eq!(v, Ok(8)),
+ }
+ thread::sleep(ms(1000));
+ select! {
+ recv(r) -> v => assert_eq!(v, Ok(9)),
+ }
+ select! {
+ recv(r) -> v => assert_eq!(v, Err(RecvError)),
+ }
+ });
+ scope.spawn(move |_| {
+ thread::sleep(ms(1500));
+ select! {
+ send(s, 7) -> res => res.unwrap(),
+ }
+ select! {
+ send(s, 8) -> res => res.unwrap(),
+ }
+ select! {
+ send(s, 9) -> res => res.unwrap(),
+ }
+ });
+ })
+ .unwrap();
+}
+
+#[test]
+fn recv_timeout() {
+ let (s, r) = bounded::<i32>(0);
+
+ scope(|scope| {
+ scope.spawn(move |_| {
+ select! {
+ recv(r) -> _ => panic!(),
+ default(ms(1000)) => {}
+ }
+ select! {
+ recv(r) -> v => assert_eq!(v, Ok(7)),
+ default(ms(1000)) => panic!(),
+ }
+ select! {
+ recv(r) -> v => assert_eq!(v, Err(RecvError)),
+ default(ms(1000)) => panic!(),
+ }
+ });
+ scope.spawn(move |_| {
+ thread::sleep(ms(1500));
+ select! {
+ send(s, 7) -> res => res.unwrap(),
+ }
+ });
+ })
+ .unwrap();
+}
+
+#[test]
+fn try_send() {
+ let (s, r) = bounded(0);
+
+ scope(|scope| {
+ scope.spawn(move |_| {
+ select! {
+ send(s, 7) -> _ => panic!(),
+ default => {}
+ }
+ thread::sleep(ms(1500));
+ select! {
+ send(s, 8) -> res => res.unwrap(),
+ default => panic!(),
+ }
+ thread::sleep(ms(500));
+ select! {
+ send(s, 8) -> res => assert_eq!(res, Err(SendError(8))),
+ default => panic!(),
+ }
+ });
+ scope.spawn(move |_| {
+ thread::sleep(ms(1000));
+ select! {
+ recv(r) -> v => assert_eq!(v, Ok(8)),
+ }
+ });
+ })
+ .unwrap();
+}
+
+#[test]
+fn send() {
+ let (s, r) = bounded(0);
+
+ scope(|scope| {
+ scope.spawn(move |_| {
+ select! {
+ send(s, 7) -> res => res.unwrap(),
+ }
+ thread::sleep(ms(1000));
+ select! {
+ send(s, 8) -> res => res.unwrap(),
+ }
+ thread::sleep(ms(1000));
+ select! {
+ send(s, 9) -> res => res.unwrap(),
+ }
+ });
+ scope.spawn(move |_| {
+ thread::sleep(ms(1500));
+ select! {
+ recv(r) -> v => assert_eq!(v, Ok(7)),
+ }
+ select! {
+ recv(r) -> v => assert_eq!(v, Ok(8)),
+ }
+ select! {
+ recv(r) -> v => assert_eq!(v, Ok(9)),
+ }
+ });
+ })
+ .unwrap();
+}
+
+#[test]
+fn send_timeout() {
+ let (s, r) = bounded(0);
+
+ scope(|scope| {
+ scope.spawn(move |_| {
+ select! {
+ send(s, 7) -> _ => panic!(),
+ default(ms(1000)) => {}
+ }
+ select! {
+ send(s, 8) -> res => res.unwrap(),
+ default(ms(1000)) => panic!(),
+ }
+ select! {
+ send(s, 9) -> res => assert_eq!(res, Err(SendError(9))),
+ default(ms(1000)) => panic!(),
+ }
+ });
+ scope.spawn(move |_| {
+ thread::sleep(ms(1500));
+ select! {
+ recv(r) -> v => assert_eq!(v, Ok(8)),
+ }
+ });
+ })
+ .unwrap();
+}
+
+#[test]
+fn disconnect_wakes_sender() {
+ let (s, r) = bounded(0);
+
+ scope(|scope| {
+ scope.spawn(move |_| {
+ select! {
+ send(s, ()) -> res => assert_eq!(res, Err(SendError(()))),
+ }
+ });
+ scope.spawn(move |_| {
+ thread::sleep(ms(1000));
+ drop(r);
+ });
+ })
+ .unwrap();
+}
+
+#[test]
+fn disconnect_wakes_receiver() {
+ let (s, r) = bounded::<()>(0);
+
+ scope(|scope| {
+ scope.spawn(move |_| {
+ select! {
+ recv(r) -> res => assert_eq!(res, Err(RecvError)),
+ }
+ });
+ scope.spawn(move |_| {
+ thread::sleep(ms(1000));
+ drop(s);
+ });
+ })
+ .unwrap();
+}
+
+#[test]
+fn trailing_comma() {
+ let (s, r) = unbounded::<usize>();
+
+ select! {
+ send(s, 1,) -> _ => {},
+ recv(r,) -> _ => {},
+ default(ms(1000),) => {},
+ }
+}