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