1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
45use fuchsia_async::{self as fasync, TimeoutExt};
67use std::future::Future;
8use std::os::raw::c_char;
9use zx::sys::zx_time_t;
1011/// A version of the fidl `DeadlineId` containing unowned data.
12#[derive(Clone, Copy)]
13pub struct DeadlineId<'a> {
14 component_id: &'a str,
15 code: &'a str,
16}
1718impl<'a> Into<fidl_fuchsia_testing_deadline::DeadlineId> for DeadlineId<'a> {
19fn into(self) -> fidl_fuchsia_testing_deadline::DeadlineId {
20 fidl_fuchsia_testing_deadline::DeadlineId {
21 component_id: self.component_id.to_string(),
22 code: self.code.to_string(),
23 }
24 }
25}
2627impl<'a> DeadlineId<'a> {
28/// Create a new deadline identifier.
29pub const fn new(component_id: &'a str, code: &'a str) -> Self {
30Self { component_id, code }
31 }
32}
3334extern "C" {
35fn create_named_deadline(
36 component: *const c_char,
37 component_len: usize,
38 code: *const c_char,
39 code_len: usize,
40 duration: zx_time_t,
41 out: *mut zx_time_t,
42 ) -> bool;
43}
4445fn create_named_deadline_rust(
46 deadline: &DeadlineId<'_>,
47 duration: zx::MonotonicDuration,
48) -> fasync::MonotonicInstant {
49let mut time: zx_time_t = 0;
50let time_valid = unsafe {
51 create_named_deadline(
52 deadline.component_id.as_ptr() as *const c_char,
53 deadline.component_id.len(),
54 deadline.code.as_ptr() as *const c_char,
55 deadline.code.len(),
56 duration.into_nanos(),
57&mut time,
58 )
59 };
60match time_valid {
61true => zx::MonotonicInstant::from_nanos(time).into(),
62false => fasync::MonotonicInstant::now() + duration,
63 }
64}
6566/// A timer with an associated name.
67/// This timer is intended to be used in conjunction with the fake-clock library. Under normal
68/// execution, the timer behaves the same as a regular [`fuchsia_async::Timer`]. When run in an
69/// integration test with the fake-clock library linked in, the creation of the timer and
70/// the expiration of the timer are reported to the fake-clock service. The integration test may
71/// register interest in these events to stop time when they occur.
72pub struct NamedTimer;
7374impl NamedTimer {
75/// Create a new `NamedTimer` that will expire `duration` in the future.
76 /// In an integration test, the `SET` event is reported immediately when this method is called,
77 /// and `EXPIRED` is reported after `duration` elapses. Note `EXPIRED` is still reported even
78 /// if the timer is dropped before `duration` elapses.
79pub fn new(id: &DeadlineId<'_>, duration: zx::MonotonicDuration) -> fasync::Timer {
80let deadline = create_named_deadline_rust(id, duration);
81 fasync::Timer::new(deadline)
82 }
83}
8485/// An extension trait that allows setting a timeout with an associated name.
86/// The timeout is intended to be used in conjunction with the fake-clock library. Under normal
87/// execution, this behaves identically to [`fuchsia_async::TimeoutExt`].
88/// When run in an integration test with the fake-clock library linked in, the creation of the
89/// timer and the expiration of the timer are reported to the fake-clock service. The integration
90/// test may register interest in these events to stop time when they occur.
91pub trait NamedTimeoutExt: Future + Sized {
92/// Wraps the future in a timeout, calling `on_timeout` when the timeout occurs.
93 /// In an integration test, the `SET` event is reported immediately when this method is called,
94 /// and `EXPIRED` is reported after `duration` elapses. Note `EXPIRED` is still reported even
95 /// if `on_timeout` is not run.
96fn on_timeout_named<OT>(
97self,
98 id: &DeadlineId<'_>,
99 duration: zx::MonotonicDuration,
100 on_timeout: OT,
101 ) -> fasync::OnTimeout<Self, OT>
102where
103OT: FnOnce() -> Self::Output,
104 {
105let deadline = create_named_deadline_rust(id, duration);
106self.on_timeout(deadline, on_timeout)
107 }
108}
109110impl<F: Future + Sized> NamedTimeoutExt for F {}
111112#[cfg(test)]
113mod test {
114use super::*;
115use core::task::Poll;
116use std::pin::pin;
117118// When the fake-clock library is not linked in, these timers should behave identical to
119 // fasync::Timer. These tests verify that the fake time utilities provided by
120 // fasync::TestExecutor continue to work when fake-clock is NOT linked in. Behavior with
121 // fake-clock linked in is verified by integration tests in fake-clock/examples.
122123const ONE_HOUR: zx::MonotonicDuration = zx::MonotonicDuration::from_hours(1);
124const DEADLINE_ID: DeadlineId<'static> = DeadlineId::new("component", "code");
125126#[test]
127fn test_timer() {
128let mut executor = fasync::TestExecutor::new_with_fake_time();
129let start_time = executor.now();
130let mut timer = pin!(NamedTimer::new(&DEADLINE_ID, ONE_HOUR));
131assert!(executor.run_until_stalled(&mut timer).is_pending());
132133 executor.set_fake_time(start_time + ONE_HOUR);
134assert_eq!(executor.wake_next_timer(), Some(start_time + ONE_HOUR));
135assert!(executor.run_until_stalled(&mut timer).is_ready());
136 }
137138#[test]
139fn test_timeout_not_invoked() {
140let mut executor = fasync::TestExecutor::new_with_fake_time();
141142let mut ready_future =
143pin!(futures::future::ready("ready")
144 .on_timeout_named(&DEADLINE_ID, ONE_HOUR, || "timeout"));
145assert_eq!(executor.run_until_stalled(&mut ready_future), Poll::Ready("ready"));
146 }
147148#[test]
149fn test_timeout_invoked() {
150let mut executor = fasync::TestExecutor::new_with_fake_time();
151152let start_time = executor.now();
153let mut stalled_future =
154pin!(futures::future::pending().on_timeout_named(&DEADLINE_ID, ONE_HOUR, || "timeout"));
155assert!(executor.run_until_stalled(&mut stalled_future).is_pending());
156 executor.set_fake_time(start_time + ONE_HOUR);
157assert_eq!(executor.wake_next_timer(), Some(start_time + ONE_HOUR));
158assert_eq!(executor.run_until_stalled(&mut stalled_future), Poll::Ready("timeout"));
159 }
160}