tokio-test-0.4.4/.cargo_vcs_info.json0000644000000001500000000000100131370ustar { "git": { "sha1": "3d0d0fd2af9192ca5cf2836451e96dffab68216a" }, "path_in_vcs": "tokio-test" }tokio-test-0.4.4/CHANGELOG.md000064400000000000000000000025751046102023000135550ustar 00000000000000# 0.4.4 (March 14, 2024) - task: mark `Spawn` as `#[must_use]` ([#6371]) - test: increase MSRV to 1.63 ([#6126]) - test: update category slug ([#5953]) [#5953]: https://github.com/tokio-rs/tokio/pull/5953 [#6126]: https://github.com/tokio-rs/tokio/pull/6126 [#6371]: https://github.com/tokio-rs/tokio/pull/6371 # 0.4.3 (August 23, 2023) - deps: fix minimum required version of `async-stream` ([#5347]) - deps: fix minimum required version of `tokio-stream` ([#4376]) - docs: improve `tokio_test::task` docs ([#5132]) - io: fetch actions from mock handle before write ([#5814]) - io: fix wait operation on mock ([#5554]) [#4376]: https://github.com/tokio-rs/tokio/pull/4376 [#5132]: https://github.com/tokio-rs/tokio/pull/5132 [#5347]: https://github.com/tokio-rs/tokio/pull/5347 [#5554]: https://github.com/tokio-rs/tokio/pull/5554 [#5814]: https://github.com/tokio-rs/tokio/pull/5814 # 0.4.2 (May 14, 2021) - test: add `assert_elapsed!` macro ([#3728]) [#3728]: https://github.com/tokio-rs/tokio/pull/3728 # 0.4.1 (March 10, 2021) - Fix `io::Mock` to be `Send` and `Sync` ([#3594]) [#3594]: https://github.com/tokio-rs/tokio/pull/3594 # 0.4.0 (December 23, 2020) - Track `tokio` 1.0 release. # 0.3.0 (October 15, 2020) - Track `tokio` 0.3 release. # 0.2.1 (April 17, 2020) - Add `Future` and `Stream` implementations for `task::Spawn`. # 0.2.0 (November 25, 2019) - Initial release tokio-test-0.4.4/Cargo.toml0000644000000024420000000000100111430ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.63" name = "tokio-test" version = "0.4.4" authors = ["Tokio Contributors "] description = """ Testing utilities for Tokio- and futures-based code """ homepage = "https://tokio.rs" readme = "README.md" categories = [ "asynchronous", "development-tools::testing", ] license = "MIT" repository = "https://github.com/tokio-rs/tokio" [package.metadata.docs.rs] all-features = true [dependencies.async-stream] version = "0.3.3" [dependencies.bytes] version = "1.0.0" [dependencies.futures-core] version = "0.3.0" [dependencies.tokio] version = "1.2.0" features = [ "rt", "sync", "time", "test-util", ] [dependencies.tokio-stream] version = "0.1.1" [dev-dependencies.futures-util] version = "0.3.0" [dev-dependencies.tokio] version = "1.2.0" features = ["full"] tokio-test-0.4.4/Cargo.toml.orig000064400000000000000000000015711046102023000146260ustar 00000000000000[package] name = "tokio-test" # When releasing to crates.io: # - Remove path dependencies # - Update CHANGELOG.md. # - Create "tokio-test-0.4.x" git tag. version = "0.4.4" edition = "2021" rust-version = "1.63" authors = ["Tokio Contributors "] license = "MIT" repository = "https://github.com/tokio-rs/tokio" homepage = "https://tokio.rs" description = """ Testing utilities for Tokio- and futures-based code """ categories = ["asynchronous", "development-tools::testing"] [dependencies] tokio = { version = "1.2.0", path = "../tokio", features = ["rt", "sync", "time", "test-util"] } tokio-stream = { version = "0.1.1", path = "../tokio-stream" } async-stream = "0.3.3" bytes = "1.0.0" futures-core = "0.3.0" [dev-dependencies] tokio = { version = "1.2.0", path = "../tokio", features = ["full"] } futures-util = "0.3.0" [package.metadata.docs.rs] all-features = true tokio-test-0.4.4/LICENSE000064400000000000000000000020461046102023000127420ustar 00000000000000Copyright (c) 2023 Tokio Contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. tokio-test-0.4.4/README.md000064400000000000000000000005101046102023000132060ustar 00000000000000# tokio-test Tokio and Futures based testing utilities ## License This project is licensed under the [MIT license](LICENSE). ### Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in Tokio by you, shall be licensed as MIT, without any additional terms or conditions. tokio-test-0.4.4/src/io.rs000064400000000000000000000361471046102023000135120ustar 00000000000000#![cfg(not(loom))] //! A mock type implementing [`AsyncRead`] and [`AsyncWrite`]. //! //! //! # Overview //! //! Provides a type that implements [`AsyncRead`] + [`AsyncWrite`] that can be configured //! to handle an arbitrary sequence of read and write operations. This is useful //! for writing unit tests for networking services as using an actual network //! type is fairly non deterministic. //! //! # Usage //! //! Attempting to write data that the mock isn't expecting will result in a //! panic. //! //! [`AsyncRead`]: tokio::io::AsyncRead //! [`AsyncWrite`]: tokio::io::AsyncWrite use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; use tokio::sync::mpsc; use tokio::time::{self, Duration, Instant, Sleep}; use tokio_stream::wrappers::UnboundedReceiverStream; use futures_core::{ready, Stream}; use std::collections::VecDeque; use std::fmt; use std::future::Future; use std::pin::Pin; use std::sync::Arc; use std::task::{self, Poll, Waker}; use std::{cmp, io}; /// An I/O object that follows a predefined script. /// /// This value is created by `Builder` and implements `AsyncRead` + `AsyncWrite`. It /// follows the scenario described by the builder and panics otherwise. #[derive(Debug)] pub struct Mock { inner: Inner, } /// A handle to send additional actions to the related `Mock`. #[derive(Debug)] pub struct Handle { tx: mpsc::UnboundedSender, } /// Builds `Mock` instances. #[derive(Debug, Clone, Default)] pub struct Builder { // Sequence of actions for the Mock to take actions: VecDeque, } #[derive(Debug, Clone)] enum Action { Read(Vec), Write(Vec), Wait(Duration), // Wrapped in Arc so that Builder can be cloned and Send. // Mock is not cloned as does not need to check Rc for ref counts. ReadError(Option>), WriteError(Option>), } struct Inner { actions: VecDeque, waiting: Option, sleep: Option>>, read_wait: Option, rx: UnboundedReceiverStream, } impl Builder { /// Return a new, empty `Builder`. pub fn new() -> Self { Self::default() } /// Sequence a `read` operation. /// /// The next operation in the mock's script will be to expect a `read` call /// and return `buf`. pub fn read(&mut self, buf: &[u8]) -> &mut Self { self.actions.push_back(Action::Read(buf.into())); self } /// Sequence a `read` operation that produces an error. /// /// The next operation in the mock's script will be to expect a `read` call /// and return `error`. pub fn read_error(&mut self, error: io::Error) -> &mut Self { let error = Some(error.into()); self.actions.push_back(Action::ReadError(error)); self } /// Sequence a `write` operation. /// /// The next operation in the mock's script will be to expect a `write` /// call. pub fn write(&mut self, buf: &[u8]) -> &mut Self { self.actions.push_back(Action::Write(buf.into())); self } /// Sequence a `write` operation that produces an error. /// /// The next operation in the mock's script will be to expect a `write` /// call that provides `error`. pub fn write_error(&mut self, error: io::Error) -> &mut Self { let error = Some(error.into()); self.actions.push_back(Action::WriteError(error)); self } /// Sequence a wait. /// /// The next operation in the mock's script will be to wait without doing so /// for `duration` amount of time. pub fn wait(&mut self, duration: Duration) -> &mut Self { let duration = cmp::max(duration, Duration::from_millis(1)); self.actions.push_back(Action::Wait(duration)); self } /// Build a `Mock` value according to the defined script. pub fn build(&mut self) -> Mock { let (mock, _) = self.build_with_handle(); mock } /// Build a `Mock` value paired with a handle pub fn build_with_handle(&mut self) -> (Mock, Handle) { let (inner, handle) = Inner::new(self.actions.clone()); let mock = Mock { inner }; (mock, handle) } } impl Handle { /// Sequence a `read` operation. /// /// The next operation in the mock's script will be to expect a `read` call /// and return `buf`. pub fn read(&mut self, buf: &[u8]) -> &mut Self { self.tx.send(Action::Read(buf.into())).unwrap(); self } /// Sequence a `read` operation error. /// /// The next operation in the mock's script will be to expect a `read` call /// and return `error`. pub fn read_error(&mut self, error: io::Error) -> &mut Self { let error = Some(error.into()); self.tx.send(Action::ReadError(error)).unwrap(); self } /// Sequence a `write` operation. /// /// The next operation in the mock's script will be to expect a `write` /// call. pub fn write(&mut self, buf: &[u8]) -> &mut Self { self.tx.send(Action::Write(buf.into())).unwrap(); self } /// Sequence a `write` operation error. /// /// The next operation in the mock's script will be to expect a `write` /// call error. pub fn write_error(&mut self, error: io::Error) -> &mut Self { let error = Some(error.into()); self.tx.send(Action::WriteError(error)).unwrap(); self } } impl Inner { fn new(actions: VecDeque) -> (Inner, Handle) { let (tx, rx) = mpsc::unbounded_channel(); let rx = UnboundedReceiverStream::new(rx); let inner = Inner { actions, sleep: None, read_wait: None, rx, waiting: None, }; let handle = Handle { tx }; (inner, handle) } fn poll_action(&mut self, cx: &mut task::Context<'_>) -> Poll> { Pin::new(&mut self.rx).poll_next(cx) } fn read(&mut self, dst: &mut ReadBuf<'_>) -> io::Result<()> { match self.action() { Some(&mut Action::Read(ref mut data)) => { // Figure out how much to copy let n = cmp::min(dst.remaining(), data.len()); // Copy the data into the `dst` slice dst.put_slice(&data[..n]); // Drain the data from the source data.drain(..n); Ok(()) } Some(&mut Action::ReadError(ref mut err)) => { // As the let err = err.take().expect("Should have been removed from actions."); let err = Arc::try_unwrap(err).expect("There are no other references."); Err(err) } Some(_) => { // Either waiting or expecting a write Err(io::ErrorKind::WouldBlock.into()) } None => Ok(()), } } fn write(&mut self, mut src: &[u8]) -> io::Result { let mut ret = 0; if self.actions.is_empty() { return Err(io::ErrorKind::BrokenPipe.into()); } if let Some(&mut Action::Wait(..)) = self.action() { return Err(io::ErrorKind::WouldBlock.into()); } if let Some(&mut Action::WriteError(ref mut err)) = self.action() { let err = err.take().expect("Should have been removed from actions."); let err = Arc::try_unwrap(err).expect("There are no other references."); return Err(err); } for i in 0..self.actions.len() { match self.actions[i] { Action::Write(ref mut expect) => { let n = cmp::min(src.len(), expect.len()); assert_eq!(&src[..n], &expect[..n]); // Drop data that was matched expect.drain(..n); src = &src[n..]; ret += n; if src.is_empty() { return Ok(ret); } } Action::Wait(..) | Action::WriteError(..) => { break; } _ => {} } // TODO: remove write } Ok(ret) } fn remaining_wait(&mut self) -> Option { match self.action() { Some(&mut Action::Wait(dur)) => Some(dur), _ => None, } } fn action(&mut self) -> Option<&mut Action> { loop { if self.actions.is_empty() { return None; } match self.actions[0] { Action::Read(ref mut data) => { if !data.is_empty() { break; } } Action::Write(ref mut data) => { if !data.is_empty() { break; } } Action::Wait(ref mut dur) => { if let Some(until) = self.waiting { let now = Instant::now(); if now < until { break; } else { self.waiting = None; } } else { self.waiting = Some(Instant::now() + *dur); break; } } Action::ReadError(ref mut error) | Action::WriteError(ref mut error) => { if error.is_some() { break; } } } let _action = self.actions.pop_front(); } self.actions.front_mut() } } // ===== impl Inner ===== impl Mock { fn maybe_wakeup_reader(&mut self) { match self.inner.action() { Some(&mut Action::Read(_)) | Some(&mut Action::ReadError(_)) | None => { if let Some(waker) = self.inner.read_wait.take() { waker.wake(); } } _ => {} } } } impl AsyncRead for Mock { fn poll_read( mut self: Pin<&mut Self>, cx: &mut task::Context<'_>, buf: &mut ReadBuf<'_>, ) -> Poll> { loop { if let Some(ref mut sleep) = self.inner.sleep { ready!(Pin::new(sleep).poll(cx)); } // If a sleep is set, it has already fired self.inner.sleep = None; // Capture 'filled' to monitor if it changed let filled = buf.filled().len(); match self.inner.read(buf) { Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { if let Some(rem) = self.inner.remaining_wait() { let until = Instant::now() + rem; self.inner.sleep = Some(Box::pin(time::sleep_until(until))); } else { self.inner.read_wait = Some(cx.waker().clone()); return Poll::Pending; } } Ok(()) => { if buf.filled().len() == filled { match ready!(self.inner.poll_action(cx)) { Some(action) => { self.inner.actions.push_back(action); continue; } None => { return Poll::Ready(Ok(())); } } } else { return Poll::Ready(Ok(())); } } Err(e) => return Poll::Ready(Err(e)), } } } } impl AsyncWrite for Mock { fn poll_write( mut self: Pin<&mut Self>, cx: &mut task::Context<'_>, buf: &[u8], ) -> Poll> { loop { if let Some(ref mut sleep) = self.inner.sleep { ready!(Pin::new(sleep).poll(cx)); } // If a sleep is set, it has already fired self.inner.sleep = None; if self.inner.actions.is_empty() { match self.inner.poll_action(cx) { Poll::Pending => { // do not propagate pending } Poll::Ready(Some(action)) => { self.inner.actions.push_back(action); } Poll::Ready(None) => { panic!("unexpected write"); } } } match self.inner.write(buf) { Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { if let Some(rem) = self.inner.remaining_wait() { let until = Instant::now() + rem; self.inner.sleep = Some(Box::pin(time::sleep_until(until))); } else { panic!("unexpected WouldBlock"); } } Ok(0) => { // TODO: Is this correct? if !self.inner.actions.is_empty() { return Poll::Pending; } // TODO: Extract match ready!(self.inner.poll_action(cx)) { Some(action) => { self.inner.actions.push_back(action); continue; } None => { panic!("unexpected write"); } } } ret => { self.maybe_wakeup_reader(); return Poll::Ready(ret); } } } } fn poll_flush(self: Pin<&mut Self>, _cx: &mut task::Context<'_>) -> Poll> { Poll::Ready(Ok(())) } fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut task::Context<'_>) -> Poll> { Poll::Ready(Ok(())) } } /// Ensures that Mock isn't dropped with data "inside". impl Drop for Mock { fn drop(&mut self) { // Avoid double panicking, since makes debugging much harder. if std::thread::panicking() { return; } self.inner.actions.iter().for_each(|a| match a { Action::Read(data) => assert!(data.is_empty(), "There is still data left to read."), Action::Write(data) => assert!(data.is_empty(), "There is still data left to write."), _ => (), }); } } /* /// Returns `true` if called from the context of a futures-rs Task fn is_task_ctx() -> bool { use std::panic; // Save the existing panic hook let h = panic::take_hook(); // Install a new one that does nothing panic::set_hook(Box::new(|_| {})); // Attempt to call the fn let r = panic::catch_unwind(|| task::current()).is_ok(); // Re-install the old one panic::set_hook(h); // Return the result r } */ impl fmt::Debug for Inner { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Inner {{...}}") } } tokio-test-0.4.4/src/lib.rs000064400000000000000000000015521046102023000136410ustar 00000000000000#![warn( missing_debug_implementations, missing_docs, rust_2018_idioms, unreachable_pub )] #![doc(test( no_crate_inject, attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables)) ))] //! Tokio and Futures based testing utilities pub mod io; pub mod stream_mock; mod macros; pub mod task; /// Runs the provided future, blocking the current thread until the /// future completes. /// /// For more information, see the documentation for /// [`tokio::runtime::Runtime::block_on`][runtime-block-on]. /// /// [runtime-block-on]: https://docs.rs/tokio/1.3.0/tokio/runtime/struct.Runtime.html#method.block_on pub fn block_on(future: F) -> F::Output { use tokio::runtime; let rt = runtime::Builder::new_current_thread() .enable_all() .build() .unwrap(); rt.block_on(future) } tokio-test-0.4.4/src/macros.rs000064400000000000000000000174641046102023000143700ustar 00000000000000//! A collection of useful macros for testing futures and tokio based code /// Asserts a `Poll` is ready, returning the value. /// /// This will invoke `panic!` if the provided `Poll` does not evaluate to `Poll::Ready` at /// runtime. /// /// # Custom Messages /// /// This macro has a second form, where a custom panic message can be provided with or without /// arguments for formatting. /// /// # Examples /// /// ``` /// use futures_util::future; /// use tokio_test::{assert_ready, task}; /// /// let mut fut = task::spawn(future::ready(())); /// assert_ready!(fut.poll()); /// ``` #[macro_export] macro_rules! assert_ready { ($e:expr) => {{ use core::task::Poll; match $e { Poll::Ready(v) => v, Poll::Pending => panic!("pending"), } }}; ($e:expr, $($msg:tt)+) => {{ use core::task::Poll; match $e { Poll::Ready(v) => v, Poll::Pending => { panic!("pending; {}", format_args!($($msg)+)) } } }}; } /// Asserts a `Poll>` is ready and `Ok`, returning the value. /// /// This will invoke `panic!` if the provided `Poll` does not evaluate to `Poll::Ready(Ok(..))` at /// runtime. /// /// # Custom Messages /// /// This macro has a second form, where a custom panic message can be provided with or without /// arguments for formatting. /// /// # Examples /// /// ``` /// use futures_util::future; /// use tokio_test::{assert_ready_ok, task}; /// /// let mut fut = task::spawn(future::ok::<_, ()>(())); /// assert_ready_ok!(fut.poll()); /// ``` #[macro_export] macro_rules! assert_ready_ok { ($e:expr) => {{ use tokio_test::{assert_ready, assert_ok}; let val = assert_ready!($e); assert_ok!(val) }}; ($e:expr, $($msg:tt)+) => {{ use tokio_test::{assert_ready, assert_ok}; let val = assert_ready!($e, $($msg)*); assert_ok!(val, $($msg)*) }}; } /// Asserts a `Poll>` is ready and `Err`, returning the error. /// /// This will invoke `panic!` if the provided `Poll` does not evaluate to `Poll::Ready(Err(..))` at /// runtime. /// /// # Custom Messages /// /// This macro has a second form, where a custom panic message can be provided with or without /// arguments for formatting. /// /// # Examples /// /// ``` /// use futures_util::future; /// use tokio_test::{assert_ready_err, task}; /// /// let mut fut = task::spawn(future::err::<(), _>(())); /// assert_ready_err!(fut.poll()); /// ``` #[macro_export] macro_rules! assert_ready_err { ($e:expr) => {{ use tokio_test::{assert_ready, assert_err}; let val = assert_ready!($e); assert_err!(val) }}; ($e:expr, $($msg:tt)+) => {{ use tokio_test::{assert_ready, assert_err}; let val = assert_ready!($e, $($msg)*); assert_err!(val, $($msg)*) }}; } /// Asserts a `Poll` is pending. /// /// This will invoke `panic!` if the provided `Poll` does not evaluate to `Poll::Pending` at /// runtime. /// /// # Custom Messages /// /// This macro has a second form, where a custom panic message can be provided with or without /// arguments for formatting. /// /// # Examples /// /// ``` /// use futures_util::future; /// use tokio_test::{assert_pending, task}; /// /// let mut fut = task::spawn(future::pending::<()>()); /// assert_pending!(fut.poll()); /// ``` #[macro_export] macro_rules! assert_pending { ($e:expr) => {{ use core::task::Poll; match $e { Poll::Pending => {} Poll::Ready(v) => panic!("ready; value = {:?}", v), } }}; ($e:expr, $($msg:tt)+) => {{ use core::task::Poll; match $e { Poll::Pending => {} Poll::Ready(v) => { panic!("ready; value = {:?}; {}", v, format_args!($($msg)+)) } } }}; } /// Asserts if a poll is ready and check for equality on the value /// /// This will invoke `panic!` if the provided `Poll` does not evaluate to `Poll::Ready` at /// runtime and the value produced does not partially equal the expected value. /// /// # Custom Messages /// /// This macro has a second form, where a custom panic message can be provided with or without /// arguments for formatting. /// /// # Examples /// /// ``` /// use futures_util::future; /// use tokio_test::{assert_ready_eq, task}; /// /// let mut fut = task::spawn(future::ready(42)); /// assert_ready_eq!(fut.poll(), 42); /// ``` #[macro_export] macro_rules! assert_ready_eq { ($e:expr, $expect:expr) => { let val = $crate::assert_ready!($e); assert_eq!(val, $expect) }; ($e:expr, $expect:expr, $($msg:tt)+) => { let val = $crate::assert_ready!($e, $($msg)*); assert_eq!(val, $expect, $($msg)*) }; } /// Asserts that the expression evaluates to `Ok` and returns the value. /// /// This will invoke the `panic!` macro if the provided expression does not evaluate to `Ok` at /// runtime. /// /// # Custom Messages /// /// This macro has a second form, where a custom panic message can be provided with or without /// arguments for formatting. /// /// # Examples /// /// ``` /// use tokio_test::assert_ok; /// /// let n: u32 = assert_ok!("123".parse()); /// /// let s = "123"; /// let n: u32 = assert_ok!(s.parse(), "testing parsing {:?} as a u32", s); /// ``` #[macro_export] macro_rules! assert_ok { ($e:expr) => { assert_ok!($e,) }; ($e:expr,) => {{ use std::result::Result::*; match $e { Ok(v) => v, Err(e) => panic!("assertion failed: Err({:?})", e), } }}; ($e:expr, $($arg:tt)+) => {{ use std::result::Result::*; match $e { Ok(v) => v, Err(e) => panic!("assertion failed: Err({:?}): {}", e, format_args!($($arg)+)), } }}; } /// Asserts that the expression evaluates to `Err` and returns the error. /// /// This will invoke the `panic!` macro if the provided expression does not evaluate to `Err` at /// runtime. /// /// # Custom Messages /// /// This macro has a second form, where a custom panic message can be provided with or without /// arguments for formatting. /// /// # Examples /// /// ``` /// use tokio_test::assert_err; /// use std::str::FromStr; /// /// /// let err = assert_err!(u32::from_str("fail")); /// /// let msg = "fail"; /// let err = assert_err!(u32::from_str(msg), "testing parsing {:?} as u32", msg); /// ``` #[macro_export] macro_rules! assert_err { ($e:expr) => { assert_err!($e,); }; ($e:expr,) => {{ use std::result::Result::*; match $e { Ok(v) => panic!("assertion failed: Ok({:?})", v), Err(e) => e, } }}; ($e:expr, $($arg:tt)+) => {{ use std::result::Result::*; match $e { Ok(v) => panic!("assertion failed: Ok({:?}): {}", v, format_args!($($arg)+)), Err(e) => e, } }}; } /// Asserts that an exact duration has elapsed since the start instant ±1ms. /// /// ```rust /// use tokio::time::{self, Instant}; /// use std::time::Duration; /// use tokio_test::assert_elapsed; /// # async fn test_time_passed() { /// /// let start = Instant::now(); /// let dur = Duration::from_millis(50); /// time::sleep(dur).await; /// assert_elapsed!(start, dur); /// # } /// ``` /// /// This 1ms buffer is required because Tokio's hashed-wheel timer has finite time resolution and /// will not always sleep for the exact interval. #[macro_export] macro_rules! assert_elapsed { ($start:expr, $dur:expr) => {{ let elapsed = $start.elapsed(); // type ascription improves compiler error when wrong type is passed let lower: std::time::Duration = $dur; // Handles ms rounding assert!( elapsed >= lower && elapsed <= lower + std::time::Duration::from_millis(1), "actual = {:?}, expected = {:?}", elapsed, lower ); }}; } tokio-test-0.4.4/src/stream_mock.rs000064400000000000000000000111151046102023000153730ustar 00000000000000#![cfg(not(loom))] //! A mock stream implementing [`Stream`]. //! //! # Overview //! This crate provides a `StreamMock` that can be used to test code that interacts with streams. //! It allows you to mock the behavior of a stream and control the items it yields and the waiting //! intervals between items. //! //! # Usage //! To use the `StreamMock`, you need to create a builder using [`StreamMockBuilder`]. The builder //! allows you to enqueue actions such as returning items or waiting for a certain duration. //! //! # Example //! ```rust //! //! use futures_util::StreamExt; //! use std::time::Duration; //! use tokio_test::stream_mock::StreamMockBuilder; //! //! async fn test_stream_mock_wait() { //! let mut stream_mock = StreamMockBuilder::new() //! .next(1) //! .wait(Duration::from_millis(300)) //! .next(2) //! .build(); //! //! assert_eq!(stream_mock.next().await, Some(1)); //! let start = std::time::Instant::now(); //! assert_eq!(stream_mock.next().await, Some(2)); //! let elapsed = start.elapsed(); //! assert!(elapsed >= Duration::from_millis(300)); //! assert_eq!(stream_mock.next().await, None); //! } //! ``` use std::collections::VecDeque; use std::pin::Pin; use std::task::Poll; use std::time::Duration; use futures_core::{ready, Stream}; use std::future::Future; use tokio::time::{sleep_until, Instant, Sleep}; #[derive(Debug, Clone)] enum Action { Next(T), Wait(Duration), } /// A builder for [`StreamMock`] #[derive(Debug, Clone)] pub struct StreamMockBuilder { actions: VecDeque>, } impl StreamMockBuilder { /// Create a new empty [`StreamMockBuilder`] pub fn new() -> Self { StreamMockBuilder::default() } /// Queue an item to be returned by the stream pub fn next(mut self, value: T) -> Self { self.actions.push_back(Action::Next(value)); self } // Queue an item to be consumed by the sink, // commented out until Sink is implemented. // // pub fn consume(mut self, value: T) -> Self { // self.actions.push_back(Action::Consume(value)); // self // } /// Queue the stream to wait for a duration pub fn wait(mut self, duration: Duration) -> Self { self.actions.push_back(Action::Wait(duration)); self } /// Build the [`StreamMock`] pub fn build(self) -> StreamMock { StreamMock { actions: self.actions, sleep: None, } } } impl Default for StreamMockBuilder { fn default() -> Self { StreamMockBuilder { actions: VecDeque::new(), } } } /// A mock stream implementing [`Stream`] /// /// See [`StreamMockBuilder`] for more information. #[derive(Debug)] pub struct StreamMock { actions: VecDeque>, sleep: Option>>, } impl StreamMock { fn next_action(&mut self) -> Option> { self.actions.pop_front() } } impl Stream for StreamMock { type Item = T; fn poll_next( mut self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>, ) -> std::task::Poll> { // Try polling the sleep future first if let Some(ref mut sleep) = self.sleep { ready!(Pin::new(sleep).poll(cx)); // Since we're ready, discard the sleep future self.sleep.take(); } match self.next_action() { Some(action) => match action { Action::Next(item) => Poll::Ready(Some(item)), Action::Wait(duration) => { // Set up a sleep future and schedule this future to be polled again for it. self.sleep = Some(Box::pin(sleep_until(Instant::now() + duration))); cx.waker().wake_by_ref(); Poll::Pending } }, None => Poll::Ready(None), } } } impl Drop for StreamMock { fn drop(&mut self) { // Avoid double panicking to make debugging easier. if std::thread::panicking() { return; } let undropped_count = self .actions .iter() .filter(|action| match action { Action::Next(_) => true, Action::Wait(_) => false, }) .count(); assert!( undropped_count == 0, "StreamMock was dropped before all actions were consumed, {} actions were not consumed", undropped_count ); } } tokio-test-0.4.4/src/task.rs000064400000000000000000000160231046102023000140340ustar 00000000000000//! Futures task based helpers to easily test futures and manually written futures. //! //! The [`Spawn`] type is used as a mock task harness that allows you to poll futures //! without needing to setup pinning or context. Any future can be polled but if the //! future requires the tokio async context you will need to ensure that you poll the //! [`Spawn`] within a tokio context, this means that as long as you are inside the //! runtime it will work and you can poll it via [`Spawn`]. //! //! [`Spawn`] also supports [`Stream`] to call `poll_next` without pinning //! or context. //! //! In addition to circumventing the need for pinning and context, [`Spawn`] also tracks //! the amount of times the future/task was woken. This can be useful to track if some //! leaf future notified the root task correctly. //! //! # Example //! //! ``` //! use tokio_test::task; //! //! let fut = async {}; //! //! let mut task = task::spawn(fut); //! //! assert!(task.poll().is_ready(), "Task was not ready!"); //! ``` use std::future::Future; use std::mem; use std::ops; use std::pin::Pin; use std::sync::{Arc, Condvar, Mutex}; use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker}; use tokio_stream::Stream; /// Spawn a future into a [`Spawn`] which wraps the future in a mocked executor. /// /// This can be used to spawn a [`Future`] or a [`Stream`]. /// /// For more information, check the module docs. pub fn spawn(task: T) -> Spawn { Spawn { task: MockTask::new(), future: Box::pin(task), } } /// Future spawned on a mock task that can be used to poll the future or stream /// without needing pinning or context types. #[derive(Debug)] #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct Spawn { task: MockTask, future: Pin>, } #[derive(Debug, Clone)] struct MockTask { waker: Arc, } #[derive(Debug)] struct ThreadWaker { state: Mutex, condvar: Condvar, } const IDLE: usize = 0; const WAKE: usize = 1; const SLEEP: usize = 2; impl Spawn { /// Consumes `self` returning the inner value pub fn into_inner(self) -> T where T: Unpin, { *Pin::into_inner(self.future) } /// Returns `true` if the inner future has received a wake notification /// since the last call to `enter`. pub fn is_woken(&self) -> bool { self.task.is_woken() } /// Returns the number of references to the task waker /// /// The task itself holds a reference. The return value will never be zero. pub fn waker_ref_count(&self) -> usize { self.task.waker_ref_count() } /// Enter the task context pub fn enter(&mut self, f: F) -> R where F: FnOnce(&mut Context<'_>, Pin<&mut T>) -> R, { let fut = self.future.as_mut(); self.task.enter(|cx| f(cx, fut)) } } impl ops::Deref for Spawn { type Target = T; fn deref(&self) -> &T { &self.future } } impl ops::DerefMut for Spawn { fn deref_mut(&mut self) -> &mut T { &mut self.future } } impl Spawn { /// If `T` is a [`Future`] then poll it. This will handle pinning and the context /// type for the future. pub fn poll(&mut self) -> Poll { let fut = self.future.as_mut(); self.task.enter(|cx| fut.poll(cx)) } } impl Spawn { /// If `T` is a [`Stream`] then `poll_next` it. This will handle pinning and the context /// type for the stream. pub fn poll_next(&mut self) -> Poll> { let stream = self.future.as_mut(); self.task.enter(|cx| stream.poll_next(cx)) } } impl Future for Spawn { type Output = T::Output; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { self.future.as_mut().poll(cx) } } impl Stream for Spawn { type Item = T::Item; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.future.as_mut().poll_next(cx) } } impl MockTask { /// Creates new mock task fn new() -> Self { MockTask { waker: Arc::new(ThreadWaker::new()), } } /// Runs a closure from the context of the task. /// /// Any wake notifications resulting from the execution of the closure are /// tracked. fn enter(&mut self, f: F) -> R where F: FnOnce(&mut Context<'_>) -> R, { self.waker.clear(); let waker = self.waker(); let mut cx = Context::from_waker(&waker); f(&mut cx) } /// Returns `true` if the inner future has received a wake notification /// since the last call to `enter`. fn is_woken(&self) -> bool { self.waker.is_woken() } /// Returns the number of references to the task waker /// /// The task itself holds a reference. The return value will never be zero. fn waker_ref_count(&self) -> usize { Arc::strong_count(&self.waker) } fn waker(&self) -> Waker { unsafe { let raw = to_raw(self.waker.clone()); Waker::from_raw(raw) } } } impl Default for MockTask { fn default() -> Self { Self::new() } } impl ThreadWaker { fn new() -> Self { ThreadWaker { state: Mutex::new(IDLE), condvar: Condvar::new(), } } /// Clears any previously received wakes, avoiding potential spurious /// wake notifications. This should only be called immediately before running the /// task. fn clear(&self) { *self.state.lock().unwrap() = IDLE; } fn is_woken(&self) -> bool { match *self.state.lock().unwrap() { IDLE => false, WAKE => true, _ => unreachable!(), } } fn wake(&self) { // First, try transitioning from IDLE -> NOTIFY, this does not require a lock. let mut state = self.state.lock().unwrap(); let prev = *state; if prev == WAKE { return; } *state = WAKE; if prev == IDLE { return; } // The other half is sleeping, so we wake it up. assert_eq!(prev, SLEEP); self.condvar.notify_one(); } } static VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake_by_ref, drop_waker); unsafe fn to_raw(waker: Arc) -> RawWaker { RawWaker::new(Arc::into_raw(waker) as *const (), &VTABLE) } unsafe fn from_raw(raw: *const ()) -> Arc { Arc::from_raw(raw as *const ThreadWaker) } unsafe fn clone(raw: *const ()) -> RawWaker { let waker = from_raw(raw); // Increment the ref count mem::forget(waker.clone()); to_raw(waker) } unsafe fn wake(raw: *const ()) { let waker = from_raw(raw); waker.wake(); } unsafe fn wake_by_ref(raw: *const ()) { let waker = from_raw(raw); waker.wake(); // We don't actually own a reference to the unparker mem::forget(waker); } unsafe fn drop_waker(raw: *const ()) { let _ = from_raw(raw); } tokio-test-0.4.4/tests/block_on.rs000064400000000000000000000006621046102023000152350ustar 00000000000000#![warn(rust_2018_idioms)] use tokio::time::{sleep_until, Duration, Instant}; use tokio_test::block_on; #[test] fn async_block() { assert_eq!(4, block_on(async { 4 })); } async fn five() -> u8 { 5 } #[test] fn async_fn() { assert_eq!(5, block_on(five())); } #[test] fn test_sleep() { let deadline = Instant::now() + Duration::from_millis(100); block_on(async { sleep_until(deadline).await; }); } tokio-test-0.4.4/tests/io.rs000064400000000000000000000113711046102023000140550ustar 00000000000000#![warn(rust_2018_idioms)] use std::io; use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio::time::{Duration, Instant}; use tokio_test::io::Builder; #[tokio::test] async fn read() { let mut mock = Builder::new().read(b"hello ").read(b"world!").build(); let mut buf = [0; 256]; let n = mock.read(&mut buf).await.expect("read 1"); assert_eq!(&buf[..n], b"hello "); let n = mock.read(&mut buf).await.expect("read 2"); assert_eq!(&buf[..n], b"world!"); } #[tokio::test] async fn read_error() { let error = io::Error::new(io::ErrorKind::Other, "cruel"); let mut mock = Builder::new() .read(b"hello ") .read_error(error) .read(b"world!") .build(); let mut buf = [0; 256]; let n = mock.read(&mut buf).await.expect("read 1"); assert_eq!(&buf[..n], b"hello "); match mock.read(&mut buf).await { Err(error) => { assert_eq!(error.kind(), io::ErrorKind::Other); assert_eq!("cruel", format!("{}", error)); } Ok(_) => panic!("error not received"), } let n = mock.read(&mut buf).await.expect("read 1"); assert_eq!(&buf[..n], b"world!"); } #[tokio::test] async fn write() { let mut mock = Builder::new().write(b"hello ").write(b"world!").build(); mock.write_all(b"hello ").await.expect("write 1"); mock.write_all(b"world!").await.expect("write 2"); } #[tokio::test] async fn write_with_handle() { let (mut mock, mut handle) = Builder::new().build_with_handle(); handle.write(b"hello "); handle.write(b"world!"); mock.write_all(b"hello ").await.expect("write 1"); mock.write_all(b"world!").await.expect("write 2"); } #[tokio::test] async fn read_with_handle() { let (mut mock, mut handle) = Builder::new().build_with_handle(); handle.read(b"hello "); handle.read(b"world!"); let mut buf = vec![0; 6]; mock.read_exact(&mut buf).await.expect("read 1"); assert_eq!(&buf[..], b"hello "); mock.read_exact(&mut buf).await.expect("read 2"); assert_eq!(&buf[..], b"world!"); } #[tokio::test] async fn write_error() { let error = io::Error::new(io::ErrorKind::Other, "cruel"); let mut mock = Builder::new() .write(b"hello ") .write_error(error) .write(b"world!") .build(); mock.write_all(b"hello ").await.expect("write 1"); match mock.write_all(b"whoa").await { Err(error) => { assert_eq!(error.kind(), io::ErrorKind::Other); assert_eq!("cruel", format!("{}", error)); } Ok(_) => panic!("error not received"), } mock.write_all(b"world!").await.expect("write 2"); } #[tokio::test] #[should_panic] async fn mock_panics_read_data_left() { use tokio_test::io::Builder; Builder::new().read(b"read").build(); } #[tokio::test] #[should_panic] async fn mock_panics_write_data_left() { use tokio_test::io::Builder; Builder::new().write(b"write").build(); } #[tokio::test(start_paused = true)] async fn wait() { const FIRST_WAIT: Duration = Duration::from_secs(1); let mut mock = Builder::new() .wait(FIRST_WAIT) .read(b"hello ") .read(b"world!") .build(); let mut buf = [0; 256]; let start = Instant::now(); // record the time the read call takes // let n = mock.read(&mut buf).await.expect("read 1"); assert_eq!(&buf[..n], b"hello "); println!("time elapsed after first read {:?}", start.elapsed()); let n = mock.read(&mut buf).await.expect("read 2"); assert_eq!(&buf[..n], b"world!"); println!("time elapsed after second read {:?}", start.elapsed()); // make sure the .wait() instruction worked assert!( start.elapsed() >= FIRST_WAIT, "consuming the whole mock only took {}ms", start.elapsed().as_millis() ); } #[tokio::test(start_paused = true)] async fn multiple_wait() { const FIRST_WAIT: Duration = Duration::from_secs(1); const SECOND_WAIT: Duration = Duration::from_secs(1); let mut mock = Builder::new() .wait(FIRST_WAIT) .read(b"hello ") .wait(SECOND_WAIT) .read(b"world!") .build(); let mut buf = [0; 256]; let start = Instant::now(); // record the time it takes to consume the mock let n = mock.read(&mut buf).await.expect("read 1"); assert_eq!(&buf[..n], b"hello "); println!("time elapsed after first read {:?}", start.elapsed()); let n = mock.read(&mut buf).await.expect("read 2"); assert_eq!(&buf[..n], b"world!"); println!("time elapsed after second read {:?}", start.elapsed()); // make sure the .wait() instruction worked assert!( start.elapsed() >= FIRST_WAIT + SECOND_WAIT, "consuming the whole mock only took {}ms", start.elapsed().as_millis() ); } tokio-test-0.4.4/tests/macros.rs000064400000000000000000000040401046102023000147250ustar 00000000000000#![warn(rust_2018_idioms)] use std::task::Poll; use tokio_test::{ assert_pending, assert_ready, assert_ready_eq, assert_ready_err, assert_ready_ok, }; fn ready() -> Poll<()> { Poll::Ready(()) } fn ready_ok() -> Poll> { Poll::Ready(Ok(())) } fn ready_err() -> Poll> { Poll::Ready(Err(())) } fn pending() -> Poll<()> { Poll::Pending } #[derive(Debug)] enum Test { Data, } #[test] fn assert_ready() { let poll = ready(); assert_ready!(poll); assert_ready!(poll, "some message"); assert_ready!(poll, "{:?}", ()); assert_ready!(poll, "{:?}", Test::Data); } #[test] #[should_panic] fn assert_ready_on_pending() { let poll = pending(); assert_ready!(poll); } #[test] fn assert_pending() { let poll = pending(); assert_pending!(poll); assert_pending!(poll, "some message"); assert_pending!(poll, "{:?}", ()); assert_pending!(poll, "{:?}", Test::Data); } #[test] #[should_panic] fn assert_pending_on_ready() { let poll = ready(); assert_pending!(poll); } #[test] fn assert_ready_ok() { let poll = ready_ok(); assert_ready_ok!(poll); assert_ready_ok!(poll, "some message"); assert_ready_ok!(poll, "{:?}", ()); assert_ready_ok!(poll, "{:?}", Test::Data); } #[test] #[should_panic] fn assert_ok_on_err() { let poll = ready_err(); assert_ready_ok!(poll); } #[test] fn assert_ready_err() { let poll = ready_err(); assert_ready_err!(poll); assert_ready_err!(poll, "some message"); assert_ready_err!(poll, "{:?}", ()); assert_ready_err!(poll, "{:?}", Test::Data); } #[test] #[should_panic] fn assert_err_on_ok() { let poll = ready_ok(); assert_ready_err!(poll); } #[test] fn assert_ready_eq() { let poll = ready(); assert_ready_eq!(poll, ()); assert_ready_eq!(poll, (), "some message"); assert_ready_eq!(poll, (), "{:?}", ()); assert_ready_eq!(poll, (), "{:?}", Test::Data); } #[test] #[should_panic] fn assert_eq_on_not_eq() { let poll = ready_err(); assert_ready_eq!(poll, Ok(())); } tokio-test-0.4.4/tests/stream_mock.rs000064400000000000000000000030661046102023000157540ustar 00000000000000use futures_util::StreamExt; use std::time::Duration; use tokio_test::stream_mock::StreamMockBuilder; #[tokio::test] async fn test_stream_mock_empty() { let mut stream_mock = StreamMockBuilder::::new().build(); assert_eq!(stream_mock.next().await, None); assert_eq!(stream_mock.next().await, None); } #[tokio::test] async fn test_stream_mock_items() { let mut stream_mock = StreamMockBuilder::new().next(1).next(2).build(); assert_eq!(stream_mock.next().await, Some(1)); assert_eq!(stream_mock.next().await, Some(2)); assert_eq!(stream_mock.next().await, None); } #[tokio::test] async fn test_stream_mock_wait() { let mut stream_mock = StreamMockBuilder::new() .next(1) .wait(Duration::from_millis(300)) .next(2) .build(); assert_eq!(stream_mock.next().await, Some(1)); let start = std::time::Instant::now(); assert_eq!(stream_mock.next().await, Some(2)); let elapsed = start.elapsed(); assert!(elapsed >= Duration::from_millis(300)); assert_eq!(stream_mock.next().await, None); } #[tokio::test] #[should_panic(expected = "StreamMock was dropped before all actions were consumed")] async fn test_stream_mock_drop_without_consuming_all() { let stream_mock = StreamMockBuilder::new().next(1).next(2).build(); drop(stream_mock); } #[tokio::test] #[should_panic(expected = "test panic was not masked")] async fn test_stream_mock_drop_during_panic_doesnt_mask_panic() { let _stream_mock = StreamMockBuilder::new().next(1).next(2).build(); panic!("test panic was not masked"); }