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