Skip to main content

input_pipeline/
mouse_injector_handler.rs

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.
4
5#![warn(clippy::await_holding_refcell_ref)]
6
7use crate::input_handler::{Handler, InputHandler, InputHandlerStatus};
8use crate::utils::{self, CursorMessage, Position, Size};
9use crate::{
10    Dispatcher, Incoming, MonotonicInstant, Transport, input_device, metrics, mouse_binding,
11};
12use anyhow::{Context, Error, Result, anyhow};
13use async_trait::async_trait;
14use async_utils::hanging_get::client::HangingGetStream;
15use fidl_fuchsia_input_report::Range;
16use fidl_fuchsia_ui_pointerinjector_configuration as pointerinjector_config;
17use fidl_next_fuchsia_ui_pointerinjector as pointerinjector;
18use fuchsia_inspect::health::Reporter;
19use futures::SinkExt;
20use futures::channel::mpsc::Sender;
21use futures::stream::StreamExt;
22use metrics_registry::*;
23use sorted_vec_map::SortedVecMap;
24use std::cell::{Ref, RefCell, RefMut};
25use std::rc::Rc;
26
27/// A [`MouseInjectorHandler`] parses mouse events and forwards them to Scenic through the
28/// fidl_fuchsia_pointerinjector protocols.
29pub struct MouseInjectorHandler {
30    /// The mutable fields of this handler.
31    mutable_state: RefCell<MutableState>,
32
33    /// The scope and coordinate system of injection.
34    /// See [`fidl_fuchsia_pointerinjector::Context`] for more details.
35    context_view_ref: fidl_fuchsia_ui_views::ViewRef,
36
37    /// The region where dispatch is attempted for injected events.
38    /// See [`fidl_fuchsia_pointerinjector::Target`] for more details.
39    target_view_ref: fidl_fuchsia_ui_views::ViewRef,
40
41    /// The maximum position sent to clients, used to bound relative movements
42    /// and scale absolute positions from device coordinates.
43    max_position: Position,
44
45    /// The FIDL proxy to register new injectors.
46    injector_registry_proxy: fidl_next::Client<pointerinjector::Registry, Transport>,
47
48    /// The FIDL proxy used to get configuration details for pointer injection.
49    configuration_proxy: pointerinjector_config::SetupProxy,
50
51    /// The inventory of this handler's Inspect status.
52    pub inspect_status: InputHandlerStatus,
53
54    metrics_logger: metrics::MetricsLogger,
55}
56
57struct MutableState {
58    /// A rectangular region that directs injected events into a target.
59    /// See fidl_fuchsia_pointerinjector::Viewport for more details.
60    viewport: Option<pointerinjector::Viewport>,
61
62    /// The injectors registered with Scenic, indexed by their device ids.
63    injectors: SortedVecMap<u32, fidl_next::Client<pointerinjector::Device, Transport>>,
64
65    /// The current position.
66    current_position: Position,
67
68    /// A [`Sender`] used to communicate the current cursor state.
69    cursor_message_sender: Sender<CursorMessage>,
70}
71
72impl Handler for MouseInjectorHandler {
73    fn set_handler_healthy(self: std::rc::Rc<Self>) {
74        self.inspect_status.health_node.borrow_mut().set_ok();
75    }
76
77    fn set_handler_unhealthy(self: std::rc::Rc<Self>, msg: &str) {
78        self.inspect_status.health_node.borrow_mut().set_unhealthy(msg);
79    }
80
81    fn get_name(&self) -> &'static str {
82        "MouseInjectorHandler"
83    }
84
85    fn interest(&self) -> Vec<input_device::InputEventType> {
86        vec![input_device::InputEventType::Mouse]
87    }
88}
89
90#[async_trait(?Send)]
91impl InputHandler for MouseInjectorHandler {
92    async fn handle_input_event(
93        self: Rc<Self>,
94        mut input_event: input_device::InputEvent,
95    ) -> Vec<input_device::InputEvent> {
96        fuchsia_trace::duration!("input", "mouse_injector_handler");
97        match input_event {
98            input_device::InputEvent {
99                device_event: input_device::InputDeviceEvent::Mouse(ref mut mouse_event),
100                device_descriptor:
101                    input_device::InputDeviceDescriptor::Mouse(ref mouse_device_descriptor),
102                event_time,
103                handled: input_device::Handled::No,
104                trace_id,
105            } => {
106                fuchsia_trace::duration!("input", "mouse_injector_handler[processing]");
107                let trace_id = match trace_id {
108                    Some(id) => {
109                        fuchsia_trace::flow_step!("input", "event_in_input_pipeline", id.into());
110                        id
111                    }
112                    None => fuchsia_trace::Id::new(),
113                };
114
115                self.inspect_status.count_received_event(&event_time);
116                // TODO(https://fxbug.dev/42171756): Investigate latency introduced by waiting for update_cursor_renderer
117                if let Err(e) =
118                    self.update_cursor_renderer(mouse_event, &mouse_device_descriptor).await
119                {
120                    self.metrics_logger.log_error(
121                        InputPipelineErrorMetricDimensionEvent::MouseInjectorUpdateCursorRendererFailed,
122                        std::format!("update_cursor_renderer failed: {}", e));
123                }
124
125                // Create a new injector if this is the first time seeing device_id.
126                if let Err(e) = self
127                    .ensure_injector_registered(mouse_event, &mouse_device_descriptor, event_time)
128                    .await
129                {
130                    self.metrics_logger.log_error(
131                        InputPipelineErrorMetricDimensionEvent::MouseInjectorEnsureInjectorRegisteredFailed,
132                        std::format!("ensure_injector_registered failed: {}", e));
133                }
134
135                // Handle the event.
136                if let Err(e) = self.send_event_to_scenic(
137                    mouse_event,
138                    &mouse_device_descriptor,
139                    event_time,
140                    trace_id.into(),
141                ) {
142                    self.metrics_logger.log_error(
143                        InputPipelineErrorMetricDimensionEvent::MouseInjectorSendEventToScenicFailed,
144                        std::format!("send_event_to_scenic failed: {}", e));
145                }
146
147                // Consume the input event.
148                input_event.handled = input_device::Handled::Yes;
149                self.inspect_status.count_handled_event();
150            }
151            _ => {
152                self.metrics_logger.log_error(
153                    InputPipelineErrorMetricDimensionEvent::HandlerReceivedUninterestedEvent,
154                    std::format!(
155                        "{} uninterested input event: {:?}",
156                        self.get_name(),
157                        input_event.get_event_type()
158                    ),
159                );
160            }
161        }
162        vec![input_event]
163    }
164}
165
166impl MouseInjectorHandler {
167    /// Creates a new mouse handler that holds mouse pointer injectors.
168    /// The caller is expected to spawn a task to continually watch for updates to the viewport.
169    /// Example:
170    /// let handler = MouseInjectorHandler::new(display_size).await?;
171    /// fasync::Task::local(handler.clone().watch_viewport()).detach();
172    ///
173    /// # Parameters
174    /// - `display_size`: The size of the associated display.
175    /// - `cursor_message_sender`: A [`Sender`] used to communicate the current cursor state.
176    ///
177    /// # Errors
178    /// If unable to connect to pointerinjector protocols.
179    pub async fn new(
180        incoming: &Incoming,
181        display_size: Size,
182        cursor_message_sender: Sender<CursorMessage>,
183        input_handlers_node: &fuchsia_inspect::Node,
184        metrics_logger: metrics::MetricsLogger,
185    ) -> Result<Rc<Self>, Error> {
186        let configuration_proxy =
187            incoming.connect_protocol::<pointerinjector_config::SetupProxy>()?;
188        let injector_registry_proxy =
189            incoming.connect_protocol_next::<pointerinjector::Registry>()?.spawn();
190
191        Self::new_handler(
192            configuration_proxy,
193            injector_registry_proxy,
194            display_size,
195            cursor_message_sender,
196            input_handlers_node,
197            metrics_logger,
198        )
199        .await
200    }
201
202    /// Creates a new mouse handler that holds mouse pointer injectors.
203    /// The caller is expected to spawn a task to continually watch for updates to the viewport.
204    /// Example:
205    /// let handler = MouseInjectorHandler::new_with_config_proxy(config_proxy, display_size).await?;
206    /// fasync::Task::local(handler.clone().watch_viewport()).detach();
207    ///
208    /// # Parameters
209    /// - `configuration_proxy`: A proxy used to get configuration details for pointer
210    ///    injection.
211    /// - `display_size`: The size of the associated display.
212    /// - `cursor_message_sender`: A [`Sender`] used to communicate the current cursor state.
213    ///
214    /// # Errors
215    /// If unable to get injection view refs from `configuration_proxy`.
216    /// If unable to connect to pointerinjector Registry protocol.
217    pub async fn new_with_config_proxy(
218        incoming: &Incoming,
219        configuration_proxy: pointerinjector_config::SetupProxy,
220        display_size: Size,
221        cursor_message_sender: Sender<CursorMessage>,
222        input_handlers_node: &fuchsia_inspect::Node,
223        metrics_logger: metrics::MetricsLogger,
224    ) -> Result<Rc<Self>, Error> {
225        let injector_registry_proxy =
226            incoming.connect_protocol_next::<pointerinjector::Registry>()?.spawn();
227        Self::new_handler(
228            configuration_proxy,
229            injector_registry_proxy,
230            display_size,
231            cursor_message_sender,
232            input_handlers_node,
233            metrics_logger,
234        )
235        .await
236    }
237
238    fn inner(&self) -> Ref<'_, MutableState> {
239        self.mutable_state.borrow()
240    }
241
242    fn inner_mut(&self) -> RefMut<'_, MutableState> {
243        self.mutable_state.borrow_mut()
244    }
245
246    /// Creates a new mouse handler that holds mouse pointer injectors.
247    /// The caller is expected to spawn a task to continually watch for updates to the viewport.
248    /// Example:
249    /// let handler = MouseInjectorHandler::new_handler(None, None, display_size).await?;
250    /// fasync::Task::local(handler.clone().watch_viewport()).detach();
251    ///
252    /// # Parameters
253    /// - `configuration_proxy`: A proxy used to get configuration details for pointer
254    ///    injection.
255    /// - `injector_registry_proxy`: A proxy used to register new pointer injectors.
256    /// - `display_size`: The size of the associated display.
257    /// - `cursor_message_sender`: A [`Sender`] used to communicate the current cursor state.
258    ///
259    /// # Errors
260    /// If unable to get injection view refs from `configuration_proxy`.
261    async fn new_handler(
262        configuration_proxy: pointerinjector_config::SetupProxy,
263        injector_registry_proxy: fidl_next::Client<pointerinjector::Registry, Transport>,
264        display_size: Size,
265        cursor_message_sender: Sender<CursorMessage>,
266        input_handlers_node: &fuchsia_inspect::Node,
267        metrics_logger: metrics::MetricsLogger,
268    ) -> Result<Rc<Self>, Error> {
269        // Get the context and target views to inject into.
270        let (context_view_ref, target_view_ref) = configuration_proxy.get_view_refs().await?;
271        let inspect_status = InputHandlerStatus::new(
272            input_handlers_node,
273            "mouse_injector_handler",
274            /* generates_events */ false,
275        );
276        let handler = Rc::new(Self {
277            mutable_state: RefCell::new(MutableState {
278                viewport: None,
279                injectors: SortedVecMap::new(),
280                // Initially centered.
281                current_position: Position {
282                    x: display_size.width / 2.0,
283                    y: display_size.height / 2.0,
284                },
285                cursor_message_sender,
286            }),
287            context_view_ref,
288            target_view_ref,
289            max_position: Position { x: display_size.width, y: display_size.height },
290            injector_registry_proxy,
291            configuration_proxy,
292            inspect_status,
293            metrics_logger,
294        });
295
296        Ok(handler)
297    }
298
299    /// Adds a new pointer injector and tracks it in `self.injectors` if one doesn't exist at
300    /// `mouse_descriptor.device_id`.
301    ///
302    /// # Parameters
303    /// - `mouse_event`: The mouse event to send to Scenic.
304    /// - `mouse_descriptor`: The descriptor for the device that sent the mouse event.
305    /// - `event_time`: The time in nanoseconds when the event was first recorded.
306    async fn ensure_injector_registered(
307        self: &Rc<Self>,
308        mouse_event: &mut mouse_binding::MouseEvent,
309        mouse_descriptor: &mouse_binding::MouseDeviceDescriptor,
310        event_time: zx::MonotonicInstant,
311    ) -> Result<(), anyhow::Error> {
312        if self.inner().injectors.contains_key(&mouse_descriptor.device_id) {
313            return Ok(());
314        }
315
316        // Create a new injector.
317        let (device_proxy, device_server) =
318            fidl_next::fuchsia::create_channel::<pointerinjector::Device>();
319        let device_proxy = Dispatcher::client_from_zx_channel(device_proxy).spawn();
320        let context = fuchsia_scenic::duplicate_view_ref(&self.context_view_ref)
321            .context("Failed to duplicate context view ref.")?;
322        let context = fidl_next_fuchsia_ui_views::ViewRef { reference: context.reference };
323        let target = fuchsia_scenic::duplicate_view_ref(&self.target_view_ref)
324            .context("Failed to duplicate target view ref.")?;
325        let target = fidl_next_fuchsia_ui_views::ViewRef { reference: target.reference };
326
327        let viewport = self.inner().viewport.clone();
328        let config = pointerinjector::Config {
329            device_id: Some(mouse_descriptor.device_id),
330            device_type: Some(pointerinjector::DeviceType::Mouse),
331            context: Some(pointerinjector::Context::View(context)),
332            target: Some(pointerinjector::Target::View(target)),
333            viewport,
334            dispatch_policy: Some(pointerinjector::DispatchPolicy::MouseHoverAndLatchInTarget),
335            scroll_v_range: utils::axis_to_next(mouse_descriptor.wheel_v_range.as_ref()),
336            scroll_h_range: utils::axis_to_next(mouse_descriptor.wheel_h_range.as_ref()),
337            buttons: mouse_descriptor.buttons.clone(),
338            ..Default::default()
339        };
340
341        // Register the new injector.
342        self.injector_registry_proxy
343            .register(config, device_server)
344            .await
345            .context("Failed to register injector.")?;
346        log::info!("Registered injector with device id {:?}", mouse_descriptor.device_id);
347
348        // Keep track of the injector.
349        self.inner_mut().injectors.insert(mouse_descriptor.device_id, device_proxy.clone());
350
351        // Inject ADD event the first time a MouseDevice is seen.
352        let events_to_send = vec![self.create_pointer_sample_event(
353            mouse_event,
354            event_time,
355            pointerinjector::EventPhase::Add,
356            self.inner().current_position,
357            None,
358            None,
359        )];
360        device_proxy
361            .inject_events(events_to_send)
362            .send_immediately()
363            .context("Failed to ADD new MouseDevice.")?;
364
365        Ok(())
366    }
367
368    /// Updates the current cursor position according to the received mouse event.
369    ///
370    /// The updated cursor state is sent via `self.inner.cursor_message_sender` to a client
371    /// that renders the cursor on-screen.
372    ///
373    /// If there is no movement, the location is not sent.
374    ///
375    /// # Parameters
376    /// - `mouse_event`: The mouse event to use to update the cursor location.
377    /// - `mouse_descriptor`: The descriptor for the input device generating the input reports.
378    async fn update_cursor_renderer(
379        &self,
380        mouse_event: &mouse_binding::MouseEvent,
381        mouse_descriptor: &mouse_binding::MouseDeviceDescriptor,
382    ) -> Result<(), anyhow::Error> {
383        let mut new_position = match (mouse_event.location, mouse_descriptor) {
384            (
385                mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation { counts }),
386                _,
387            ) => self.inner().current_position + counts,
388            (
389                mouse_binding::MouseLocation::Absolute(position),
390                mouse_binding::MouseDeviceDescriptor {
391                    absolute_x_range: Some(x_range),
392                    absolute_y_range: Some(y_range),
393                    ..
394                },
395            ) => self.scale_absolute_position(&position, &x_range, &y_range),
396            (mouse_binding::MouseLocation::Absolute(_), _) => {
397                return Err(anyhow!(
398                    "Received an Absolute mouse location without absolute device ranges."
399                ));
400            }
401        };
402        Position::clamp(&mut new_position, Position::zero(), self.max_position);
403        self.inner_mut().current_position = new_position;
404
405        let mut cursor_message_sender = self.inner().cursor_message_sender.clone();
406        cursor_message_sender
407            .send(CursorMessage::SetPosition(new_position))
408            .await
409            .context("Failed to send current mouse position to cursor renderer")?;
410
411        Ok(())
412    }
413
414    /// Returns an absolute cursor position scaled from device coordinates to the handler's
415    /// max position.
416    ///
417    /// # Parameters
418    /// - `position`: Absolute cursor position in device coordinates.
419    /// - `x_range`: The range of possible x values of absolute mouse positions.
420    /// - `y_range`: The range of possible y values of absolute mouse positions.
421    fn scale_absolute_position(
422        &self,
423        position: &Position,
424        x_range: &Range,
425        y_range: &Range,
426    ) -> Position {
427        let range_min = Position { x: x_range.min as f32, y: y_range.min as f32 };
428        let range_max = Position { x: x_range.max as f32, y: y_range.max as f32 };
429        self.max_position * ((*position - range_min) / (range_max - range_min))
430    }
431
432    /// Sends the given event to Scenic.
433    ///
434    /// # Parameters
435    /// - `mouse_event`: The mouse event to send to Scenic.
436    /// - `mouse_descriptor`: The descriptor for the device that sent the mouse event.
437    /// - `event_time`: The time in nanoseconds when the event was first recorded.
438    fn send_event_to_scenic(
439        &self,
440        mouse_event: &mut mouse_binding::MouseEvent,
441        mouse_descriptor: &mouse_binding::MouseDeviceDescriptor,
442        event_time: zx::MonotonicInstant,
443        tracing_id: u64,
444    ) -> Result<(), anyhow::Error> {
445        let injector = self.inner().injectors.get(&mouse_descriptor.device_id).cloned();
446        if let Some(injector) = injector {
447            let relative_motion = match mouse_event.location {
448                mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
449                    counts: offset_counts,
450                }) if mouse_event.phase == mouse_binding::MousePhase::Move => {
451                    Some([offset_counts.x, offset_counts.y])
452                }
453                _ => None,
454            };
455            let events_to_send = vec![self.create_pointer_sample_event(
456                mouse_event,
457                event_time,
458                pointerinjector::EventPhase::Change,
459                self.inner().current_position,
460                relative_motion,
461                Some(tracing_id),
462            )];
463
464            fuchsia_trace::flow_begin!("input", "dispatch_event_to_scenic", tracing_id.into());
465
466            _ = injector.inject_events(events_to_send).send_immediately();
467
468            Ok(())
469        } else {
470            Err(anyhow::format_err!(
471                "No injector found for mouse device {}.",
472                mouse_descriptor.device_id
473            ))
474        }
475    }
476
477    /// Creates a [`fidl_fuchsia_ui_pointerinjector::Event`] representing the given MouseEvent.
478    ///
479    /// # Parameters
480    /// - `mouse_event`: The mouse event to send to Scenic.
481    /// - `event_time`: The time in nanoseconds when the event was first recorded.
482    /// - `phase`: The EventPhase to send to Scenic.
483    /// - `current_position`: The current cursor position.
484    /// - `relative_motion`: The relative motion to send to Scenic.
485    fn create_pointer_sample_event(
486        &self,
487        mouse_event: &mut mouse_binding::MouseEvent,
488        event_time: zx::MonotonicInstant,
489        phase: pointerinjector::EventPhase,
490        current_position: Position,
491        relative_motion: Option<[f32; 2]>,
492        trace_id: Option<u64>,
493    ) -> pointerinjector::Event {
494        let pointer_sample = pointerinjector::PointerSample {
495            pointer_id: Some(0),
496            phase: Some(phase),
497            position_in_viewport: Some([current_position.x, current_position.y]),
498            scroll_v: mouse_event.wheel_delta_v.as_ref().map(|delta| delta.ticks),
499            scroll_h: mouse_event.wheel_delta_h.as_ref().map(|delta| delta.ticks),
500            scroll_v_physical_pixel: match mouse_event.wheel_delta_v {
501                Some(mouse_binding::WheelDelta { physical_pixel: Some(pixel), .. }) => {
502                    Some(pixel.into())
503                }
504                _ => None,
505            },
506            scroll_h_physical_pixel: match mouse_event.wheel_delta_h {
507                Some(mouse_binding::WheelDelta { physical_pixel: Some(pixel), .. }) => {
508                    Some(pixel.into())
509                }
510                _ => None,
511            },
512            is_precision_scroll: match mouse_event.phase {
513                mouse_binding::MousePhase::Wheel => match mouse_event.is_precision_scroll {
514                    Some(mouse_binding::PrecisionScroll::Yes) => Some(true),
515                    Some(mouse_binding::PrecisionScroll::No) => Some(false),
516                    None => {
517                        self.metrics_logger.log_error(
518                            InputPipelineErrorMetricDimensionEvent::MouseInjectorMissingIsPrecisionScroll,
519                            "mouse wheel event does not have value in is_precision_scroll.");
520                        None
521                    }
522                },
523                _ => None,
524            },
525            pressed_buttons: Some(mouse_event.pressed_buttons.clone().into()),
526            relative_motion,
527            ..Default::default()
528        };
529        pointerinjector::Event {
530            timestamp: Some(event_time.into_nanos()),
531            data: Some(pointerinjector::Data::PointerSample(pointer_sample)),
532            trace_flow_id: trace_id,
533            wake_lease: mouse_event.wake_lease.take(),
534            ..Default::default()
535        }
536    }
537
538    /// Watches for viewport updates from the scene manager.
539    pub async fn watch_viewport(self: Rc<Self>) {
540        let configuration_proxy = self.configuration_proxy.clone();
541        let mut viewport_stream = HangingGetStream::new(
542            configuration_proxy,
543            pointerinjector_config::SetupProxy::watch_viewport,
544        );
545        loop {
546            match viewport_stream.next().await {
547                Some(Ok(new_viewport)) => {
548                    // Update the viewport tracked by this handler.
549                    self.inner_mut().viewport = Some(utils::viewport_to_next(&new_viewport));
550
551                    // Update Scenic with the latest viewport.
552                    let injectors =
553                        self.inner().injectors.iter().map(|(_, v)| v).cloned().collect::<Vec<_>>();
554                    for injector in injectors {
555                        let events = vec![pointerinjector::Event {
556                            timestamp: Some(MonotonicInstant::now().into_nanos()),
557                            data: Some(pointerinjector::Data::Viewport(utils::viewport_to_next(
558                                &new_viewport,
559                            ))),
560                            trace_flow_id: Some(fuchsia_trace::Id::new().into()),
561                            ..Default::default()
562                        }];
563                        injector
564                            .inject_events(events)
565                            .await
566                            .expect("Failed to inject updated viewport.");
567                    }
568                }
569                Some(Err(e)) => {
570                    self.metrics_logger.log_error(
571                        InputPipelineErrorMetricDimensionEvent::MouseInjectorErrorWhileReadingViewportUpdate,
572                        std::format!("Error while reading viewport update: {}", e));
573                    return;
574                }
575                None => {
576                    self.metrics_logger.log_error(
577                        InputPipelineErrorMetricDimensionEvent::MouseInjectorViewportUpdateStreamTerminatedUnexpectedly,
578                        "Viewport update stream terminated unexpectedly");
579                    return;
580                }
581            }
582        }
583    }
584}
585
586#[cfg(test)]
587mod tests {
588    use super::*;
589    use crate::testing_utilities::{
590        assert_handler_ignores_input_event_sequence, create_mouse_event,
591        create_mouse_event_with_handled, create_mouse_pointer_sample_event,
592        create_mouse_pointer_sample_event_phase_add,
593        create_mouse_pointer_sample_event_with_wheel_physical_pixel, next_client_old_stream,
594    };
595    use assert_matches::assert_matches;
596    use fidl_fuchsia_input_report as fidl_input_report;
597    use fidl_fuchsia_ui_pointerinjector as pointerinjector;
598    use fidl_next_fuchsia_ui_pointerinjector as pointerinjector_next;
599    use fuchsia_async as fasync;
600    use futures::channel::mpsc;
601    use pretty_assertions::assert_eq;
602    use sorted_vec_map::SortedVecSet;
603    use std::ops::Add;
604    use test_case::test_case;
605
606    const DISPLAY_WIDTH_IN_PHYSICAL_PX: f32 = 100.0;
607    const DISPLAY_HEIGHT_IN_PHYSICAL_PX: f32 = 100.0;
608    /// Returns an |input_device::InputDeviceDescriptor::MouseDescriptor|.
609    const DESCRIPTOR: input_device::InputDeviceDescriptor =
610        input_device::InputDeviceDescriptor::Mouse(mouse_binding::MouseDeviceDescriptor {
611            device_id: 1,
612            absolute_x_range: Some(fidl_input_report::Range { min: 0, max: 100 }),
613            absolute_y_range: Some(fidl_input_report::Range { min: 0, max: 100 }),
614            wheel_v_range: Some(fidl_input_report::Axis {
615                range: fidl_input_report::Range { min: -1, max: 1 },
616                unit: fidl_input_report::Unit {
617                    type_: fidl_input_report::UnitType::Other,
618                    exponent: 0,
619                },
620            }),
621            wheel_h_range: Some(fidl_input_report::Axis {
622                range: fidl_input_report::Range { min: -1, max: 1 },
623                unit: fidl_input_report::Unit {
624                    type_: fidl_input_report::UnitType::Other,
625                    exponent: 0,
626                },
627            }),
628            buttons: None,
629        });
630
631    /// Handles |fidl_fuchsia_pointerinjector_configuration::SetupRequest::GetViewRefs|.
632    async fn handle_configuration_request_stream(
633        stream: &mut pointerinjector_config::SetupRequestStream,
634    ) {
635        if let Some(Ok(request)) = stream.next().await {
636            match request {
637                pointerinjector_config::SetupRequest::GetViewRefs { responder, .. } => {
638                    let context = fuchsia_scenic::ViewRefPair::new()
639                        .expect("Failed to create viewrefpair.")
640                        .view_ref;
641                    let target = fuchsia_scenic::ViewRefPair::new()
642                        .expect("Failed to create viewrefpair.")
643                        .view_ref;
644                    let _ = responder.send(context, target);
645                }
646                _ => {}
647            };
648        }
649    }
650
651    /// Handles |fidl_fuchsia_pointerinjector::RegistryRequest|s by forwarding the registered device
652    /// over `injector_sender` to be handled by handle_device_request_stream().
653    async fn handle_registry_request_stream(
654        mut stream: pointerinjector::RegistryRequestStream,
655        injector_sender: futures::channel::oneshot::Sender<pointerinjector::DeviceRequestStream>,
656    ) {
657        if let Some(request) = stream.next().await {
658            match request {
659                Ok(pointerinjector::RegistryRequest::Register {
660                    config: _,
661                    injector,
662                    responder,
663                    ..
664                }) => {
665                    let injector_stream = injector.into_stream();
666                    let _ = injector_sender.send(injector_stream);
667                    responder.send().expect("failed to respond");
668                }
669                _ => {}
670            };
671        } else {
672            panic!("RegistryRequestStream failed.");
673        }
674    }
675
676    // Handles |fidl_fuchsia_pointerinjector::RegistryRequest|s
677    async fn handle_registry_request_stream2(
678        mut stream: pointerinjector::RegistryRequestStream,
679        injector_sender: mpsc::UnboundedSender<Vec<pointerinjector::Event>>,
680    ) {
681        let (injector, responder) = match stream.next().await {
682            Some(Ok(pointerinjector::RegistryRequest::Register {
683                config: _,
684                injector,
685                responder,
686                ..
687            })) => (injector, responder),
688            other => panic!("expected register request, but got {:?}", other),
689        };
690        let injector_stream: pointerinjector::DeviceRequestStream = injector.into_stream();
691        responder.send().expect("failed to respond");
692        injector_stream
693            .for_each(|request| {
694                futures::future::ready({
695                    match request {
696                        Ok(pointerinjector::DeviceRequest::Inject { .. }) => {
697                            panic!("DeviceRequest::Inject is deprecated.");
698                        }
699                        Ok(pointerinjector::DeviceRequest::InjectEvents { events, .. }) => {
700                            let _ = injector_sender.unbounded_send(events);
701                        }
702                        Err(e) => panic!("FIDL error {}", e),
703                    }
704                })
705            })
706            .await;
707    }
708
709    /// Handles |fidl_fuchsia_pointerinjector::DeviceRequest|s by asserting the injector stream
710    /// received on `injector_stream_receiver` gets `expected_events`.
711    async fn handle_device_request_stream(
712        injector_stream_receiver: futures::channel::oneshot::Receiver<
713            pointerinjector::DeviceRequestStream,
714        >,
715        expected_events: Vec<pointerinjector::Event>,
716    ) {
717        let mut injector_stream =
718            injector_stream_receiver.await.expect("Failed to get DeviceRequestStream.");
719        for expected_event in expected_events {
720            match injector_stream.next().await {
721                Some(Ok(pointerinjector::DeviceRequest::Inject { .. })) => {
722                    panic!("DeviceRequest::Inject is deprecated.");
723                }
724                Some(Ok(pointerinjector::DeviceRequest::InjectEvents { events, .. })) => {
725                    assert_eq!(events, vec![expected_event]);
726                }
727                Some(Err(e)) => panic!("FIDL error {}", e),
728                None => panic!("Expected another event."),
729            }
730        }
731    }
732
733    // Creates a |pointerinjector::Viewport|.
734    fn create_viewport(min: f32, max: f32) -> pointerinjector::Viewport {
735        pointerinjector::Viewport {
736            extents: Some([[min, min], [max, max]]),
737            viewport_to_context_transform: None,
738            ..Default::default()
739        }
740    }
741
742    fn create_viewport_next(min: f32, max: f32) -> pointerinjector_next::Viewport {
743        pointerinjector_next::Viewport {
744            extents: Some([[min, min], [max, max]]),
745            viewport_to_context_transform: None,
746            ..Default::default()
747        }
748    }
749
750    // Tests that MouseInjectorHandler::receives_viewport_updates() tracks viewport updates
751    // and notifies injectors about said updates.
752    #[fuchsia::test]
753    fn receives_viewport_updates() {
754        let mut exec = fasync::TestExecutor::new();
755
756        // Set up fidl streams.
757        let (configuration_proxy, mut configuration_request_stream) =
758            fidl::endpoints::create_proxy_and_stream::<pointerinjector_config::SetupMarker>();
759        let (injector_registry_proxy, _) =
760            fidl_next::fuchsia::create_channel::<pointerinjector_next::Registry>();
761        let injector_registry_proxy =
762            Dispatcher::client_from_zx_channel(injector_registry_proxy).spawn();
763        let (sender, _) = futures::channel::mpsc::channel::<CursorMessage>(0);
764
765        let inspector = fuchsia_inspect::Inspector::default();
766        let test_node = inspector.root().create_child("test_node");
767
768        // Create mouse handler.
769        let mouse_handler_fut = MouseInjectorHandler::new_handler(
770            configuration_proxy,
771            injector_registry_proxy,
772            Size { width: DISPLAY_WIDTH_IN_PHYSICAL_PX, height: DISPLAY_HEIGHT_IN_PHYSICAL_PX },
773            sender,
774            &test_node,
775            metrics::MetricsLogger::default(),
776        );
777        let config_request_stream_fut =
778            handle_configuration_request_stream(&mut configuration_request_stream);
779        let (mouse_handler_res, _) = exec.run_singlethreaded(futures::future::join(
780            mouse_handler_fut,
781            config_request_stream_fut,
782        ));
783        let mouse_handler = mouse_handler_res.expect("Failed to create mouse handler");
784
785        // Add an injector.
786        let (injector_device_proxy, mut injector_device_request_stream) =
787            next_client_old_stream::<pointerinjector::DeviceMarker, pointerinjector_next::Device>();
788        mouse_handler.inner_mut().injectors.insert(1, injector_device_proxy);
789
790        // This nested block is used to bound the lifetime of `watch_viewport_fut`.
791        {
792            // Request a viewport update.
793            let watch_viewport_fut = mouse_handler.clone().watch_viewport();
794            futures::pin_mut!(watch_viewport_fut);
795            assert!(exec.run_until_stalled(&mut watch_viewport_fut).is_pending());
796
797            // Send a viewport update.
798            match exec.run_singlethreaded(&mut configuration_request_stream.next()) {
799                Some(Ok(pointerinjector_config::SetupRequest::WatchViewport {
800                    responder, ..
801                })) => {
802                    responder.send(&create_viewport(0.0, 100.0)).expect("Failed to send viewport.");
803                }
804                other => panic!("Received unexpected value: {:?}", other),
805            };
806            assert!(exec.run_until_stalled(&mut watch_viewport_fut).is_pending());
807
808            // Check that the injector received an updated viewport
809            exec.run_singlethreaded(async {
810                match injector_device_request_stream.next().await {
811                    Some(Ok(pointerinjector::DeviceRequest::Inject { .. })) => {
812                        panic!("DeviceRequest::Inject is deprecated.");
813                    }
814                    Some(Ok(pointerinjector::DeviceRequest::InjectEvents { events, .. })) => {
815                        assert_eq!(events.len(), 1);
816                        assert!(events[0].data.is_some());
817                        assert_eq!(
818                            events[0].data,
819                            Some(pointerinjector::Data::Viewport(create_viewport(0.0, 100.0)))
820                        );
821                    }
822                    other => panic!("Received unexpected value: {:?}", other),
823                }
824            });
825
826            // Request viewport update.
827            assert!(exec.run_until_stalled(&mut watch_viewport_fut).is_pending());
828
829            // Send viewport update.
830            match exec.run_singlethreaded(&mut configuration_request_stream.next()) {
831                Some(Ok(pointerinjector_config::SetupRequest::WatchViewport {
832                    responder, ..
833                })) => {
834                    responder
835                        .send(&create_viewport(100.0, 200.0))
836                        .expect("Failed to send viewport.");
837                }
838                other => panic!("Received unexpected value: {:?}", other),
839            };
840
841            // Process viewport update.
842            assert!(exec.run_until_stalled(&mut watch_viewport_fut).is_pending());
843        }
844
845        // Check that the injector received an updated viewport
846        exec.run_singlethreaded(async {
847            match injector_device_request_stream.next().await {
848                Some(Ok(pointerinjector::DeviceRequest::Inject { .. })) => {
849                    panic!("DeviceRequest::Inject is deprecated.");
850                }
851                Some(Ok(pointerinjector::DeviceRequest::InjectEvents { events, .. })) => {
852                    assert_eq!(events.len(), 1);
853                    assert!(events[0].data.is_some());
854                    assert_eq!(
855                        events[0].data,
856                        Some(pointerinjector::Data::Viewport(create_viewport(100.0, 200.0)))
857                    );
858                }
859                other => panic!("Received unexpected value: {:?}", other),
860            }
861        });
862
863        // Check the viewport on the handler is accurate.
864        let expected_viewport = create_viewport_next(100.0, 200.0);
865        assert_eq!(mouse_handler.inner().viewport, Some(expected_viewport));
866    }
867
868    fn wheel_delta_ticks(
869        ticks: i64,
870        physical_pixel: Option<f32>,
871    ) -> Option<mouse_binding::WheelDelta> {
872        Some(mouse_binding::WheelDelta { ticks, physical_pixel })
873    }
874
875    // Tests that a mouse move event both sends an update to scenic and sends the current cursor
876    // location via the cursor location sender.
877    #[test_case(
878        mouse_binding::MouseLocation::Relative(
879            mouse_binding::RelativeLocation {
880                counts: Position { x: 10.0, y: 20.0 }
881            }),
882        Position {
883            x: DISPLAY_WIDTH_IN_PHYSICAL_PX / 2.0 + 10.0,
884            y: DISPLAY_HEIGHT_IN_PHYSICAL_PX / 2.0 + 20.0,
885        },
886        [10.0, 20.0]; "Valid move event."
887    )]
888    #[test_case(
889        mouse_binding::MouseLocation::Relative(
890            mouse_binding::RelativeLocation {
891                counts: Position {
892                    x: DISPLAY_WIDTH_IN_PHYSICAL_PX + 2.0,
893                    y: DISPLAY_HEIGHT_IN_PHYSICAL_PX + 1.0,
894                }}),
895        Position {
896          x: DISPLAY_WIDTH_IN_PHYSICAL_PX,
897          y: DISPLAY_HEIGHT_IN_PHYSICAL_PX,
898        },
899        [
900            DISPLAY_WIDTH_IN_PHYSICAL_PX + 2.0,
901            DISPLAY_HEIGHT_IN_PHYSICAL_PX + 1.0,
902        ]; "Move event exceeds max bounds."
903    )]
904    #[test_case(
905        mouse_binding::MouseLocation::Relative(
906            mouse_binding::RelativeLocation {
907                counts: Position {
908                    x: -(DISPLAY_WIDTH_IN_PHYSICAL_PX + 2.0),
909                    y: -(DISPLAY_HEIGHT_IN_PHYSICAL_PX + 1.0),
910                }}),
911        Position { x: 0.0, y: 0.0 },
912        [
913            -(DISPLAY_WIDTH_IN_PHYSICAL_PX + 2.0),
914            -(DISPLAY_HEIGHT_IN_PHYSICAL_PX + 1.0),
915        ]; "Move event exceeds min bounds."
916    )]
917    #[fuchsia::test(allow_stalls = false)]
918    async fn move_event(
919        move_location: mouse_binding::MouseLocation,
920        expected_position: Position,
921        expected_relative_motion: [f32; 2],
922    ) {
923        // Set up fidl streams.
924        let (configuration_proxy, mut configuration_request_stream) =
925            fidl::endpoints::create_proxy_and_stream::<pointerinjector_config::SetupMarker>();
926        let (injector_registry_proxy, injector_registry_request_stream) = next_client_old_stream::<
927            pointerinjector::RegistryMarker,
928            pointerinjector_next::Registry,
929        >();
930        let config_request_stream_fut =
931            handle_configuration_request_stream(&mut configuration_request_stream);
932
933        // Create MouseInjectorHandler.
934        let (sender, mut receiver) = futures::channel::mpsc::channel::<CursorMessage>(1);
935        let inspector = fuchsia_inspect::Inspector::default();
936        let test_node = inspector.root().create_child("test_node");
937        let mouse_handler_fut = MouseInjectorHandler::new_handler(
938            configuration_proxy,
939            injector_registry_proxy,
940            Size { width: DISPLAY_WIDTH_IN_PHYSICAL_PX, height: DISPLAY_HEIGHT_IN_PHYSICAL_PX },
941            sender,
942            &test_node,
943            metrics::MetricsLogger::default(),
944        );
945        let (mouse_handler_res, _) = futures::join!(mouse_handler_fut, config_request_stream_fut);
946        let mouse_handler = mouse_handler_res.expect("Failed to create mouse handler");
947
948        let event_time = zx::MonotonicInstant::get();
949        let input_event = create_mouse_event(
950            move_location,
951            None, /* wheel_delta_v */
952            None, /* wheel_delta_h */
953            None, /* is_precision_scroll */
954            mouse_binding::MousePhase::Move,
955            SortedVecSet::new(),
956            SortedVecSet::new(),
957            event_time,
958            &DESCRIPTOR,
959        );
960
961        // Handle event.
962        let handle_event_fut = mouse_handler.handle_input_event(input_event);
963        let expected_events = vec![
964            create_mouse_pointer_sample_event_phase_add(vec![], expected_position, event_time),
965            create_mouse_pointer_sample_event(
966                pointerinjector::EventPhase::Change,
967                vec![],
968                expected_position,
969                Some(expected_relative_motion),
970                None, /*wheel_delta_v*/
971                None, /*wheel_delta_h*/
972                None, /*is_precision_scroll*/
973                event_time,
974            ),
975        ];
976
977        // Create a channel for the the registered device's handle to be forwarded to the
978        // DeviceRequestStream handler. This allows the registry_fut to complete and allows
979        // handle_input_event() to continue.
980        let (injector_stream_sender, injector_stream_receiver) =
981            futures::channel::oneshot::channel::<pointerinjector::DeviceRequestStream>();
982        let registry_fut = handle_registry_request_stream(
983            injector_registry_request_stream,
984            injector_stream_sender,
985        );
986        let device_fut = handle_device_request_stream(injector_stream_receiver, expected_events);
987
988        // Await all futures concurrently. If this completes, then the mouse event was handled and
989        // matches `expected_events`.
990        let (handle_result, _, _) = futures::join!(handle_event_fut, registry_fut, device_fut);
991        match receiver.next().await {
992            Some(CursorMessage::SetPosition(position)) => {
993                pretty_assertions::assert_eq!(position, expected_position);
994            }
995            Some(CursorMessage::SetVisibility(_)) => {
996                panic!("Received unexpected cursor visibility update.")
997            }
998            None => panic!("Did not receive cursor update."),
999        }
1000
1001        // No unhandled events.
1002        assert_matches!(
1003            handle_result.as_slice(),
1004            [input_device::InputEvent { handled: input_device::Handled::Yes, .. }]
1005        );
1006    }
1007
1008    // Tests that an absolute mouse move event scales the location from device coordinates to
1009    // between {0, 0} and the handler's maximum position.
1010    #[fuchsia::test(allow_stalls = false)]
1011    async fn move_absolute_event() {
1012        const DEVICE_ID: u32 = 1;
1013
1014        // Set up fidl streams.
1015        let (configuration_proxy, mut configuration_request_stream) =
1016            fidl::endpoints::create_proxy_and_stream::<pointerinjector_config::SetupMarker>();
1017        let (injector_registry_proxy, injector_registry_request_stream) = next_client_old_stream::<
1018            pointerinjector::RegistryMarker,
1019            pointerinjector_next::Registry,
1020        >();
1021        let config_request_stream_fut =
1022            handle_configuration_request_stream(&mut configuration_request_stream);
1023
1024        // Create MouseInjectorHandler.
1025        let (sender, mut receiver) = futures::channel::mpsc::channel::<CursorMessage>(1);
1026        let inspector = fuchsia_inspect::Inspector::default();
1027        let test_node = inspector.root().create_child("test_node");
1028        let mouse_handler_fut = MouseInjectorHandler::new_handler(
1029            configuration_proxy,
1030            injector_registry_proxy,
1031            Size { width: DISPLAY_WIDTH_IN_PHYSICAL_PX, height: DISPLAY_HEIGHT_IN_PHYSICAL_PX },
1032            sender,
1033            &test_node,
1034            metrics::MetricsLogger::default(),
1035        );
1036        let (mouse_handler_res, _) = futures::join!(mouse_handler_fut, config_request_stream_fut);
1037        let mouse_handler = mouse_handler_res.expect("Failed to create mouse handler");
1038
1039        // The location is rescaled from the device coordinate system defined
1040        // by `absolute_x_range` and `absolute_y_range`, to the display coordinate
1041        // system defined by `max_position`.
1042        //
1043        //          -50 y              0 +------------------ w
1044        //            |                  |         .
1045        //            |                  |         .
1046        //            |                  |         .
1047        // -50 x -----o----- 50   ->     | . . . . . . . . .
1048        //            |                  |         .
1049        //         * { x: -25, y: 25 }   |    * { x: w * 0.25, y: h * 0.75 }
1050        //            |                  |         .
1051        //           50                h |         .
1052        //
1053        // Where w = DISPLAY_WIDTH, h = DISPLAY_HEIGHT
1054        let cursor_location =
1055            mouse_binding::MouseLocation::Absolute(Position { x: -25.0, y: 25.0 });
1056        let event_time = zx::MonotonicInstant::get();
1057        let descriptor =
1058            input_device::InputDeviceDescriptor::Mouse(mouse_binding::MouseDeviceDescriptor {
1059                device_id: DEVICE_ID,
1060                absolute_x_range: Some(fidl_input_report::Range { min: -50, max: 50 }),
1061                absolute_y_range: Some(fidl_input_report::Range { min: -50, max: 50 }),
1062                wheel_v_range: None,
1063                wheel_h_range: None,
1064                buttons: None,
1065            });
1066        let input_event = create_mouse_event(
1067            cursor_location,
1068            None, /* wheel_delta_v */
1069            None, /* wheel_delta_h */
1070            None, /* is_precision_scroll */
1071            mouse_binding::MousePhase::Move,
1072            SortedVecSet::new(),
1073            SortedVecSet::new(),
1074            event_time,
1075            &descriptor,
1076        );
1077
1078        // Handle event.
1079        let handle_event_fut = mouse_handler.handle_input_event(input_event);
1080        let expected_position = Position {
1081            x: DISPLAY_WIDTH_IN_PHYSICAL_PX * 0.25,
1082            y: DISPLAY_WIDTH_IN_PHYSICAL_PX * 0.75,
1083        };
1084        let expected_events = vec![
1085            create_mouse_pointer_sample_event_phase_add(vec![], expected_position, event_time),
1086            create_mouse_pointer_sample_event(
1087                pointerinjector::EventPhase::Change,
1088                vec![],
1089                expected_position,
1090                None, /*relative_motion*/
1091                None, /*wheel_delta_v*/
1092                None, /*wheel_delta_h*/
1093                None, /*is_precision_scroll*/
1094                event_time,
1095            ),
1096        ];
1097
1098        // Create a channel for the the registered device's handle to be forwarded to the
1099        // DeviceRequestStream handler. This allows the registry_fut to complete and allows
1100        // handle_input_event() to continue.
1101        let (injector_stream_sender, injector_stream_receiver) =
1102            futures::channel::oneshot::channel::<pointerinjector::DeviceRequestStream>();
1103        let registry_fut = handle_registry_request_stream(
1104            injector_registry_request_stream,
1105            injector_stream_sender,
1106        );
1107        let device_fut = handle_device_request_stream(injector_stream_receiver, expected_events);
1108
1109        // Await all futures concurrently. If this completes, then the mouse event was handled and
1110        // matches `expected_events`.
1111        let (handle_result, _, _) = futures::join!(handle_event_fut, registry_fut, device_fut);
1112        match receiver.next().await {
1113            Some(CursorMessage::SetPosition(position)) => {
1114                assert_eq!(position, expected_position);
1115            }
1116            Some(CursorMessage::SetVisibility(_)) => {
1117                panic!("Received unexpected cursor visibility update.")
1118            }
1119            None => panic!("Did not receive cursor update."),
1120        }
1121
1122        // No unhandled events.
1123        assert_matches!(
1124            handle_result.as_slice(),
1125            [input_device::InputEvent { handled: input_device::Handled::Yes, .. }]
1126        );
1127    }
1128
1129    // Tests that mouse down and up events inject button press state.
1130    #[test_case(
1131      mouse_binding::MousePhase::Down,
1132      vec![1], vec![1]; "Down event injects button press state."
1133    )]
1134    #[test_case(
1135      mouse_binding::MousePhase::Up,
1136      vec![1], vec![]; "Up event injects button press state."
1137    )]
1138    #[fuchsia::test(allow_stalls = false)]
1139    async fn button_state_event(
1140        phase: mouse_binding::MousePhase,
1141        affected_buttons: Vec<mouse_binding::MouseButton>,
1142        pressed_buttons: Vec<mouse_binding::MouseButton>,
1143    ) {
1144        // Set up fidl streams.
1145        let (configuration_proxy, mut configuration_request_stream) =
1146            fidl::endpoints::create_proxy_and_stream::<pointerinjector_config::SetupMarker>();
1147        let (injector_registry_proxy, injector_registry_request_stream) = next_client_old_stream::<
1148            pointerinjector::RegistryMarker,
1149            pointerinjector_next::Registry,
1150        >();
1151        let config_request_stream_fut =
1152            handle_configuration_request_stream(&mut configuration_request_stream);
1153
1154        // Create MouseInjectorHandler.
1155        let (sender, mut receiver) = futures::channel::mpsc::channel::<CursorMessage>(1);
1156        let inspector = fuchsia_inspect::Inspector::default();
1157        let test_node = inspector.root().create_child("test_node");
1158        let mouse_handler_fut = MouseInjectorHandler::new_handler(
1159            configuration_proxy,
1160            injector_registry_proxy,
1161            Size { width: DISPLAY_WIDTH_IN_PHYSICAL_PX, height: DISPLAY_HEIGHT_IN_PHYSICAL_PX },
1162            sender,
1163            &test_node,
1164            metrics::MetricsLogger::default(),
1165        );
1166        let (mouse_handler_res, _) = futures::join!(mouse_handler_fut, config_request_stream_fut);
1167        let mouse_handler = mouse_handler_res.expect("Failed to create mouse handler");
1168
1169        let cursor_location = mouse_binding::MouseLocation::Absolute(Position { x: 0.0, y: 0.0 });
1170        let event_time = zx::MonotonicInstant::get();
1171
1172        let input_event = create_mouse_event(
1173            cursor_location,
1174            None, /* wheel_delta_v */
1175            None, /* wheel_delta_h */
1176            None, /* is_precision_scroll */
1177            phase,
1178            affected_buttons.clone().into(),
1179            pressed_buttons.clone().into(),
1180            event_time,
1181            &DESCRIPTOR,
1182        );
1183
1184        // Handle event.
1185        let handle_event_fut = mouse_handler.handle_input_event(input_event);
1186        let expected_position = Position { x: 0.0, y: 0.0 };
1187        let expected_events = vec![
1188            create_mouse_pointer_sample_event_phase_add(
1189                pressed_buttons.clone().into(),
1190                expected_position,
1191                event_time,
1192            ),
1193            create_mouse_pointer_sample_event(
1194                pointerinjector::EventPhase::Change,
1195                pressed_buttons.clone().into(),
1196                expected_position,
1197                None, /*relative_motion*/
1198                None, /*wheel_delta_v*/
1199                None, /*wheel_delta_h*/
1200                None, /*is_precision_scroll*/
1201                event_time,
1202            ),
1203        ];
1204
1205        // Create a channel for the the registered device's handle to be forwarded to the
1206        // DeviceRequestStream handler. This allows the registry_fut to complete and allows
1207        // handle_input_event() to continue.
1208        let (injector_stream_sender, injector_stream_receiver) =
1209            futures::channel::oneshot::channel::<pointerinjector::DeviceRequestStream>();
1210        let registry_fut = handle_registry_request_stream(
1211            injector_registry_request_stream,
1212            injector_stream_sender,
1213        );
1214        let device_fut = handle_device_request_stream(injector_stream_receiver, expected_events);
1215
1216        // Await all futures concurrently. If this completes, then the mouse event was handled and
1217        // matches `expected_events`.
1218        let (handle_result, _, _) = futures::join!(handle_event_fut, registry_fut, device_fut);
1219        match receiver.next().await {
1220            Some(CursorMessage::SetPosition(position)) => {
1221                pretty_assertions::assert_eq!(position, expected_position);
1222            }
1223            Some(CursorMessage::SetVisibility(_)) => {
1224                panic!("Received unexpected cursor visibility update.")
1225            }
1226            None => panic!("Did not receive cursor update."),
1227        }
1228
1229        // No unhandled events.
1230        assert_matches!(
1231            handle_result.as_slice(),
1232            [input_device::InputEvent { handled: input_device::Handled::Yes, .. }]
1233        );
1234    }
1235
1236    // Tests that mouse down followed by mouse up events inject button press state.
1237    #[fuchsia::test(allow_stalls = false)]
1238    async fn down_up_event() {
1239        // Set up fidl streams.
1240        let (configuration_proxy, mut configuration_request_stream) =
1241            fidl::endpoints::create_proxy_and_stream::<pointerinjector_config::SetupMarker>();
1242        let (injector_registry_proxy, injector_registry_request_stream) = next_client_old_stream::<
1243            pointerinjector::RegistryMarker,
1244            pointerinjector_next::Registry,
1245        >();
1246        let config_request_stream_fut =
1247            handle_configuration_request_stream(&mut configuration_request_stream);
1248
1249        // Create MouseInjectorHandler.
1250        // Note: The size of the CursorMessage channel's buffer is 2 to allow for one cursor
1251        // update for every input event being sent.
1252        let (sender, mut receiver) = futures::channel::mpsc::channel::<CursorMessage>(2);
1253        let inspector = fuchsia_inspect::Inspector::default();
1254        let test_node = inspector.root().create_child("test_node");
1255        let mouse_handler_fut = MouseInjectorHandler::new_handler(
1256            configuration_proxy,
1257            injector_registry_proxy,
1258            Size { width: DISPLAY_WIDTH_IN_PHYSICAL_PX, height: DISPLAY_HEIGHT_IN_PHYSICAL_PX },
1259            sender,
1260            &test_node,
1261            metrics::MetricsLogger::default(),
1262        );
1263        let (mouse_handler_res, _) = futures::join!(mouse_handler_fut, config_request_stream_fut);
1264        let mouse_handler = mouse_handler_res.expect("Failed to create mouse handler");
1265
1266        let cursor_location = mouse_binding::MouseLocation::Absolute(Position { x: 0.0, y: 0.0 });
1267        let event_time1 = zx::MonotonicInstant::get();
1268        let event_time2 = event_time1.add(zx::MonotonicDuration::from_micros(1));
1269
1270        let event1 = create_mouse_event(
1271            cursor_location,
1272            None, /* wheel_delta_v */
1273            None, /* wheel_delta_h */
1274            None, /* is_precision_scroll */
1275            mouse_binding::MousePhase::Down,
1276            SortedVecSet::from(vec![1]),
1277            SortedVecSet::from(vec![1]),
1278            event_time1,
1279            &DESCRIPTOR,
1280        );
1281
1282        let event2 = create_mouse_event(
1283            cursor_location,
1284            None, /* wheel_delta_v */
1285            None, /* wheel_delta_h */
1286            None, /* is_precision_scroll */
1287            mouse_binding::MousePhase::Up,
1288            SortedVecSet::from(vec![1]),
1289            SortedVecSet::new(),
1290            event_time2,
1291            &DESCRIPTOR,
1292        );
1293
1294        let expected_position = Position { x: 0.0, y: 0.0 };
1295
1296        // Create a channel for the the registered device's handle to be forwarded to the
1297        // DeviceRequestStream handler. This allows the registry_fut to complete and allows
1298        // handle_input_event() to continue.
1299        let (injector_stream_sender, injector_stream_receiver) =
1300            mpsc::unbounded::<Vec<pointerinjector::Event>>();
1301        // Up to 2 events per handle_input_event() call.
1302        let mut injector_stream_receiver = injector_stream_receiver.ready_chunks(2);
1303        let registry_fut = handle_registry_request_stream2(
1304            injector_registry_request_stream,
1305            injector_stream_sender,
1306        );
1307
1308        // Run future until the handler future completes.
1309        let _registry_task = fasync::Task::local(registry_fut);
1310
1311        mouse_handler.clone().handle_input_event(event1).await;
1312        assert_eq!(
1313            injector_stream_receiver
1314                .next()
1315                .await
1316                .map(|events| events.into_iter().flatten().collect()),
1317            Some(vec![
1318                create_mouse_pointer_sample_event_phase_add(
1319                    vec![1],
1320                    expected_position,
1321                    event_time1,
1322                ),
1323                create_mouse_pointer_sample_event(
1324                    pointerinjector::EventPhase::Change,
1325                    vec![1],
1326                    expected_position,
1327                    None, /*relative_motion*/
1328                    None, /*wheel_delta_v*/
1329                    None, /*wheel_delta_h*/
1330                    None, /*is_precision_scroll*/
1331                    event_time1,
1332                )
1333            ])
1334        );
1335
1336        // Send another input event.
1337        mouse_handler.clone().handle_input_event(event2).await;
1338        assert_eq!(
1339            injector_stream_receiver
1340                .next()
1341                .await
1342                .map(|events| events.into_iter().flatten().collect()),
1343            Some(vec![create_mouse_pointer_sample_event(
1344                pointerinjector::EventPhase::Change,
1345                vec![],
1346                expected_position,
1347                None, /*relative_motion*/
1348                None, /*wheel_delta_v*/
1349                None, /*wheel_delta_h*/
1350                None, /*is_precision_scroll*/
1351                event_time2,
1352            )])
1353        );
1354
1355        // Wait until validation is complete.
1356        match receiver.next().await {
1357            Some(CursorMessage::SetPosition(position)) => {
1358                assert_eq!(position, expected_position);
1359            }
1360            Some(CursorMessage::SetVisibility(_)) => {
1361                panic!("Received unexpected cursor visibility update.")
1362            }
1363            None => panic!("Did not receive cursor update."),
1364        }
1365    }
1366
1367    /// Tests that two staggered button presses followed by stagged releases generate four mouse
1368    /// events with distinct `affected_button` and `pressed_button`.
1369    /// Specifically, we test and expect the following in order:
1370    /// | Action           | MousePhase | Injected Phase | `pressed_buttons` |
1371    /// | ---------------- | ---------- | -------------- | ----------------- |
1372    /// | Press button 1   | Down       | Change         | [1]               |
1373    /// | Press button 2   | Down       | Change         | [1, 2]            |
1374    /// | Release button 1 | Up         | Change         | [2]               |
1375    /// | Release button 2 | Up         | Change         | []                |
1376    #[fuchsia::test(allow_stalls = false)]
1377    async fn down_down_up_up_event() {
1378        // Set up fidl streams.
1379        let (configuration_proxy, mut configuration_request_stream) =
1380            fidl::endpoints::create_proxy_and_stream::<pointerinjector_config::SetupMarker>();
1381        let (injector_registry_proxy, injector_registry_request_stream) = next_client_old_stream::<
1382            pointerinjector::RegistryMarker,
1383            pointerinjector_next::Registry,
1384        >();
1385        let config_request_stream_fut =
1386            handle_configuration_request_stream(&mut configuration_request_stream);
1387
1388        // Create MouseInjectorHandler.
1389        // Note: The size of the CursorMessage channel's buffer is 4 to allow for one cursor
1390        // update for every input event being sent.
1391        let (sender, mut receiver) = futures::channel::mpsc::channel::<CursorMessage>(4);
1392        let inspector = fuchsia_inspect::Inspector::default();
1393        let test_node = inspector.root().create_child("test_node");
1394        let mouse_handler_fut = MouseInjectorHandler::new_handler(
1395            configuration_proxy,
1396            injector_registry_proxy,
1397            Size { width: DISPLAY_WIDTH_IN_PHYSICAL_PX, height: DISPLAY_HEIGHT_IN_PHYSICAL_PX },
1398            sender,
1399            &test_node,
1400            metrics::MetricsLogger::default(),
1401        );
1402        let (mouse_handler_res, _) = futures::join!(mouse_handler_fut, config_request_stream_fut);
1403        let mouse_handler = mouse_handler_res.expect("Failed to create mouse handler");
1404
1405        let cursor_location = mouse_binding::MouseLocation::Absolute(Position { x: 0.0, y: 0.0 });
1406        let event_time1 = zx::MonotonicInstant::get();
1407        let event_time2 = event_time1.add(zx::MonotonicDuration::from_micros(1));
1408        let event_time3 = event_time2.add(zx::MonotonicDuration::from_micros(1));
1409        let event_time4 = event_time3.add(zx::MonotonicDuration::from_micros(1));
1410
1411        let event1 = create_mouse_event(
1412            cursor_location,
1413            None, /* wheel_delta_v */
1414            None, /* wheel_delta_h */
1415            None, /* is_precision_scroll */
1416            mouse_binding::MousePhase::Down,
1417            SortedVecSet::from(vec![1]),
1418            SortedVecSet::from(vec![1]),
1419            event_time1,
1420            &DESCRIPTOR,
1421        );
1422        let event2 = create_mouse_event(
1423            cursor_location,
1424            None, /* wheel_delta_v */
1425            None, /* wheel_delta_h */
1426            None, /* is_precision_scroll */
1427            mouse_binding::MousePhase::Down,
1428            SortedVecSet::from(vec![2]),
1429            SortedVecSet::from(vec![1, 2]),
1430            event_time2,
1431            &DESCRIPTOR,
1432        );
1433        let event3 = create_mouse_event(
1434            cursor_location,
1435            None, /* wheel_delta_v */
1436            None, /* wheel_delta_h */
1437            None, /* is_precision_scroll */
1438            mouse_binding::MousePhase::Up,
1439            SortedVecSet::from(vec![1]),
1440            SortedVecSet::from(vec![2]),
1441            event_time3,
1442            &DESCRIPTOR,
1443        );
1444        let event4 = create_mouse_event(
1445            cursor_location,
1446            None, /* wheel_delta_v */
1447            None, /* wheel_delta_h */
1448            None, /* is_precision_scroll */
1449            mouse_binding::MousePhase::Up,
1450            SortedVecSet::from(vec![2]),
1451            SortedVecSet::new(),
1452            event_time4,
1453            &DESCRIPTOR,
1454        );
1455
1456        let expected_position = Position { x: 0.0, y: 0.0 };
1457
1458        // Create a channel for the the registered device's handle to be forwarded to the
1459        // DeviceRequestStream handler. This allows the registry_fut to complete and allows
1460        // handle_input_event() to continue.
1461        let (injector_stream_sender, injector_stream_receiver) =
1462            mpsc::unbounded::<Vec<pointerinjector::Event>>();
1463        // Up to 2 events per handle_input_event() call.
1464        let mut injector_stream_receiver = injector_stream_receiver.ready_chunks(2);
1465        let registry_fut = handle_registry_request_stream2(
1466            injector_registry_request_stream,
1467            injector_stream_sender,
1468        );
1469
1470        // Run future until the handler future completes.
1471        let _registry_task = fasync::Task::local(registry_fut);
1472        mouse_handler.clone().handle_input_event(event1).await;
1473        assert_eq!(
1474            injector_stream_receiver
1475                .next()
1476                .await
1477                .map(|events| events.into_iter().flatten().collect()),
1478            Some(vec![
1479                create_mouse_pointer_sample_event_phase_add(
1480                    vec![1],
1481                    expected_position,
1482                    event_time1,
1483                ),
1484                create_mouse_pointer_sample_event(
1485                    pointerinjector::EventPhase::Change,
1486                    vec![1],
1487                    expected_position,
1488                    None, /*relative_motion*/
1489                    None, /*wheel_delta_v*/
1490                    None, /*wheel_delta_h*/
1491                    None, /*is_precision_scroll*/
1492                    event_time1,
1493                )
1494            ])
1495        );
1496
1497        // Send another down event.
1498        mouse_handler.clone().handle_input_event(event2).await;
1499        let pointer_sample_event2: Vec<_> = injector_stream_receiver
1500            .next()
1501            .await
1502            .map(|events| events.into_iter().flatten().collect())
1503            .expect("Failed to receive pointer sample event.");
1504        let expected_event_time: i64 = event_time2.into_nanos();
1505        assert_eq!(pointer_sample_event2.len(), 1);
1506
1507        // We must break this event result apart for assertions since the
1508        // `pressed_buttons` can be given with elements in any order.
1509        match &pointer_sample_event2[0] {
1510            pointerinjector::Event {
1511                timestamp: Some(actual_event_time),
1512                data:
1513                    Some(pointerinjector::Data::PointerSample(pointerinjector::PointerSample {
1514                        pointer_id: Some(0),
1515                        phase: Some(pointerinjector::EventPhase::Change),
1516                        position_in_viewport: Some(actual_position),
1517                        scroll_v: None,
1518                        scroll_h: None,
1519                        pressed_buttons: Some(actual_buttons),
1520                        relative_motion: None,
1521                        ..
1522                    })),
1523                ..
1524            } => {
1525                assert_eq!(*actual_event_time, expected_event_time);
1526                assert_eq!(actual_position[0], expected_position.x);
1527                assert_eq!(actual_position[1], expected_position.y);
1528                assert_eq!(actual_buttons.as_slice(), &[1u8, 2u8]);
1529            }
1530            _ => panic!("Unexpected pointer sample event: {:?}", pointer_sample_event2[0]),
1531        }
1532
1533        // Send another up event.
1534        mouse_handler.clone().handle_input_event(event3).await;
1535        assert_eq!(
1536            injector_stream_receiver
1537                .next()
1538                .await
1539                .map(|events| events.into_iter().flatten().collect()),
1540            Some(vec![create_mouse_pointer_sample_event(
1541                pointerinjector::EventPhase::Change,
1542                vec![2],
1543                expected_position,
1544                None, /*relative_motion*/
1545                None, /*wheel_delta_v*/
1546                None, /*wheel_delta_h*/
1547                None, /*is_precision_scroll*/
1548                event_time3,
1549            )])
1550        );
1551
1552        // Send another up event.
1553        mouse_handler.clone().handle_input_event(event4).await;
1554        assert_eq!(
1555            injector_stream_receiver
1556                .next()
1557                .await
1558                .map(|events| events.into_iter().flatten().collect()),
1559            Some(vec![create_mouse_pointer_sample_event(
1560                pointerinjector::EventPhase::Change,
1561                vec![],
1562                expected_position,
1563                None, /*relative_motion*/
1564                None, /*wheel_delta_v*/
1565                None, /*wheel_delta_h*/
1566                None, /*is_precision_scroll*/
1567                event_time4,
1568            )])
1569        );
1570
1571        // Wait until validation is complete.
1572        match receiver.next().await {
1573            Some(CursorMessage::SetPosition(position)) => {
1574                assert_eq!(position, expected_position);
1575            }
1576            Some(CursorMessage::SetVisibility(_)) => {
1577                panic!("Received unexpected cursor visibility update.")
1578            }
1579            None => panic!("Did not receive cursor update."),
1580        }
1581    }
1582
1583    /// Tests that button press, mouse move, and button release inject changes accordingly.
1584    #[fuchsia::test(allow_stalls = false)]
1585    async fn down_move_up_event() {
1586        // Set up fidl streams.
1587        let (configuration_proxy, mut configuration_request_stream) =
1588            fidl::endpoints::create_proxy_and_stream::<pointerinjector_config::SetupMarker>();
1589        let (injector_registry_proxy, injector_registry_request_stream) = next_client_old_stream::<
1590            pointerinjector::RegistryMarker,
1591            pointerinjector_next::Registry,
1592        >();
1593        let config_request_stream_fut =
1594            handle_configuration_request_stream(&mut configuration_request_stream);
1595
1596        // Create MouseInjectorHandler.
1597        // Note: The size of the CursorMessage channel's buffer is 3 to allow for one cursor
1598        // update for every input event being sent.
1599        let (sender, mut receiver) = futures::channel::mpsc::channel::<CursorMessage>(3);
1600        let inspector = fuchsia_inspect::Inspector::default();
1601        let test_node = inspector.root().create_child("test_node");
1602        let mouse_handler_fut = MouseInjectorHandler::new_handler(
1603            configuration_proxy,
1604            injector_registry_proxy,
1605            Size { width: DISPLAY_WIDTH_IN_PHYSICAL_PX, height: DISPLAY_HEIGHT_IN_PHYSICAL_PX },
1606            sender,
1607            &test_node,
1608            metrics::MetricsLogger::default(),
1609        );
1610        let (mouse_handler_res, _) = futures::join!(mouse_handler_fut, config_request_stream_fut);
1611        let mouse_handler = mouse_handler_res.expect("Failed to create mouse handler");
1612
1613        let event_time1 = zx::MonotonicInstant::get();
1614        let event_time2 = event_time1.add(zx::MonotonicDuration::from_micros(1));
1615        let event_time3 = event_time2.add(zx::MonotonicDuration::from_micros(1));
1616        let zero_position = Position { x: 0.0, y: 0.0 };
1617        let expected_position = Position { x: 10.0, y: 5.0 };
1618        let expected_relative_motion = [10.0, 5.0];
1619        let event1 = create_mouse_event(
1620            mouse_binding::MouseLocation::Absolute(Position { x: 0.0, y: 0.0 }),
1621            None, /* wheel_delta_v */
1622            None, /* wheel_delta_h */
1623            None, /* is_precision_scroll */
1624            mouse_binding::MousePhase::Down,
1625            SortedVecSet::from(vec![1]),
1626            SortedVecSet::from(vec![1]),
1627            event_time1,
1628            &DESCRIPTOR,
1629        );
1630        let event2 = create_mouse_event(
1631            mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
1632                counts: Position { x: 10.0, y: 5.0 },
1633            }),
1634            None, /* wheel_delta_v */
1635            None, /* wheel_delta_h */
1636            None, /* is_precision_scroll */
1637            mouse_binding::MousePhase::Move,
1638            SortedVecSet::from(vec![1]),
1639            SortedVecSet::from(vec![1]),
1640            event_time2,
1641            &DESCRIPTOR,
1642        );
1643        let event3 = create_mouse_event(
1644            mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
1645                counts: Position { x: 0.0, y: 0.0 },
1646            }),
1647            None, /* wheel_delta_v */
1648            None, /* wheel_delta_h */
1649            None, /* is_precision_scroll */
1650            mouse_binding::MousePhase::Up,
1651            SortedVecSet::from(vec![1]),
1652            SortedVecSet::new(),
1653            event_time3,
1654            &DESCRIPTOR,
1655        );
1656
1657        // Create a channel for the the registered device's handle to be forwarded to the
1658        // DeviceRequestStream handler. This allows the registry_fut to complete and allows
1659        // handle_input_event() to continue.
1660        let (injector_stream_sender, injector_stream_receiver) =
1661            mpsc::unbounded::<Vec<pointerinjector::Event>>();
1662        // Up to 2 events per handle_input_event() call.
1663        let mut injector_stream_receiver = injector_stream_receiver.ready_chunks(2);
1664        let registry_fut = handle_registry_request_stream2(
1665            injector_registry_request_stream,
1666            injector_stream_sender,
1667        );
1668
1669        // Run future until the handler future completes.
1670        let _registry_task = fasync::Task::local(registry_fut);
1671        mouse_handler.clone().handle_input_event(event1).await;
1672        assert_eq!(
1673            injector_stream_receiver
1674                .next()
1675                .await
1676                .map(|events| events.into_iter().flatten().collect()),
1677            Some(vec![
1678                create_mouse_pointer_sample_event_phase_add(vec![1], zero_position, event_time1,),
1679                create_mouse_pointer_sample_event(
1680                    pointerinjector::EventPhase::Change,
1681                    vec![1],
1682                    zero_position,
1683                    None, /*relative_motion*/
1684                    None, /*wheel_delta_v*/
1685                    None, /*wheel_delta_h*/
1686                    None, /*is_precision_scroll*/
1687                    event_time1,
1688                )
1689            ])
1690        );
1691
1692        // Wait until cursor position validation is complete.
1693        match receiver.next().await {
1694            Some(CursorMessage::SetPosition(position)) => {
1695                assert_eq!(position, zero_position);
1696            }
1697            Some(CursorMessage::SetVisibility(_)) => {
1698                panic!("Received unexpected cursor visibility update.")
1699            }
1700            None => panic!("Did not receive cursor update."),
1701        }
1702
1703        // Send a move event.
1704        mouse_handler.clone().handle_input_event(event2).await;
1705        assert_eq!(
1706            injector_stream_receiver
1707                .next()
1708                .await
1709                .map(|events| events.into_iter().flatten().collect()),
1710            Some(vec![create_mouse_pointer_sample_event(
1711                pointerinjector::EventPhase::Change,
1712                vec![1],
1713                expected_position,
1714                Some(expected_relative_motion),
1715                None, /*wheel_delta_v*/
1716                None, /*wheel_delta_h*/
1717                None, /*is_precision_scroll*/
1718                event_time2,
1719            )])
1720        );
1721
1722        // Wait until cursor position validation is complete.
1723        match receiver.next().await {
1724            Some(CursorMessage::SetPosition(position)) => {
1725                assert_eq!(position, expected_position);
1726            }
1727            Some(CursorMessage::SetVisibility(_)) => {
1728                panic!("Received unexpected cursor visibility update.")
1729            }
1730            None => panic!("Did not receive cursor update."),
1731        }
1732
1733        // Send an up event.
1734        mouse_handler.clone().handle_input_event(event3).await;
1735        assert_eq!(
1736            injector_stream_receiver
1737                .next()
1738                .await
1739                .map(|events| events.into_iter().flatten().collect()),
1740            Some(vec![create_mouse_pointer_sample_event(
1741                pointerinjector::EventPhase::Change,
1742                vec![],
1743                expected_position,
1744                None, /*relative_motion*/
1745                None, /*wheel_delta_v*/
1746                None, /*wheel_delta_h*/
1747                None, /*is_precision_scroll*/
1748                event_time3,
1749            )])
1750        );
1751
1752        // Wait until cursor position validation is complete.
1753        match receiver.next().await {
1754            Some(CursorMessage::SetPosition(position)) => {
1755                assert_eq!(position, expected_position);
1756            }
1757            Some(CursorMessage::SetVisibility(_)) => {
1758                panic!("Received unexpected cursor visibility update.")
1759            }
1760            None => panic!("Did not receive cursor update."),
1761        }
1762    }
1763
1764    // Tests that a mouse move event that has already been handled is not forwarded to scenic.
1765    #[fuchsia::test(allow_stalls = false)]
1766    async fn handler_ignores_handled_events() {
1767        // Set up fidl streams.
1768        let (configuration_proxy, mut configuration_request_stream) =
1769            fidl::endpoints::create_proxy_and_stream::<pointerinjector_config::SetupMarker>();
1770        let (injector_registry_proxy, injector_registry_request_stream) = next_client_old_stream::<
1771            pointerinjector::RegistryMarker,
1772            pointerinjector_next::Registry,
1773        >();
1774        let config_request_stream_fut =
1775            handle_configuration_request_stream(&mut configuration_request_stream);
1776
1777        // Create MouseInjectorHandler.
1778        let (sender, mut receiver) = futures::channel::mpsc::channel::<CursorMessage>(1);
1779        let inspector = fuchsia_inspect::Inspector::default();
1780        let test_node = inspector.root().create_child("test_node");
1781        let mouse_handler_fut = MouseInjectorHandler::new_handler(
1782            configuration_proxy,
1783            injector_registry_proxy,
1784            Size { width: DISPLAY_WIDTH_IN_PHYSICAL_PX, height: DISPLAY_HEIGHT_IN_PHYSICAL_PX },
1785            sender,
1786            &test_node,
1787            metrics::MetricsLogger::default(),
1788        );
1789        let (mouse_handler_res, _) = futures::join!(mouse_handler_fut, config_request_stream_fut);
1790        let mouse_handler = mouse_handler_res.expect("Failed to create mouse handler");
1791
1792        let cursor_relative_position = Position { x: 50.0, y: 75.0 };
1793        let cursor_location =
1794            mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
1795                counts: Position { x: cursor_relative_position.x, y: cursor_relative_position.y },
1796            });
1797        let event_time = zx::MonotonicInstant::get();
1798        let input_events = vec![create_mouse_event_with_handled(
1799            cursor_location,
1800            None, /* wheel_delta_v */
1801            None, /* wheel_delta_h */
1802            None, /* is_precision_scroll */
1803            mouse_binding::MousePhase::Move,
1804            SortedVecSet::new(),
1805            SortedVecSet::new(),
1806            event_time,
1807            &DESCRIPTOR,
1808            input_device::Handled::Yes,
1809        )];
1810
1811        assert_handler_ignores_input_event_sequence(
1812            mouse_handler,
1813            input_events,
1814            injector_registry_request_stream,
1815        )
1816        .await;
1817
1818        // The cursor location stream should not receive any position.
1819        assert!(receiver.next().await.is_none());
1820    }
1821
1822    fn zero_relative_location() -> mouse_binding::MouseLocation {
1823        mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
1824            counts: Position { x: 0.0, y: 0.0 },
1825        })
1826    }
1827
1828    #[test_case(
1829        create_mouse_event(
1830            zero_relative_location(),
1831            wheel_delta_ticks(1, None),               /*wheel_delta_v*/
1832            None,                                     /*wheel_delta_h*/
1833            Some(mouse_binding::PrecisionScroll::No), /*is_precision_scroll*/
1834            mouse_binding::MousePhase::Wheel,
1835            SortedVecSet::new(),
1836            SortedVecSet::new(),
1837            zx::MonotonicInstant::ZERO,
1838            &DESCRIPTOR,
1839        ),
1840        create_mouse_pointer_sample_event(
1841            pointerinjector::EventPhase::Change,
1842            vec![],
1843            Position { x: 50.0, y: 50.0 },
1844            None,    /*relative_motion*/
1845            Some(1), /*wheel_delta_v*/
1846            None,    /*wheel_delta_h*/
1847            Some(false), /*is_precision_scroll*/
1848            zx::MonotonicInstant::ZERO,
1849        ); "v tick scroll"
1850    )]
1851    #[test_case(
1852        create_mouse_event(
1853            zero_relative_location(),
1854            None,                                     /*wheel_delta_v*/
1855            wheel_delta_ticks(1, None),               /*wheel_delta_h*/
1856            Some(mouse_binding::PrecisionScroll::No), /*is_precision_scroll*/
1857            mouse_binding::MousePhase::Wheel,
1858            SortedVecSet::new(),
1859            SortedVecSet::new(),
1860            zx::MonotonicInstant::ZERO,
1861            &DESCRIPTOR,
1862        ),
1863        create_mouse_pointer_sample_event(
1864            pointerinjector::EventPhase::Change,
1865            vec![],
1866            Position { x: 50.0, y: 50.0 },
1867            None,    /*relative_motion*/
1868            None,    /*wheel_delta_v*/
1869            Some(1), /*wheel_delta_h*/
1870            Some(false), /*is_precision_scroll*/
1871            zx::MonotonicInstant::ZERO,
1872        ); "h tick scroll"
1873    )]
1874    #[test_case(
1875        create_mouse_event(
1876            zero_relative_location(),
1877            wheel_delta_ticks(1, Some(120.0)),        /*wheel_delta_v*/
1878            None,                                     /*wheel_delta_h*/
1879            Some(mouse_binding::PrecisionScroll::No), /*is_precision_scroll*/
1880            mouse_binding::MousePhase::Wheel,
1881            SortedVecSet::new(),
1882            SortedVecSet::new(),
1883            zx::MonotonicInstant::ZERO,
1884            &DESCRIPTOR,
1885        ),
1886        create_mouse_pointer_sample_event_with_wheel_physical_pixel(
1887            pointerinjector::EventPhase::Change,
1888            vec![],
1889            Position { x: 50.0, y: 50.0 },
1890            None,        /*relative_motion*/
1891            Some(1),     /*wheel_delta_v*/
1892            None,        /*wheel_delta_h*/
1893            Some(120.0), /*wheel_delta_v_physical_pixel*/
1894            None,        /*wheel_delta_h_physical_pixel*/
1895            Some(false), /*is_precision_scroll*/
1896            zx::MonotonicInstant::ZERO,
1897        ); "v tick scroll with physical pixel"
1898    )]
1899    #[test_case(
1900        create_mouse_event(
1901            zero_relative_location(),
1902            None,                                     /*wheel_delta_v*/
1903            wheel_delta_ticks(1, Some(120.0)),        /*wheel_delta_h*/
1904            Some(mouse_binding::PrecisionScroll::No), /*is_precision_scroll*/
1905            mouse_binding::MousePhase::Wheel,
1906            SortedVecSet::new(),
1907            SortedVecSet::new(),
1908            zx::MonotonicInstant::ZERO,
1909            &DESCRIPTOR,
1910        ),
1911        create_mouse_pointer_sample_event_with_wheel_physical_pixel(
1912            pointerinjector::EventPhase::Change,
1913            vec![],
1914            Position { x: 50.0, y: 50.0 },
1915            None,        /*relative_motion*/
1916            None,        /*wheel_delta_v*/
1917            Some(1),     /*wheel_delta_h*/
1918            None,        /*wheel_delta_v_physical_pixel*/
1919            Some(120.0), /*wheel_delta_h_physical_pixel*/
1920            Some(false), /*is_precision_scroll*/
1921            zx::MonotonicInstant::ZERO,
1922        ); "h tick scroll with physical pixel"
1923    )]
1924    #[test_case(
1925        create_mouse_event(
1926            zero_relative_location(),
1927            wheel_delta_ticks(1, Some(120.0)),          /*wheel_delta_v*/
1928            None,                                      /*wheel_delta_h*/
1929            Some(mouse_binding::PrecisionScroll::Yes), /*is_precision_scroll*/
1930            mouse_binding::MousePhase::Wheel,
1931            SortedVecSet::new(),
1932            SortedVecSet::new(),
1933            zx::MonotonicInstant::ZERO,
1934            &DESCRIPTOR,
1935        ),
1936        create_mouse_pointer_sample_event_with_wheel_physical_pixel(
1937            pointerinjector::EventPhase::Change,
1938            vec![],
1939            Position { x: 50.0, y: 50.0 },
1940            None,        /*relative_motion*/
1941            Some(1),     /*wheel_delta_v*/
1942            None,        /*wheel_delta_h*/
1943            Some(120.0), /*wheel_delta_v_physical_pixel*/
1944            None,        /*wheel_delta_h_physical_pixel*/
1945            Some(true),  /*is_precision_scroll*/
1946            zx::MonotonicInstant::ZERO,
1947        ); "v precision scroll with physical pixel"
1948    )]
1949    #[test_case(
1950        create_mouse_event(
1951            zero_relative_location(),
1952            None,                                      /*wheel_delta_v*/
1953            wheel_delta_ticks(1, Some(120.0)),          /*wheel_delta_h*/
1954            Some(mouse_binding::PrecisionScroll::Yes), /*is_precision_scroll*/
1955            mouse_binding::MousePhase::Wheel,
1956            SortedVecSet::new(),
1957            SortedVecSet::new(),
1958            zx::MonotonicInstant::ZERO,
1959            &DESCRIPTOR,
1960        ),
1961        create_mouse_pointer_sample_event_with_wheel_physical_pixel(
1962            pointerinjector::EventPhase::Change,
1963            vec![],
1964            Position { x: 50.0, y: 50.0 },
1965            None,        /*relative_motion*/
1966            None,        /*wheel_delta_v*/
1967            Some(1),     /*wheel_delta_h*/
1968            None,        /*wheel_delta_v_physical_pixel*/
1969            Some(120.0), /*wheel_delta_h_physical_pixel*/
1970            Some(true),  /*is_precision_scroll*/
1971            zx::MonotonicInstant::ZERO,
1972        ); "h precision scroll with physical pixel"
1973    )]
1974    /// Test simple scroll in vertical and horizontal.
1975    #[fuchsia::test(allow_stalls = false)]
1976    async fn scroll(event: input_device::InputEvent, want_event: pointerinjector::Event) {
1977        // Set up fidl streams.
1978        let (configuration_proxy, mut configuration_request_stream) =
1979            fidl::endpoints::create_proxy_and_stream::<pointerinjector_config::SetupMarker>();
1980        let (injector_registry_proxy, injector_registry_request_stream) = next_client_old_stream::<
1981            pointerinjector::RegistryMarker,
1982            pointerinjector_next::Registry,
1983        >();
1984        let config_request_stream_fut =
1985            handle_configuration_request_stream(&mut configuration_request_stream);
1986
1987        // Create MouseInjectorHandler.
1988        let (sender, _) = futures::channel::mpsc::channel::<CursorMessage>(1);
1989        let inspector = fuchsia_inspect::Inspector::default();
1990        let test_node = inspector.root().create_child("test_node");
1991        let mouse_handler_fut = MouseInjectorHandler::new_handler(
1992            configuration_proxy,
1993            injector_registry_proxy,
1994            Size { width: DISPLAY_WIDTH_IN_PHYSICAL_PX, height: DISPLAY_HEIGHT_IN_PHYSICAL_PX },
1995            sender,
1996            &test_node,
1997            metrics::MetricsLogger::default(),
1998        );
1999        let (mouse_handler_res, _) = futures::join!(mouse_handler_fut, config_request_stream_fut);
2000        let mouse_handler = mouse_handler_res.expect("Failed to create mouse handler");
2001
2002        // Create a channel for the the registered device's handle to be forwarded to the
2003        // DeviceRequestStream handler. This allows the registry_fut to complete and allows
2004        // handle_input_event() to continue.
2005        let (injector_stream_sender, injector_stream_receiver) =
2006            mpsc::unbounded::<Vec<pointerinjector::Event>>();
2007        // Up to 2 events per handle_input_event() call.
2008        let mut injector_stream_receiver = injector_stream_receiver.ready_chunks(2);
2009        let registry_fut = handle_registry_request_stream2(
2010            injector_registry_request_stream,
2011            injector_stream_sender,
2012        );
2013
2014        let event_time = zx::MonotonicInstant::get();
2015
2016        let event = input_device::InputEvent { event_time, ..event };
2017
2018        let want_event =
2019            pointerinjector::Event { timestamp: Some(event_time.into_nanos()), ..want_event };
2020
2021        // Run future until the handler future completes.
2022        let _registry_task = fasync::Task::local(registry_fut);
2023
2024        mouse_handler.clone().handle_input_event(event).await;
2025        let got_events: Vec<_> = injector_stream_receiver
2026            .next()
2027            .await
2028            .map(|events| events.into_iter().flatten().collect())
2029            .unwrap();
2030        pretty_assertions::assert_eq!(got_events.len(), 2);
2031        assert_matches!(
2032            got_events[0],
2033            pointerinjector::Event {
2034                data: Some(pointerinjector::Data::PointerSample(pointerinjector::PointerSample {
2035                    phase: Some(pointerinjector::EventPhase::Add),
2036                    ..
2037                })),
2038                ..
2039            }
2040        );
2041
2042        pretty_assertions::assert_eq!(got_events[1], want_event);
2043    }
2044
2045    /// Test button down -> scroll -> button up -> continue scroll.
2046    #[fuchsia::test(allow_stalls = false)]
2047    async fn down_scroll_up_scroll() {
2048        // Set up fidl streams.
2049        let (configuration_proxy, mut configuration_request_stream) =
2050            fidl::endpoints::create_proxy_and_stream::<pointerinjector_config::SetupMarker>();
2051        let (injector_registry_proxy, injector_registry_request_stream) = next_client_old_stream::<
2052            pointerinjector::RegistryMarker,
2053            pointerinjector_next::Registry,
2054        >();
2055        let config_request_stream_fut =
2056            handle_configuration_request_stream(&mut configuration_request_stream);
2057
2058        // Create MouseInjectorHandler.
2059        let (sender, _) = futures::channel::mpsc::channel::<CursorMessage>(1);
2060        let inspector = fuchsia_inspect::Inspector::default();
2061        let test_node = inspector.root().create_child("test_node");
2062        let mouse_handler_fut = MouseInjectorHandler::new_handler(
2063            configuration_proxy,
2064            injector_registry_proxy,
2065            Size { width: DISPLAY_WIDTH_IN_PHYSICAL_PX, height: DISPLAY_HEIGHT_IN_PHYSICAL_PX },
2066            sender,
2067            &test_node,
2068            metrics::MetricsLogger::default(),
2069        );
2070        let (mouse_handler_res, _) = futures::join!(mouse_handler_fut, config_request_stream_fut);
2071        let mouse_handler = mouse_handler_res.expect("Failed to create mouse handler");
2072
2073        // Create a channel for the the registered device's handle to be forwarded to the
2074        // DeviceRequestStream handler. This allows the registry_fut to complete and allows
2075        // handle_input_event() to continue.
2076        let (injector_stream_sender, injector_stream_receiver) =
2077            mpsc::unbounded::<Vec<pointerinjector::Event>>();
2078        // Up to 2 events per handle_input_event() call.
2079        let mut injector_stream_receiver = injector_stream_receiver.ready_chunks(2);
2080        let registry_fut = handle_registry_request_stream2(
2081            injector_registry_request_stream,
2082            injector_stream_sender,
2083        );
2084
2085        let event_time1 = zx::MonotonicInstant::get();
2086        let event_time2 = event_time1.add(zx::MonotonicDuration::from_micros(1));
2087        let event_time3 = event_time2.add(zx::MonotonicDuration::from_micros(1));
2088        let event_time4 = event_time3.add(zx::MonotonicDuration::from_micros(1));
2089
2090        // Run future until the handler future completes.
2091        let _registry_task = fasync::Task::local(registry_fut);
2092
2093        let zero_location =
2094            mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
2095                counts: Position { x: 0.0, y: 0.0 },
2096            });
2097        let expected_position = Position { x: 50.0, y: 50.0 };
2098
2099        let down_event = create_mouse_event(
2100            zero_location,
2101            None, /* wheel_delta_v */
2102            None, /* wheel_delta_h */
2103            None, /* is_precision_scroll */
2104            mouse_binding::MousePhase::Down,
2105            SortedVecSet::from(vec![1]),
2106            SortedVecSet::from(vec![1]),
2107            event_time1,
2108            &DESCRIPTOR,
2109        );
2110
2111        let wheel_event = create_mouse_event(
2112            zero_location,
2113            wheel_delta_ticks(1, None),               /* wheel_delta_v */
2114            None,                                     /* wheel_delta_h */
2115            Some(mouse_binding::PrecisionScroll::No), /* is_precision_scroll */
2116            mouse_binding::MousePhase::Wheel,
2117            SortedVecSet::from(vec![1]),
2118            SortedVecSet::from(vec![1]),
2119            event_time2,
2120            &DESCRIPTOR,
2121        );
2122
2123        let up_event = create_mouse_event(
2124            zero_location,
2125            None,
2126            None,
2127            None, /* is_precision_scroll */
2128            mouse_binding::MousePhase::Up,
2129            SortedVecSet::from(vec![1]),
2130            SortedVecSet::new(),
2131            event_time3,
2132            &DESCRIPTOR,
2133        );
2134
2135        let continue_wheel_event = create_mouse_event(
2136            zero_location,
2137            wheel_delta_ticks(1, None),               /* wheel_delta_v */
2138            None,                                     /* wheel_delta_h */
2139            Some(mouse_binding::PrecisionScroll::No), /* is_precision_scroll */
2140            mouse_binding::MousePhase::Wheel,
2141            SortedVecSet::new(),
2142            SortedVecSet::new(),
2143            event_time4,
2144            &DESCRIPTOR,
2145        );
2146
2147        // Handle button down event.
2148        mouse_handler.clone().handle_input_event(down_event).await;
2149        assert_eq!(
2150            injector_stream_receiver
2151                .next()
2152                .await
2153                .map(|events| events.into_iter().flatten().collect()),
2154            Some(vec![
2155                create_mouse_pointer_sample_event_phase_add(
2156                    vec![1],
2157                    expected_position,
2158                    event_time1,
2159                ),
2160                create_mouse_pointer_sample_event(
2161                    pointerinjector::EventPhase::Change,
2162                    vec![1],
2163                    expected_position,
2164                    None, /*relative_motion*/
2165                    None, /*wheel_delta_v*/
2166                    None, /*wheel_delta_h*/
2167                    None, /*is_precision_scroll*/
2168                    event_time1,
2169                ),
2170            ])
2171        );
2172
2173        // Handle wheel event with button pressing.
2174        mouse_handler.clone().handle_input_event(wheel_event).await;
2175        assert_eq!(
2176            injector_stream_receiver
2177                .next()
2178                .await
2179                .map(|events| events.into_iter().flatten().collect()),
2180            Some(vec![create_mouse_pointer_sample_event(
2181                pointerinjector::EventPhase::Change,
2182                vec![1],
2183                expected_position,
2184                None,        /*relative_motion*/
2185                Some(1),     /*wheel_delta_v*/
2186                None,        /*wheel_delta_h*/
2187                Some(false), /*is_precision_scroll*/
2188                event_time2,
2189            )])
2190        );
2191
2192        // Handle button up event.
2193        mouse_handler.clone().handle_input_event(up_event).await;
2194        assert_eq!(
2195            injector_stream_receiver
2196                .next()
2197                .await
2198                .map(|events| events.into_iter().flatten().collect()),
2199            Some(vec![create_mouse_pointer_sample_event(
2200                pointerinjector::EventPhase::Change,
2201                vec![],
2202                expected_position,
2203                None, /*relative_motion*/
2204                None, /*wheel_delta_v*/
2205                None, /*wheel_delta_h*/
2206                None, /*is_precision_scroll*/
2207                event_time3,
2208            )])
2209        );
2210
2211        // Handle wheel event after button released.
2212        mouse_handler.clone().handle_input_event(continue_wheel_event).await;
2213        assert_eq!(
2214            injector_stream_receiver
2215                .next()
2216                .await
2217                .map(|events| events.into_iter().flatten().collect()),
2218            Some(vec![create_mouse_pointer_sample_event(
2219                pointerinjector::EventPhase::Change,
2220                vec![],
2221                expected_position,
2222                None,        /*relative_motion*/
2223                Some(1),     /*wheel_delta_v*/
2224                None,        /*wheel_delta_h*/
2225                Some(false), /*is_precision_scroll*/
2226                event_time4,
2227            )])
2228        );
2229    }
2230
2231    #[fuchsia::test(allow_stalls = false)]
2232    async fn mouse_injector_handler_initialized_with_inspect_node() {
2233        let (configuration_proxy, mut configuration_request_stream) =
2234            fidl::endpoints::create_proxy_and_stream::<pointerinjector_config::SetupMarker>();
2235        let config_request_stream_fut =
2236            handle_configuration_request_stream(&mut configuration_request_stream);
2237        let (sender, _) = futures::channel::mpsc::channel::<CursorMessage>(1);
2238        let inspector = fuchsia_inspect::Inspector::default();
2239        let fake_handlers_node = inspector.root().create_child("input_handlers_node");
2240        let incoming = Incoming::new();
2241        let mouse_handler_fut = MouseInjectorHandler::new_with_config_proxy(
2242            &incoming,
2243            configuration_proxy,
2244            Size { width: DISPLAY_WIDTH_IN_PHYSICAL_PX, height: DISPLAY_HEIGHT_IN_PHYSICAL_PX },
2245            sender,
2246            &fake_handlers_node,
2247            metrics::MetricsLogger::default(),
2248        );
2249        let (mouse_handler_res, _) = futures::join!(mouse_handler_fut, config_request_stream_fut);
2250        let _handler = mouse_handler_res.expect("Failed to create mouse handler");
2251
2252        diagnostics_assertions::assert_data_tree!(inspector, root: {
2253            input_handlers_node: {
2254                mouse_injector_handler: {
2255                    events_received_count: 0u64,
2256                    events_handled_count: 0u64,
2257                    last_received_timestamp_ns: 0u64,
2258                    "fuchsia.inspect.Health": {
2259                        status: "STARTING_UP",
2260                        // Timestamp value is unpredictable and not relevant in this context,
2261                        // so we only assert that the property is present.
2262                        start_timestamp_nanos: diagnostics_assertions::AnyProperty
2263                    },
2264                }
2265            }
2266        });
2267    }
2268
2269    #[fuchsia::test(allow_stalls = false)]
2270    async fn mouse_injector_handler_inspect_counts_events() {
2271        // Set up fidl streams.
2272        let (configuration_proxy, mut configuration_request_stream) =
2273            fidl::endpoints::create_proxy_and_stream::<pointerinjector_config::SetupMarker>();
2274        let (injector_registry_proxy, injector_registry_request_stream) = next_client_old_stream::<
2275            pointerinjector::RegistryMarker,
2276            pointerinjector_next::Registry,
2277        >();
2278        let (sender, _) = futures::channel::mpsc::channel::<CursorMessage>(1);
2279
2280        let inspector = fuchsia_inspect::Inspector::default();
2281        let fake_handlers_node = inspector.root().create_child("input_handlers_node");
2282
2283        // Create mouse handler.
2284        let mouse_handler_fut = MouseInjectorHandler::new_handler(
2285            configuration_proxy,
2286            injector_registry_proxy,
2287            Size { width: DISPLAY_WIDTH_IN_PHYSICAL_PX, height: DISPLAY_HEIGHT_IN_PHYSICAL_PX },
2288            sender,
2289            &fake_handlers_node,
2290            metrics::MetricsLogger::default(),
2291        );
2292        let config_request_stream_fut =
2293            handle_configuration_request_stream(&mut configuration_request_stream);
2294
2295        let (mouse_handler_res, _) = futures::join!(mouse_handler_fut, config_request_stream_fut);
2296        let mouse_handler = mouse_handler_res.expect("Failed to create mouse handler");
2297
2298        let cursor_location = mouse_binding::MouseLocation::Absolute(Position { x: 0.0, y: 0.0 });
2299        let event_time1 = zx::MonotonicInstant::get();
2300        let event_time2 = event_time1.add(zx::MonotonicDuration::from_micros(1));
2301        let event_time3 = event_time2.add(zx::MonotonicDuration::from_micros(1));
2302
2303        let input_events = vec![
2304            create_mouse_event(
2305                cursor_location,
2306                None, /* wheel_delta_v */
2307                None, /* wheel_delta_h */
2308                None, /* is_precision_scroll */
2309                mouse_binding::MousePhase::Down,
2310                SortedVecSet::from(vec![1]),
2311                SortedVecSet::from(vec![1]),
2312                event_time1,
2313                &DESCRIPTOR,
2314            ),
2315            create_mouse_event(
2316                cursor_location,
2317                None, /* wheel_delta_v */
2318                None, /* wheel_delta_h */
2319                None, /* is_precision_scroll */
2320                mouse_binding::MousePhase::Up,
2321                SortedVecSet::from(vec![1]),
2322                SortedVecSet::new(),
2323                event_time2,
2324                &DESCRIPTOR,
2325            ),
2326            create_mouse_event_with_handled(
2327                cursor_location,
2328                None, /* wheel_delta_v */
2329                None, /* wheel_delta_h */
2330                None, /* is_precision_scroll */
2331                mouse_binding::MousePhase::Down,
2332                SortedVecSet::from(vec![1]),
2333                SortedVecSet::from(vec![1]),
2334                event_time3,
2335                &DESCRIPTOR,
2336                input_device::Handled::Yes,
2337            ),
2338        ];
2339
2340        // Create a channel for the the registered device's handle to be forwarded to the
2341        // DeviceRequestStream handler. This allows the registry_fut to complete and allows
2342        // handle_input_event() to continue.
2343        let (injector_stream_sender, _) = mpsc::unbounded::<Vec<pointerinjector::Event>>();
2344        let registry_fut = handle_registry_request_stream2(
2345            injector_registry_request_stream,
2346            injector_stream_sender,
2347        );
2348
2349        // Run future until the handler future completes.
2350        let _registry_task = fasync::Task::local(registry_fut);
2351        for input_event in input_events {
2352            mouse_handler.clone().handle_input_event(input_event).await;
2353        }
2354
2355        let last_received_event_time: u64 = event_time2.into_nanos().try_into().unwrap();
2356
2357        diagnostics_assertions::assert_data_tree!(inspector, root: {
2358            input_handlers_node: {
2359                mouse_injector_handler: {
2360                    events_received_count: 2u64,
2361                    events_handled_count: 2u64,
2362                    last_received_timestamp_ns: last_received_event_time,
2363                    "fuchsia.inspect.Health": {
2364                        status: "STARTING_UP",
2365                        // Timestamp value is unpredictable and not relevant in this context,
2366                        // so we only assert that the property is present.
2367                        start_timestamp_nanos: diagnostics_assertions::AnyProperty
2368                    },
2369                }
2370            }
2371        });
2372    }
2373}