wookie/lib.rs
1//! Async test/bench toolkit including single stepping executors. No-std compatible.
2//!
3//! The primary user interface is the [`wookie!`] macro, which wraps a
4//! future with an executor and pins it on the stack.:
5//!
6//! ```
7//! use core::task::Poll;
8//! use wookie::wookie;
9//! wookie!(future: async { true });
10//! assert_eq!(future.poll(), Poll::Ready(true));
11//!
12//! // you can also just give a variable name if you have one:
13//! let future = async { true };
14//! wookie!(future);
15//! assert_eq!(future.poll(), Poll::Ready(true));
16//!
17//! // we can find out about the state of wakers any time:
18//! assert_eq!(future.cloned(), 0);
19//! assert_eq!(future.dropped(), 0);
20//! assert_eq!(future.woken(), 0);
21//! // or equivalently...
22//! future.stats().assert(0, 0, 0);
23//! ```
24//!
25//! If you do not have access to an allocator, you can use the [`local!`]
26//! macro instead, however, polling is unsafe and you must be very careful to
27//! maintain the invariants described in the `safety` sections of the
28//! [`Local`] methods.
29//!
30//! ```
31//! use core::task::Poll;
32//! use wookie::local;
33//! local!(future: async { true });
34//! assert_eq!(unsafe { future.poll() }, Poll::Ready(true));
35//!
36//! // you can also just give a variable name if you have one:
37//! let future = async { true };
38//! local!(future);
39//! assert_eq!(unsafe { future.poll() }, Poll::Ready(true));
40//!
41//! // we can find out about the state of wakers any time:
42//! assert_eq!(future.cloned(), 0);
43//! assert_eq!(future.dropped(), 0);
44//! assert_eq!(future.woken(), 0);
45//! // or equivalently...
46//! future.stats().assert(0, 0, 0);
47//! ```
48//!
49//! For benchmarking, we provide the [`dummy!`] macro, whose waker does
50//! nothing, but quite quickly.
51//!
52//! ```
53//! use core::task::Poll;
54//! use wookie::dummy;
55//! dummy!(future: async { true });
56//! assert_eq!(future.poll(), Poll::Ready(true));
57//! ```
58//!
59//! We have [`assert_pending!`] and [`assert_ready!`] to save some
60//! typing in assertions:
61//!
62//! ```
63//! use wookie::*;
64//! use core::task::Poll;
65//! assert_pending!(Poll::<i32>::Pending); // pass
66//! // assert_pending!(Poll::Ready(())); // would fail
67//!
68//! // With 1 arg, assert_ready will returning the unwrapped value.
69//! assert_eq!(42, assert_ready!(Poll::Ready(42)));
70//! // assert_ready!(Poll::<i32>::Pending); // would fail
71//!
72//! // With 2 args, it's like [`assert_eq`] on the unwrapped value.
73//! assert_ready!(42, Poll::Ready(42));
74//! // assert_ready!(Poll::<i32>::Pending); // would fail
75//! // assert_ready!(42, Poll::Ready(420)); // would fail
76//! ```
77//!
78//! ## Features
79//!
80//! Default features: `alloc`.
81//!
82//! * `alloc` - enables use of an allocator. Required by [`Wookie`] / [`wookie!`].
83#![no_std]
84
85#[cfg(feature="alloc")]
86extern crate alloc;
87
88mod dummy;
89#[doc(inline)]
90pub use dummy::*;
91
92mod local;
93pub use local::*;
94
95#[cfg(feature="alloc")]
96mod wookie;
97#[cfg(feature="alloc")]
98pub use crate::wookie::*;
99
100/// Statistics of waker activity for [`Wookie`] or [`Local`].
101pub struct Stats {
102 /// The number of times a Waker has been cloned. Usually equivalent to the
103 /// number of times a waker has been set.
104 pub cloned: u16,
105 /// The number of times a Waker has been dropped. Note that `wake` causes
106 /// this count to be incremented as it takes ownership of the Waker.
107 pub dropped: u16,
108 /// The number of times a Waker has been woken. Includes calls to both
109 /// `wake` and `wake_by_ref`.
110 pub woken: u16,
111}
112
113impl Stats {
114 /// The number of live wakers, i.e. `cloned - dropped`.
115 #[inline(always)]
116 pub fn live(&self) -> u16 { self.cloned - self.dropped }
117
118 /// Assert that `cloned`, `dropped` and `woken` are the provided values.
119 pub fn assert(&self, cloned: u16, dropped: u16, woken: u16) {
120 assert_eq!((cloned, dropped, woken), (self.cloned, self.dropped, self.woken));
121 }
122}
123
124#[macro_export]
125/// Asserts that a [`Poll`] is a [`Poll::Pending`]
126///
127/// ## Examples
128///
129/// ```
130/// use wookie::assert_pending;
131/// use core::task::Poll;
132/// assert_pending!(Poll::<i32>::Pending); // pass
133/// // assert_pending!(Poll::Ready(())); // would fail
134/// ```
135macro_rules! assert_pending {
136 ($expr:expr) => {
137 if let Poll::Ready(r) = $expr {
138 panic!("Expected Poll::Pending, got Poll::Ready({:?})!", r);
139 }
140 }
141}
142
143#[macro_export]
144/// Asserts that a [`Poll`] is a [`Poll::Ready`]
145///
146/// ## Examples
147///
148/// ```
149/// use wookie::assert_ready;
150/// use core::task::Poll;
151///
152/// // With 1 arg, just checks for ready, returning the unwrapped value.
153/// assert_eq!(42, assert_ready!(Poll::Ready(42)));
154/// // assert_ready!(Poll::<i32>::Pending); // would fail
155///
156/// // With 2 args, it's like [`assert_eq`] on the unwrapped value.
157/// assert_ready!(42, Poll::Ready(42));
158/// // assert_ready!(Poll::<i32>::Pending); // would fail
159/// // assert_ready!(42, Poll::Ready(420)); // would fail
160/// ```
161macro_rules! assert_ready {
162 ($expr:expr) => {
163 match $expr {
164 Poll::Ready(r) => r,
165 Poll::Pending => panic!("Expected Poll::Ready, got Poll::Pending!"),
166 }
167 };
168 ($expected:expr, $expr:expr) => {
169 match $expr {
170 Poll::Ready(r) => assert_eq!($expected, r),
171 Poll::Pending => panic!("Expected Poll::Ready, got Poll::Pending!"),
172 }
173 }
174}