mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-08-25 00:00:18 +02:00
Introduce tokio-test crate (#1030)
This commit is contained in:
committed by
Carl Lerche
parent
62f34e15ce
commit
e5cf0cc717
@@ -12,6 +12,7 @@ members = [
|
||||
"tokio-reactor",
|
||||
"tokio-signal",
|
||||
"tokio-sync",
|
||||
"tokio-test",
|
||||
"tokio-threadpool",
|
||||
"tokio-timer",
|
||||
"tokio-tcp",
|
||||
|
||||
@@ -45,6 +45,7 @@ jobs:
|
||||
- tokio-sync
|
||||
- tokio-threadpool
|
||||
- tokio-timer
|
||||
- tokio-test
|
||||
- tokio-trace
|
||||
- tokio-trace/tokio-trace-core
|
||||
- tokio-trace/test-log-support
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
[package]
|
||||
name = "tokio-test"
|
||||
|
||||
# When releasing to crates.io:
|
||||
# - Update html_root_url.
|
||||
# - Update doc url
|
||||
# - Cargo.toml
|
||||
# - README.md
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "v0.1.x" git tag.
|
||||
version = "0.1.0"
|
||||
authors = ["Tokio Contributors <[email protected]>"]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/tokio-rs/tokio"
|
||||
homepage = "https://tokio.rs"
|
||||
documentation = "https://docs.rs/tokio-test/0.1.0/tokio_test"
|
||||
description = """
|
||||
Testing utilities for Tokio- and futures-based code
|
||||
"""
|
||||
categories = ["asynchronous", "testing"]
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
futures = "0.1"
|
||||
tokio-timer = "0.2"
|
||||
tokio-executor = "0.1"
|
||||
@@ -0,0 +1,25 @@
|
||||
Copyright (c) 2019 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.
|
||||
@@ -0,0 +1,36 @@
|
||||
# tokio-test
|
||||
|
||||
Tokio and Futures based testing utilities
|
||||
|
||||
[Documenation](https://docs.rs/tokio-test)
|
||||
|
||||
## Usage
|
||||
|
||||
First, add this to your `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
[dev-dependencies]
|
||||
tokio-test = "0.1.0"
|
||||
```
|
||||
|
||||
Next, add this to your crate:
|
||||
|
||||
```rust
|
||||
#[macro_use]
|
||||
extern crate tokio_test;
|
||||
```
|
||||
|
||||
You can find extensive documentation and examples about how to use this crate
|
||||
online at [https://tokio.rs](https://tokio.rs). The [API
|
||||
documentation](https://docs.rs/tokio-test) is also a great place to get started
|
||||
for the nitty-gritty.
|
||||
|
||||
## 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.
|
||||
@@ -0,0 +1,256 @@
|
||||
//! A mocked clock for use with `tokio_timer` based futures.
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! ```
|
||||
//! # #[macro_use] extern crate tokio_test;
|
||||
//! # extern crate futures;
|
||||
//! # extern crate tokio_timer;
|
||||
//! # use tokio_test::clock;
|
||||
//! # use tokio_timer::Delay;
|
||||
//! # use std::time::Duration;
|
||||
//! # use futures::Future;
|
||||
//! clock::mock(|handle| {
|
||||
//! let mut delay = Delay::new(handle.now() + Duration::from_secs(1));
|
||||
//!
|
||||
//! assert_not_ready!(delay.poll());
|
||||
//!
|
||||
//! handle.advance(Duration::from_secs(1));
|
||||
//!
|
||||
//! assert_ready!(delay.poll());
|
||||
//! });
|
||||
//! ```
|
||||
|
||||
use futures::{future::lazy, Future};
|
||||
use std::marker::PhantomData;
|
||||
use std::rc::Rc;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::{Duration, Instant};
|
||||
use tokio_executor::park::{Park, Unpark};
|
||||
use tokio_timer::clock::{Clock, Now};
|
||||
use tokio_timer::Timer;
|
||||
|
||||
/// Run the provided closure with a `MockClock` that starts at the current time.
|
||||
pub fn mock<F, R>(f: F) -> R
|
||||
where
|
||||
F: FnOnce(&mut Handle) -> R,
|
||||
{
|
||||
let mut mock = MockClock::new();
|
||||
mock.enter(f)
|
||||
}
|
||||
|
||||
/// Run the provided closure with a `MockClock` that starts at the provided `Instant`.
|
||||
pub fn mock_at<F, R>(instant: Instant, f: F) -> R
|
||||
where
|
||||
F: FnOnce(&mut Handle) -> R,
|
||||
{
|
||||
let mut mock = MockClock::with_instant(instant);
|
||||
mock.enter(f)
|
||||
}
|
||||
|
||||
/// Mock clock for use with `tokio-timer` futures.
|
||||
///
|
||||
/// A mock timer that is able to advance and wake after a
|
||||
/// certain duration.
|
||||
#[derive(Debug)]
|
||||
pub struct MockClock {
|
||||
time: MockTime,
|
||||
clock: Clock,
|
||||
}
|
||||
|
||||
/// A handle to the `MockClock`.
|
||||
#[derive(Debug)]
|
||||
pub struct Handle {
|
||||
timer: Timer<MockPark>,
|
||||
time: MockTime,
|
||||
}
|
||||
|
||||
type Inner = Arc<Mutex<State>>;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct MockTime {
|
||||
inner: Inner,
|
||||
_pd: PhantomData<Rc<()>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct MockNow {
|
||||
inner: Inner,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct MockPark {
|
||||
inner: Inner,
|
||||
_pd: PhantomData<Rc<()>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct MockUnpark {
|
||||
inner: Inner,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct State {
|
||||
base: Instant,
|
||||
advance: Duration,
|
||||
unparked: bool,
|
||||
park_for: Option<Duration>,
|
||||
}
|
||||
|
||||
impl MockClock {
|
||||
/// Create a new `MockClock` with the current time.
|
||||
pub fn new() -> Self {
|
||||
MockClock::with_instant(Instant::now())
|
||||
}
|
||||
|
||||
/// Create a `MockClock` with its current time at a duration from now
|
||||
///
|
||||
/// This will create a clock with `Instant::now() + duration` as the current time.
|
||||
pub fn with_duration(duration: Duration) -> Self {
|
||||
let instant = Instant::now() + duration;
|
||||
MockClock::with_instant(instant)
|
||||
}
|
||||
|
||||
/// Create a `MockClock` that sets its current time as the `Instant` provided.
|
||||
pub fn with_instant(instant: Instant) -> Self {
|
||||
let time = MockTime::new(instant);
|
||||
let clock = Clock::new_with_now(time.mock_now());
|
||||
|
||||
MockClock { time, clock }
|
||||
}
|
||||
|
||||
/// Enter the `MockClock` context.
|
||||
pub fn enter<F, R>(&mut self, f: F) -> R
|
||||
where
|
||||
F: FnOnce(&mut Handle) -> R,
|
||||
{
|
||||
let mut enter = ::tokio_executor::enter().unwrap();
|
||||
|
||||
::tokio_timer::clock::with_default(&self.clock, &mut enter, |enter| {
|
||||
let park = self.time.mock_park();
|
||||
let timer = Timer::new(park);
|
||||
let handle = timer.handle();
|
||||
let time = self.time.clone();
|
||||
|
||||
::tokio_timer::with_default(&handle, enter, |_| {
|
||||
let mut handle = Handle::new(timer, time);
|
||||
lazy(|| Ok::<_, ()>(f(&mut handle))).wait().unwrap()
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Handle {
|
||||
pub(self) fn new(timer: Timer<MockPark>, time: MockTime) -> Self {
|
||||
Handle { timer, time }
|
||||
}
|
||||
|
||||
/// Turn the internal timer and mock park for the provided duration.
|
||||
pub fn turn(&mut self, duration: Option<Duration>) {
|
||||
self.timer.turn(duration).unwrap();
|
||||
}
|
||||
|
||||
/// Advance the `MockClock` by the provided duration.
|
||||
pub fn advance(&mut self, duration: Duration) {
|
||||
let inner = self.timer.get_park().inner.clone();
|
||||
let deadline = inner.lock().unwrap().now() + duration;
|
||||
|
||||
while inner.lock().unwrap().now() < deadline {
|
||||
let dur = deadline - inner.lock().unwrap().now();
|
||||
self.turn(Some(dur));
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the currently mocked time
|
||||
pub fn now(&mut self) -> Instant {
|
||||
self.time.now()
|
||||
}
|
||||
}
|
||||
|
||||
impl MockTime {
|
||||
pub(crate) fn new(now: Instant) -> MockTime {
|
||||
let state = State {
|
||||
base: now,
|
||||
advance: Duration::default(),
|
||||
unparked: false,
|
||||
park_for: None,
|
||||
};
|
||||
|
||||
MockTime {
|
||||
inner: Arc::new(Mutex::new(state)),
|
||||
_pd: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn mock_now(&self) -> MockNow {
|
||||
let inner = self.inner.clone();
|
||||
MockNow { inner }
|
||||
}
|
||||
|
||||
pub(crate) fn mock_park(&self) -> MockPark {
|
||||
let inner = self.inner.clone();
|
||||
MockPark {
|
||||
inner,
|
||||
_pd: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn now(&self) -> Instant {
|
||||
self.inner.lock().unwrap().now()
|
||||
}
|
||||
}
|
||||
|
||||
impl State {
|
||||
fn now(&self) -> Instant {
|
||||
self.base + self.advance
|
||||
}
|
||||
|
||||
fn advance(&mut self, duration: Duration) {
|
||||
self.advance += duration;
|
||||
}
|
||||
}
|
||||
|
||||
impl Park for MockPark {
|
||||
type Unpark = MockUnpark;
|
||||
type Error = ();
|
||||
|
||||
fn unpark(&self) -> Self::Unpark {
|
||||
let inner = self.inner.clone();
|
||||
MockUnpark { inner }
|
||||
}
|
||||
|
||||
fn park(&mut self) -> Result<(), Self::Error> {
|
||||
let mut inner = self.inner.lock().map_err(|_| ())?;
|
||||
|
||||
let duration = inner.park_for.take().expect("call park_for first");
|
||||
|
||||
inner.advance(duration);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn park_timeout(&mut self, duration: Duration) -> Result<(), Self::Error> {
|
||||
let mut inner = self.inner.lock().unwrap();
|
||||
|
||||
if let Some(duration) = inner.park_for.take() {
|
||||
inner.advance(duration);
|
||||
} else {
|
||||
inner.advance(duration);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Unpark for MockUnpark {
|
||||
fn unpark(&self) {
|
||||
if let Ok(mut inner) = self.inner.lock() {
|
||||
inner.unparked = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Now for MockNow {
|
||||
fn now(&self) -> Instant {
|
||||
self.inner.lock().unwrap().now()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#![doc(html_root_url = "https://docs.rs/tokio-test/0.1.0")]
|
||||
#![deny(missing_docs, missing_debug_implementations, unreachable_pub)]
|
||||
#![cfg_attr(test, deny(warnings))]
|
||||
|
||||
//! Tokio and Futures based testing utilites
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! ```
|
||||
//! # extern crate futures;
|
||||
//! # #[macro_use] extern crate tokio_test;
|
||||
//! # use futures::{Future, future};
|
||||
//! let mut fut = future::ok::<(), ()>(());
|
||||
//! assert_ready!(fut.poll());
|
||||
//! ```
|
||||
|
||||
extern crate futures;
|
||||
extern crate tokio_executor;
|
||||
extern crate tokio_timer;
|
||||
|
||||
pub mod clock;
|
||||
mod macros;
|
||||
pub mod task;
|
||||
@@ -0,0 +1,125 @@
|
||||
//! A collection of useful macros for testing futures and tokio based code
|
||||
|
||||
/// Assert if a poll is ready
|
||||
#[macro_export]
|
||||
macro_rules! assert_ready {
|
||||
($e:expr) => {{
|
||||
match $e {
|
||||
Ok(::futures::Async::Ready(v)) => v,
|
||||
Ok(_) => panic!("not ready"),
|
||||
Err(e) => panic!("error = {:?}", e),
|
||||
}
|
||||
}};
|
||||
($e:expr, $($msg:tt),+) => {{
|
||||
match $e {
|
||||
Ok(::futures::Async::Ready(v)) => v,
|
||||
Ok(_) => {
|
||||
let msg = format_args!($($msg),+);
|
||||
panic!("not ready; {}", msg)
|
||||
}
|
||||
Err(e) => {
|
||||
let msg = format!($($msg),+);
|
||||
panic!("error = {:?}; {}", e, msg)
|
||||
}
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
/// Asset if the poll is not ready
|
||||
#[macro_export]
|
||||
macro_rules! assert_not_ready {
|
||||
($e:expr) => {{
|
||||
match $e {
|
||||
Ok(::futures::Async::NotReady) => {}
|
||||
Ok(::futures::Async::Ready(v)) => panic!("ready; value = {:?}", v),
|
||||
Err(e) => panic!("error = {:?}", e),
|
||||
}
|
||||
}};
|
||||
($e:expr, $($msg:tt),+) => {{
|
||||
match $e {
|
||||
Ok(::futures::Async::NotReady) => {}
|
||||
Ok(::futures::Async::Ready(v)) => {
|
||||
let msg = format_args!($($msg),+);
|
||||
panic!("ready; value = {:?}; {}", v, msg)
|
||||
}
|
||||
Err(e) => {
|
||||
let msg = format_args!($($msg),+);
|
||||
panic!("error = {:?}; {}", e, msg)
|
||||
}
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
/// Assert if a poll is ready and check for equality on the value
|
||||
#[macro_export]
|
||||
macro_rules! assert_ready_eq {
|
||||
($e:expr, $expect:expr) => {
|
||||
match $e {
|
||||
Ok(e) => assert_eq!(e, ::futures::Async::Ready($expect)),
|
||||
Err(e) => panic!("error = {:?}", e),
|
||||
}
|
||||
};
|
||||
|
||||
($e:expr, $expect:expr, $($msg:tt),+) => {
|
||||
match $e {
|
||||
Ok(e) => assert_eq!(e, ::futures::Async::Ready($expect), $($msg)+),
|
||||
Err(e) => {
|
||||
let msg = format_args!($($msg),+);
|
||||
panic!("error = {:?}; {}", e, msg)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Assert if the deadline has passed
|
||||
#[macro_export]
|
||||
macro_rules! assert_elapsed {
|
||||
($e:expr) => {
|
||||
assert!($e.unwrap_err().is_elapsed());
|
||||
};
|
||||
|
||||
($e:expr, $($msg:expr),+) => {
|
||||
assert!($e.unwrap_err().is_elapsed(), $msg);
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use futures::{future, Async, Future, Poll};
|
||||
|
||||
#[test]
|
||||
fn assert_ready() {
|
||||
let mut fut = future::ok::<(), ()>(());
|
||||
assert_ready!(fut.poll());
|
||||
let mut fut = future::ok::<(), ()>(());
|
||||
assert_ready!(fut.poll(), "some message");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn assert_ready_err() {
|
||||
let mut fut = future::err::<(), ()>(());
|
||||
assert_ready!(fut.poll());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn assert_not_ready() {
|
||||
let poll: Poll<(), ()> = Ok(Async::NotReady);
|
||||
assert_not_ready!(poll);
|
||||
assert_not_ready!(poll, "some message");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn assert_not_ready_err() {
|
||||
let mut fut = future::err::<(), ()>(());
|
||||
assert_not_ready!(fut.poll());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn assert_ready_eq() {
|
||||
let mut fut = future::ok::<(), ()>(());
|
||||
assert_ready_eq!(fut.poll(), ());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
//! Futures task based helpers
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! This example will use the `MockTask` to set the current task on
|
||||
//! poll.
|
||||
//!
|
||||
//! ```
|
||||
//! # #[macro_use] extern crate tokio_test;
|
||||
//! # extern crate futures;
|
||||
//! # use tokio_test::task::MockTask;
|
||||
//! # use futures::{sync::mpsc, Stream, Sink, Future, Async};
|
||||
//! let mut task = MockTask::new();
|
||||
//! let (tx, mut rx) = mpsc::channel(5);
|
||||
//!
|
||||
//! tx.send(()).wait();
|
||||
//!
|
||||
//! assert_ready_eq!(task.enter(|| rx.poll()), Some(()));
|
||||
//! ```
|
||||
|
||||
use futures::executor::{spawn, Notify};
|
||||
use futures::{future, Async};
|
||||
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::sync::{Arc, Condvar, Mutex};
|
||||
|
||||
/// Mock task
|
||||
///
|
||||
/// A mock task is able to intercept and track notifications.
|
||||
#[derive(Debug)]
|
||||
pub struct MockTask {
|
||||
notify: Arc<ThreadNotify>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ThreadNotify {
|
||||
state: AtomicUsize,
|
||||
mutex: Mutex<()>,
|
||||
condvar: Condvar,
|
||||
}
|
||||
|
||||
const IDLE: usize = 0;
|
||||
const NOTIFY: usize = 1;
|
||||
const SLEEP: usize = 2;
|
||||
|
||||
impl MockTask {
|
||||
/// Create a new mock task
|
||||
pub fn new() -> Self {
|
||||
MockTask {
|
||||
notify: Arc::new(ThreadNotify::new()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Run a closure from the context of the task.
|
||||
///
|
||||
/// Any notifications resulting from the execution of the closure are
|
||||
/// tracked.
|
||||
pub fn enter<F, R>(&mut self, f: F) -> R
|
||||
where
|
||||
F: FnOnce() -> R,
|
||||
{
|
||||
self.notify.clear();
|
||||
|
||||
let res = spawn(future::lazy(|| Ok::<_, ()>(f()))).poll_future_notify(&self.notify, 0);
|
||||
|
||||
match res.unwrap() {
|
||||
Async::Ready(v) => v,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the inner future has received a readiness notification
|
||||
/// since the last call to `enter`.
|
||||
pub fn is_notified(&self) -> bool {
|
||||
self.notify.is_notified()
|
||||
}
|
||||
|
||||
/// Returns the number of references to the task notifier
|
||||
///
|
||||
/// The task itself holds a reference. The return value will never be zero.
|
||||
pub fn notifier_ref_count(&self) -> usize {
|
||||
Arc::strong_count(&self.notify)
|
||||
}
|
||||
}
|
||||
|
||||
impl ThreadNotify {
|
||||
fn new() -> Self {
|
||||
ThreadNotify {
|
||||
state: AtomicUsize::new(IDLE),
|
||||
mutex: Mutex::new(()),
|
||||
condvar: Condvar::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Clears any previously received notify, avoiding potential spurrious
|
||||
/// notifications. This should only be called immediately before running the
|
||||
/// task.
|
||||
fn clear(&self) {
|
||||
self.state.store(IDLE, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
fn is_notified(&self) -> bool {
|
||||
match self.state.load(Ordering::SeqCst) {
|
||||
IDLE => false,
|
||||
NOTIFY => true,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Notify for ThreadNotify {
|
||||
fn notify(&self, _unpark_id: usize) {
|
||||
// First, try transitioning from IDLE -> NOTIFY, this does not require a
|
||||
// lock.
|
||||
match self.state.compare_and_swap(IDLE, NOTIFY, Ordering::SeqCst) {
|
||||
IDLE | NOTIFY => return,
|
||||
SLEEP => {}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
// The other half is sleeping, this requires a lock
|
||||
let _m = self.mutex.lock().unwrap();
|
||||
|
||||
// Transition from SLEEP -> NOTIFY
|
||||
match self.state.compare_and_swap(SLEEP, NOTIFY, Ordering::SeqCst) {
|
||||
SLEEP => {}
|
||||
_ => return,
|
||||
}
|
||||
|
||||
// Wakeup the sleeper
|
||||
self.condvar.notify_one();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
#[macro_use]
|
||||
extern crate tokio_test;
|
||||
extern crate futures;
|
||||
extern crate tokio_timer;
|
||||
|
||||
use futures::Future;
|
||||
use std::time::{Duration, Instant};
|
||||
use tokio_test::clock::MockClock;
|
||||
use tokio_test::task::MockTask;
|
||||
use tokio_timer::Delay;
|
||||
|
||||
#[test]
|
||||
fn clock() {
|
||||
let mut mock = MockClock::new();
|
||||
|
||||
mock.enter(|handle| {
|
||||
let deadline = Instant::now() + Duration::from_secs(1);
|
||||
let mut delay = Delay::new(deadline);
|
||||
|
||||
assert_not_ready!(delay.poll());
|
||||
|
||||
handle.advance(Duration::from_secs(2));
|
||||
|
||||
assert_ready!(delay.poll());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn notify() {
|
||||
let deadline = Instant::now() + Duration::from_secs(1);
|
||||
let mut mock = MockClock::new();
|
||||
let mut task = MockTask::new();
|
||||
|
||||
mock.enter(|handle| {
|
||||
let mut delay = Delay::new(deadline);
|
||||
|
||||
task.enter(|| assert_not_ready!(delay.poll()));
|
||||
|
||||
handle.advance(Duration::from_secs(1));
|
||||
|
||||
assert!(task.is_notified());
|
||||
assert_ready!(delay.poll());
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user