|
1 | 1 | #![warn(rust_2018_idioms)] |
2 | 2 | #![cfg(feature = "full")] |
3 | 3 |
|
4 | | -use tokio::time::{self, Duration, Instant, MissedTickBehavior}; |
5 | | -use tokio_test::{assert_pending, assert_ready_eq, task}; |
| 4 | +use std::pin::Pin; |
| 5 | +use std::task::{Context, Poll}; |
6 | 6 |
|
7 | | -use std::task::Poll; |
| 7 | +use futures::{Stream, StreamExt}; |
| 8 | +use tokio::time::{self, Duration, Instant, Interval, MissedTickBehavior}; |
| 9 | +use tokio_test::{assert_pending, assert_ready_eq, task}; |
8 | 10 |
|
9 | 11 | // Takes the `Interval` task, `start` variable, and optional time deltas |
10 | 12 | // For each time delta, it polls the `Interval` and asserts that the result is |
@@ -209,3 +211,113 @@ fn poll_next(interval: &mut task::Spawn<time::Interval>) -> Poll<Instant> { |
209 | 211 | fn ms(n: u64) -> Duration { |
210 | 212 | Duration::from_millis(n) |
211 | 213 | } |
| 214 | + |
| 215 | +/// Helper struct to test the [tokio::time::Interval::poll_tick()] method. |
| 216 | +/// |
| 217 | +/// `poll_tick()` should register the waker in the context only if it returns |
| 218 | +/// `Poll::Pending`, not when returning `Poll::Ready`. This struct contains an |
| 219 | +/// interval timer and counts up on every tick when used as stream. When the |
| 220 | +/// counter is a multiple of four, it yields the current counter value. |
| 221 | +/// Depending on the value for `wake_on_pending`, it will reschedule itself when |
| 222 | +/// it returns `Poll::Pending` or not. When used with `wake_on_pending=false`, |
| 223 | +/// we expect that the stream stalls because the timer will **not** reschedule |
| 224 | +/// the next wake-up itself once it returned `Poll::Ready`. |
| 225 | +struct IntervalStreamer { |
| 226 | + counter: u32, |
| 227 | + timer: Interval, |
| 228 | + wake_on_pending: bool, |
| 229 | +} |
| 230 | + |
| 231 | +impl Stream for IntervalStreamer { |
| 232 | + type Item = u32; |
| 233 | + |
| 234 | + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> { |
| 235 | + let this = Pin::into_inner(self); |
| 236 | + |
| 237 | + if this.counter > 12 { |
| 238 | + return Poll::Ready(None); |
| 239 | + } |
| 240 | + |
| 241 | + match this.timer.poll_tick(cx) { |
| 242 | + Poll::Pending => Poll::Pending, |
| 243 | + Poll::Ready(_) => { |
| 244 | + this.counter += 1; |
| 245 | + if this.counter % 4 == 0 { |
| 246 | + Poll::Ready(Some(this.counter)) |
| 247 | + } else { |
| 248 | + if this.wake_on_pending { |
| 249 | + // Schedule this task for wake-up |
| 250 | + cx.waker().wake_by_ref(); |
| 251 | + } |
| 252 | + Poll::Pending |
| 253 | + } |
| 254 | + } |
| 255 | + } |
| 256 | + } |
| 257 | +} |
| 258 | + |
| 259 | +#[tokio::test(start_paused = true)] |
| 260 | +async fn stream_with_interval_poll_tick_self_waking() { |
| 261 | + let stream = IntervalStreamer { |
| 262 | + counter: 0, |
| 263 | + timer: tokio::time::interval(tokio::time::Duration::from_millis(10)), |
| 264 | + wake_on_pending: true, |
| 265 | + }; |
| 266 | + |
| 267 | + let (res_tx, mut res_rx) = tokio::sync::mpsc::channel(12); |
| 268 | + |
| 269 | + // Wrap task in timeout so that it will finish eventually even if the stream |
| 270 | + // stalls. |
| 271 | + tokio::spawn(tokio::time::timeout( |
| 272 | + tokio::time::Duration::from_millis(150), |
| 273 | + async move { |
| 274 | + tokio::pin!(stream); |
| 275 | + |
| 276 | + while let Some(item) = stream.next().await { |
| 277 | + res_tx.send(item).await.ok(); |
| 278 | + } |
| 279 | + }, |
| 280 | + )); |
| 281 | + |
| 282 | + let mut items = Vec::with_capacity(3); |
| 283 | + while let Some(result) = res_rx.recv().await { |
| 284 | + items.push(result); |
| 285 | + } |
| 286 | + |
| 287 | + // We expect the stream to yield normally and thus three items. |
| 288 | + assert_eq!(items, vec![4, 8, 12]); |
| 289 | +} |
| 290 | + |
| 291 | +#[tokio::test(start_paused = true)] |
| 292 | +async fn stream_with_interval_poll_tick_no_waking() { |
| 293 | + let stream = IntervalStreamer { |
| 294 | + counter: 0, |
| 295 | + timer: tokio::time::interval(tokio::time::Duration::from_millis(10)), |
| 296 | + wake_on_pending: false, |
| 297 | + }; |
| 298 | + |
| 299 | + let (res_tx, mut res_rx) = tokio::sync::mpsc::channel(12); |
| 300 | + |
| 301 | + // Wrap task in timeout so that it will finish eventually even if the stream |
| 302 | + // stalls. |
| 303 | + tokio::spawn(tokio::time::timeout( |
| 304 | + tokio::time::Duration::from_millis(150), |
| 305 | + async move { |
| 306 | + tokio::pin!(stream); |
| 307 | + |
| 308 | + while let Some(item) = stream.next().await { |
| 309 | + res_tx.send(item).await.ok(); |
| 310 | + } |
| 311 | + }, |
| 312 | + )); |
| 313 | + |
| 314 | + let mut items = Vec::with_capacity(0); |
| 315 | + while let Some(result) = res_rx.recv().await { |
| 316 | + items.push(result); |
| 317 | + } |
| 318 | + |
| 319 | + // We expect the stream to stall because it does not reschedule itself on |
| 320 | + // `Poll::Pending` and neither does [tokio::time::Interval] reschedule the |
| 321 | + // task when returning `Poll::Ready`. |
| 322 | + assert_eq!(items, vec![]); |
| 323 | +} |
0 commit comments