From b481ecd8b51eba60c7223cef2453a0a9d4c554c0 Mon Sep 17 00:00:00 2001 From: Connor Tsui Date: Tue, 29 Jul 2025 11:11:18 +0200 Subject: [PATCH] add `oneshot` tests Tests inspired by tests in the `oneshot` third-party crate. --- library/std/tests/sync/lib.rs | 3 + library/std/tests/sync/oneshot.rs | 342 ++++++++++++++++++++++++++++++ 2 files changed, 345 insertions(+) create mode 100644 library/std/tests/sync/oneshot.rs diff --git a/library/std/tests/sync/lib.rs b/library/std/tests/sync/lib.rs index 7ade9f623147..32a7efde2a25 100644 --- a/library/std/tests/sync/lib.rs +++ b/library/std/tests/sync/lib.rs @@ -1,5 +1,6 @@ #![feature(mapped_lock_guards)] #![feature(mpmc_channel)] +#![feature(oneshot_channel)] #![feature(once_cell_try)] #![feature(lock_value_accessors)] #![feature(reentrant_lock)] @@ -26,6 +27,8 @@ mod mutex; mod once; mod once_lock; #[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] +mod oneshot; +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] mod reentrant_lock; #[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] mod rwlock; diff --git a/library/std/tests/sync/oneshot.rs b/library/std/tests/sync/oneshot.rs new file mode 100644 index 000000000000..6a87c72b9cb5 --- /dev/null +++ b/library/std/tests/sync/oneshot.rs @@ -0,0 +1,342 @@ +//! Inspired by tests from + +use std::sync::mpsc::RecvError; +use std::sync::oneshot; +use std::sync::oneshot::{RecvTimeoutError, TryRecvError}; +use std::time::{Duration, Instant}; +use std::{mem, thread}; + +#[test] +fn send_before_try_recv() { + let (sender, receiver) = oneshot::channel(); + + assert!(sender.send(19i128).is_ok()); + + match receiver.try_recv() { + Ok(19) => {} + _ => panic!("expected Ok(19)"), + } +} + +#[test] +fn send_before_recv() { + let (sender, receiver) = oneshot::channel::<()>(); + + assert!(sender.send(()).is_ok()); + assert_eq!(receiver.recv(), Ok(())); + + let (sender, receiver) = oneshot::channel::(); + + assert!(sender.send(42).is_ok()); + assert_eq!(receiver.recv(), Ok(42)); + + let (sender, receiver) = oneshot::channel::<[u8; 4096]>(); + + assert!(sender.send([0b10101010; 4096]).is_ok()); + assert!(receiver.recv().unwrap()[..] == [0b10101010; 4096][..]); +} + +#[test] +fn sender_drop() { + { + let (sender, receiver) = oneshot::channel::(); + + mem::drop(sender); + + match receiver.recv() { + Err(RecvError) => {} + _ => panic!("expected recv error"), + } + } + + { + let (sender, receiver) = oneshot::channel::(); + + mem::drop(sender); + + match receiver.try_recv() { + Err(TryRecvError::Disconnected) => {} + _ => panic!("expected disconnected error"), + } + } + { + let (sender, receiver) = oneshot::channel::(); + + mem::drop(sender); + + match receiver.recv_timeout(Duration::from_secs(1)) { + Err(RecvTimeoutError::Disconnected) => {} + _ => panic!("expected disconnected error"), + } + } +} + +#[test] +fn send_never_deadline() { + let (sender, receiver) = oneshot::channel::(); + + mem::drop(sender); + + match receiver.recv_deadline(Instant::now()) { + Err(RecvTimeoutError::Disconnected) => {} + _ => panic!("expected disconnected error"), + } +} + +#[test] +fn send_before_recv_timeout() { + let (sender, receiver) = oneshot::channel(); + + assert!(sender.send(22i128).is_ok()); + + let start = Instant::now(); + + let timeout = Duration::from_secs(1); + match receiver.recv_timeout(timeout) { + Ok(22) => {} + _ => panic!("expected Ok(22)"), + } + + assert!(start.elapsed() < timeout); +} + +#[test] +fn send_error() { + let (sender, receiver) = oneshot::channel(); + + mem::drop(receiver); + + let send_error = sender.send(32u128).unwrap_err(); + assert_eq!(send_error.0, 32); +} + +#[test] +fn recv_before_send() { + let (sender, receiver) = oneshot::channel(); + + let t1 = thread::spawn(move || { + thread::sleep(Duration::from_millis(10)); + sender.send(9u128).unwrap(); + }); + let t2 = thread::spawn(move || { + assert_eq!(receiver.recv(), Ok(9)); + }); + + t1.join().unwrap(); + t2.join().unwrap(); +} + +#[test] +fn recv_timeout_before_send() { + let (sender, receiver) = oneshot::channel(); + + let t = thread::spawn(move || { + thread::sleep(Duration::from_millis(100)); + sender.send(99u128).unwrap(); + }); + + match receiver.recv_timeout(Duration::from_secs(1)) { + Ok(99) => {} + _ => panic!("expected Ok(99)"), + } + + t.join().unwrap(); +} + +#[test] +fn recv_then_drop_sender() { + let (sender, receiver) = oneshot::channel::(); + + let t1 = thread::spawn(move || match receiver.recv() { + Err(RecvError) => {} + _ => panic!("expected recv error"), + }); + + let t2 = thread::spawn(move || { + thread::sleep(Duration::from_millis(10)); + mem::drop(sender); + }); + + t1.join().unwrap(); + t2.join().unwrap(); +} + +#[test] +fn drop_sender_then_recv() { + let (sender, receiver) = oneshot::channel::(); + + let t1 = thread::spawn(move || { + thread::sleep(Duration::from_millis(10)); + mem::drop(sender); + }); + + let t2 = thread::spawn(move || match receiver.recv() { + Err(RecvError) => {} + _ => panic!("expected disconnected error"), + }); + + t1.join().unwrap(); + t2.join().unwrap(); +} + +#[test] +fn try_recv_empty() { + let (sender, receiver) = oneshot::channel::(); + match receiver.try_recv() { + Err(TryRecvError::Empty(_)) => {} + _ => panic!("expected empty error"), + } + mem::drop(sender); +} + +#[test] +fn try_recv_then_drop_receiver() { + let (sender, receiver) = oneshot::channel::(); + + let t1 = thread::spawn(move || { + thread::sleep(Duration::from_millis(100)); + let _ = sender.send(42); + }); + + let t2 = thread::spawn(move || match receiver.try_recv() { + Ok(_) => {} + Err(TryRecvError::Empty(r)) => { + mem::drop(r); + } + Err(TryRecvError::Disconnected) => {} + }); + + t2.join().unwrap(); + t1.join().unwrap(); +} + +#[test] +fn recv_no_time() { + let (_sender, receiver) = oneshot::channel::(); + + let start = Instant::now(); + match receiver.recv_deadline(start) { + Err(RecvTimeoutError::Timeout(_)) => {} + _ => panic!("expected timeout error"), + } + + let (_sender, receiver) = oneshot::channel::(); + match receiver.recv_timeout(Duration::from_millis(0)) { + Err(RecvTimeoutError::Timeout(_)) => {} + _ => panic!("expected timeout error"), + } +} + +#[test] +fn recv_deadline_passed() { + let (_sender, receiver) = oneshot::channel::(); + + let start = Instant::now(); + let timeout = Duration::from_millis(100); + + match receiver.recv_deadline(start + timeout) { + Err(RecvTimeoutError::Timeout(_)) => {} + _ => panic!("expected timeout error"), + } + + assert!(start.elapsed() >= timeout); + assert!(start.elapsed() < timeout * 3); +} + +#[test] +fn recv_time_passed() { + let (_sender, receiver) = oneshot::channel::(); + + let start = Instant::now(); + let timeout = Duration::from_millis(100); + match receiver.recv_timeout(timeout) { + Err(RecvTimeoutError::Timeout(_)) => {} + _ => panic!("expected timeout error"), + } + assert!(start.elapsed() >= timeout); + assert!(start.elapsed() < timeout * 3); +} + +#[test] +fn non_send_type_can_be_used_on_same_thread() { + use std::ptr; + + #[derive(Debug, Eq, PartialEq)] + struct NotSend(*mut ()); + + let (sender, receiver) = oneshot::channel(); + sender.send(NotSend(ptr::null_mut())).unwrap(); + let reply = receiver.try_recv().unwrap(); + assert_eq!(reply, NotSend(ptr::null_mut())); +} + +/// Helper for testing drop behavior (taken directly from the `oneshot` crate). +struct DropCounter { + count: std::rc::Rc>, +} + +impl DropCounter { + fn new() -> (DropTracker, DropCounter) { + let count = std::rc::Rc::new(std::cell::RefCell::new(0)); + (DropTracker { count: count.clone() }, DropCounter { count }) + } + + fn count(&self) -> usize { + *self.count.borrow() + } +} + +struct DropTracker { + count: std::rc::Rc>, +} + +impl Drop for DropTracker { + fn drop(&mut self) { + *self.count.borrow_mut() += 1; + } +} + +#[test] +fn message_in_channel_dropped_on_receiver_drop() { + let (sender, receiver) = oneshot::channel(); + + let (message, counter) = DropCounter::new(); + assert_eq!(counter.count(), 0); + + sender.send(message).unwrap(); + assert_eq!(counter.count(), 0); + + mem::drop(receiver); + assert_eq!(counter.count(), 1); +} + +#[test] +fn send_error_drops_message_correctly() { + let (sender, receiver) = oneshot::channel(); + mem::drop(receiver); + + let (message, counter) = DropCounter::new(); + + let send_error = sender.send(message).unwrap_err(); + assert_eq!(counter.count(), 0); + + mem::drop(send_error); + assert_eq!(counter.count(), 1); +} + +#[test] +fn send_error_drops_message_correctly_on_extract() { + let (sender, receiver) = oneshot::channel(); + mem::drop(receiver); + + let (message, counter) = DropCounter::new(); + + let send_error = sender.send(message).unwrap_err(); + assert_eq!(counter.count(), 0); + + let message = send_error.0; // Access the inner value directly + assert_eq!(counter.count(), 0); + + mem::drop(message); + assert_eq!(counter.count(), 1); +}