Skip to main content

input_pipeline_dso/gestures/
gesture_arena.rs

1// Copyright 2022 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
5use super::{
6    args, motion, one_finger_button, primary_tap, scroll, secondary_button, secondary_tap,
7};
8use crate::input_handler::{Handler, InputHandler, InputHandlerStatus};
9use crate::utils::Size;
10use crate::{MonotonicInstant, input_device, metrics, mouse_binding, touch_binding};
11use anyhow::{Context, Error, format_err};
12use async_trait::async_trait;
13use core::cell::RefCell;
14use fidl_fuchsia_input_report as fidl_input_report;
15use fuchsia_inspect::health::Reporter;
16use fuchsia_inspect::{ArrayProperty, Node as InspectNode};
17use fuchsia_inspect_contrib::nodes::BoundedListNode;
18use metrics_registry::InputPipelineErrorMetricDimensionEvent;
19use std::any::Any;
20use std::fmt::Debug;
21
22struct GestureArenaInitialContenders {}
23
24impl ContenderFactory for GestureArenaInitialContenders {
25    fn make_contenders(&self) -> Vec<Box<dyn Contender>> {
26        vec![
27            Box::new(motion::InitialContender {
28                min_movement_in_mm: args::SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_MM,
29            }),
30            Box::new(primary_tap::InitialContender {
31                max_finger_displacement_in_mm: args::SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_MM,
32                max_time_elapsed: args::TAP_TIMEOUT,
33            }),
34            Box::new(secondary_tap::InitialContender {
35                max_finger_displacement_in_mm: args::SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_MM,
36                max_time_elapsed: args::TAP_TIMEOUT,
37            }),
38            Box::new(scroll::InitialContender {
39                motion_threshold_in_mm: args::SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_MM,
40                min_movement_in_mm: args::SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_MM,
41                max_movement_in_mm: args::MAX_SPURIOUS_TO_INTENTIONAL_SCROLL_THRESHOLD_MM,
42                limit_tangent_for_direction: args::MAX_SCROLL_DIRECTION_SKEW_DEGREES
43                    .to_radians()
44                    .tan(),
45            }),
46            Box::new(one_finger_button::InitialContender {
47                spurious_to_intentional_motion_threshold_mm:
48                    args::SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_MM,
49                spurious_to_intentional_motion_threshold_button_change_mm:
50                    args::SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_BUTTON_CHANGE_MM,
51                button_change_state_timeout: args::BUTTON_CHANGE_STATE_TIMEOUT,
52            }),
53            Box::new(secondary_button::InitialContender {
54                spurious_to_intentional_motion_threshold_mm:
55                    args::SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_MM,
56                spurious_to_intentional_motion_threshold_button_change_mm:
57                    args::SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_BUTTON_CHANGE_MM,
58                button_change_state_timeout: args::BUTTON_CHANGE_STATE_TIMEOUT,
59            }),
60        ]
61    }
62}
63
64pub fn make_input_handler(
65    inspect_node: &InspectNode,
66    input_handlers_node: &InspectNode,
67    metrics_logger: metrics::MetricsLogger,
68) -> std::rc::Rc<dyn crate::input_handler::BatchInputHandler> {
69    // TODO(https://fxbug.dev/42056283): Remove log message.
70    log::info!("touchpad: created input handler");
71    std::rc::Rc::new(GestureArena::new_internal(
72        Box::new(GestureArenaInitialContenders {}),
73        inspect_node,
74        MAX_TOUCHPAD_EVENT_LOG_ENTRIES,
75        input_handlers_node,
76        metrics_logger,
77    ))
78}
79
80pub(super) const PRIMARY_BUTTON: mouse_binding::MouseButton = 1;
81pub(super) const SECONDARY_BUTTON: mouse_binding::MouseButton = 2;
82
83// TODO(https://fxbug.dev/42053614): check that we've removed all leading `_` from types
84// and variables in this file.
85#[derive(Debug, Clone, PartialEq)]
86pub(super) struct TouchpadEvent {
87    pub(super) timestamp: zx::MonotonicInstant,
88    // TODO(https://fxbug.dev/42053615): replace these fields with a field that embeds
89    // `touch_data: super::touch_binding::TouchpadEvent`.
90    pub(super) pressed_buttons: Vec<u8>,
91    pub(super) contacts: Vec<touch_binding::TouchContact>,
92    pub(super) filtered_palm_contacts: Vec<touch_binding::TouchContact>,
93}
94
95#[derive(Debug, PartialEq)]
96pub(super) struct MouseEvent {
97    pub(super) timestamp: zx::MonotonicInstant,
98    pub(super) mouse_data: mouse_binding::MouseEvent,
99}
100
101#[derive(Debug)]
102pub(super) struct DetailedReasonUint {
103    pub(super) criterion: &'static str,
104    pub(super) min: Option<u64>,
105    pub(super) max: Option<u64>,
106    pub(super) actual: usize,
107}
108
109#[derive(Debug)]
110pub(super) struct DetailedReasonFloat {
111    pub(super) criterion: &'static str,
112    pub(super) min: Option<f32>,
113    pub(super) max: Option<f32>,
114    pub(super) actual: f32,
115}
116
117#[derive(Debug)]
118pub(super) struct DetailedReasonInt {
119    pub(super) criterion: &'static str,
120    pub(super) min: Option<i64>,
121    pub(super) max: Option<i64>,
122    pub(super) actual: i64,
123}
124
125#[derive(Debug)]
126pub(super) enum Reason {
127    Basic(&'static str),
128    DetailedUint(DetailedReasonUint),
129    DetailedFloat(DetailedReasonFloat),
130    DetailedInt(DetailedReasonInt),
131}
132
133#[derive(Debug)]
134pub(super) enum ExamineEventResult {
135    Contender(Box<dyn Contender>),
136    MatchedContender(Box<dyn MatchedContender>),
137    Mismatch(Reason),
138}
139
140pub(super) trait Contender: std::fmt::Debug + AsAny {
141    /// Examines `event`, to determine whether or not the gesture
142    /// is relevant to this `Recognizer`.
143    ///
144    /// Returns
145    /// * `ExamineEventResult::MatchedContender` if this recognizer wants
146    ///   to send (or start sending) events downstream, OR
147    /// * `ExamineEventResult::Contender` if this recognizer is not yet
148    ///   ready to send events downstream, but wants to continue
149    ///   contending for the gesture, OR
150    /// * `ExamineEventResult::Mismatch` if this recognizer no longer
151    ///   wants to contend for this gesture
152    fn examine_event(self: Box<Self>, event: &TouchpadEvent) -> ExamineEventResult;
153
154    /// Returns a string that uniquely identifies the concrete type
155    /// of this implementation of the `Contender` trait.
156    fn get_type_name(&self) -> &'static str {
157        std::any::type_name::<Self>()
158    }
159
160    /// If the gesture need to start from idle(no finger contacting the surface).
161    fn start_from_idle(&self) -> bool {
162        false
163    }
164}
165
166pub trait AsAny {
167    #[allow(dead_code)] // only used in test
168    fn as_any(&self) -> &dyn Any;
169}
170
171impl<T: Any> AsAny for T {
172    fn as_any(&self) -> &dyn Any {
173        self
174    }
175}
176
177#[derive(Debug)]
178pub(super) enum VerifyEventResult {
179    MatchedContender(Box<dyn MatchedContender>),
180    Mismatch(Reason),
181}
182
183#[derive(Clone, Copy, Debug, PartialEq)]
184pub(super) enum RecognizedGesture {
185    /// Contains one variant for each recognizer, and the
186    /// special value `Unrecognized` for when no recognizer
187    /// claims the gesture.
188    _Unrecognized,
189    PrimaryTap,
190    SecondaryTap,
191    Motion,
192    Scroll,
193    OneButtonDown,
194    SecondaryButtonDown,
195}
196
197#[derive(Debug)]
198pub(super) struct ProcessBufferedEventsResult {
199    pub(super) generated_events: Vec<MouseEvent>,
200    pub(super) winner: Option<Box<dyn Winner>>,
201    pub(super) recognized_gesture: RecognizedGesture, // for latency breakdown
202}
203
204pub(super) trait MatchedContender: std::fmt::Debug + AsAny {
205    /// Verifies that `event` still matches the gesture that is relevant
206    /// to this `Recognizer`.
207    ///
208    /// Returns
209    /// * `VerifyEventResult::MatchedContender` if this recognizer wants
210    ///   to send (or start sending) events downstream, OR
211    /// * `VerifyEventResult::Mismatch` if this recognizer no longer
212    ///   wants to contend for this gesture
213    fn verify_event(self: Box<Self>, event: &TouchpadEvent) -> VerifyEventResult;
214
215    /// Takes `events`, and generates corresponding `MouseEvent`s.
216    ///
217    /// Returns `ProcessBufferedEventsResult` with fields:
218    /// * `generated_events`: the sequence of `MouseEvent`s needed
219    ///   to effect the gesture downstream.
220    /// * `winner`:
221    ///   * `None` if the gesture is complete
222    ///   * `Some` otherwise
223    ///
224    /// Note:
225    /// * `generated_events` MAY be empty; for example, a palm
226    ///   recognizer wants to discard unintended events
227    /// * `events` is guaranteed to contains exactly the sequence of
228    ///   `TouchpadEvent`s that this recognizer has already examined
229    ///   and verified.
230    /// * recognizers MAY choose to ignore `events`
231    ///   e.g.:
232    ///   * a one-finger-tap recognizer does not need to inspect
233    ///     `events` to generate the `MouseEvent`s for the button click
234    ///   * a motion recognizer, in contrast, needs the details in
235    ///     `events` to generate the appropriate motion
236    fn process_buffered_events(
237        self: Box<Self>,
238        events: Vec<TouchpadEvent>,
239    ) -> ProcessBufferedEventsResult;
240
241    /// Returns a string that uniquely identifies the concrete type
242    /// of this implementation of the `MatchedContender` trait.
243    fn get_type_name(&self) -> &'static str {
244        std::any::type_name::<Self>()
245    }
246}
247
248#[derive(Debug, PartialEq)]
249pub(super) enum EndGestureEvent {
250    // It still possible to use EndGestureEvent::GeneratedEvent when
251    // we support scroll phase, keep it and related tests.
252    #[allow(dead_code)]
253    GeneratedEvent(MouseEvent),
254    UnconsumedEvent(TouchpadEvent),
255    NoEvent,
256}
257
258#[derive(Debug)]
259pub(super) enum ProcessNewEventResult {
260    ContinueGesture(Option<MouseEvent>, Box<dyn Winner>),
261    EndGesture(EndGestureEvent, Reason),
262}
263
264pub(super) trait Winner: std::fmt::Debug {
265    /// Takes `event`, and generates corresponding `MouseEvent`s.
266    ///
267    /// Returns:
268    /// * `ContinueGesture(Some, …)` if the gesture is still
269    ///   in progress, and a `MouseEvent` should be sent downstream;
270    ///   might be used, e.g., by a motion recognizer
271    /// * `ContinueGesutre(None, …)` if the gesture is still
272    ///   in progress, and no `MouseEvent` should be sent downstream;
273    ///   might be used, e.g., by a palm recognizer
274    /// * `EndGesture(UnconsumedEvent, …)` if the gesture has ended because
275    ///   `event` did not match; might be used, e.g., if the user
276    ///    presses the touchpad down after a motion gesture
277    /// * `EndGesture(GeneratedEvent, …)` if the gesture has ended because
278    ///   `event` did end the gesture; might be used, e.g., if the user
279    ///    release the touchpad down after a drag gesture
280    /// * `EndGesture(NoEvent, …)` if `event` matches a normal end
281    ///   of the gesture; might be used, e.g., if the user lifts
282    ///   their finger off the touchpad after a motion gesture
283    fn process_new_event(self: Box<Self>, event: TouchpadEvent) -> ProcessNewEventResult;
284
285    /// Returns a string that uniquely defines the concrete type
286    /// of this implementation of the `Winner` trait.
287    fn get_type_name(&self) -> &'static str {
288        std::any::type_name::<Self>()
289    }
290}
291
292const MAX_TOUCHPAD_EVENT_LOG_ENTRIES: usize = 1250; // 125 Hz * 10 seconds
293
294#[derive(Debug)]
295enum MutableState {
296    /// Looking for the start of any gesture.
297    Idle,
298
299    /// Looking for the start of a gesture that can be chained after
300    /// a previous gesture. "Chaining" is using two or more gestures
301    /// in sequence, while always having at least one finger in contact
302    /// with the pad.
303    ///
304    /// Note that this does _not_ require any particular finger to
305    /// be continually in contact with the pad. For example, the
306    /// following is a valid chain:
307    ///    1. Index finger
308    ///    2. Index finger + middle finger
309    ///    3. Middle finger
310    Chain,
311
312    /// Disambiguating the current gesture.
313    Matching {
314        contenders: Vec<Box<dyn Contender>>,
315        matched_contenders: Vec<Box<dyn MatchedContender>>,
316        first_event_timestamp: zx::MonotonicInstant,
317        buffered_events: Vec<TouchpadEvent>,
318    },
319
320    /// The matching gesture has been identified, and is still in progress.
321    Forwarding {
322        winner: Box<dyn Winner>,
323        current_gesture: RecognizedGesture,
324        gesture_start_timestamp: zx::MonotonicInstant,
325        num_events: usize,
326    },
327
328    /// A transient state during the processing of a single `InputEvent`.
329    Invalid,
330}
331
332/// Names for the `MutableState`s. Exists to support the state machine debug log.
333#[derive(Debug, PartialEq)]
334enum StateName {
335    Idle,
336    Chain,
337    Matching,
338    Forwarding,
339    Invalid,
340}
341
342trait ContenderFactory {
343    fn make_contenders(&self) -> Vec<Box<dyn Contender>>;
344}
345
346pub(super) struct GestureArena {
347    contender_factory: Box<dyn ContenderFactory>,
348    mutable_state: RefCell<MutableState>,
349    inspect_log: RefCell<BoundedListNode>,
350    /// The metrics logger.
351    metrics_logger: metrics::MetricsLogger,
352    /// The inventory of this handler's Inspect status.
353    inspect_status: InputHandlerStatus,
354}
355
356impl GestureArena {
357    #[cfg(test)]
358    fn new_for_test(
359        contender_factory: Box<dyn ContenderFactory>,
360        inspector: &fuchsia_inspect::Inspector,
361        max_inspect_log_entries: usize,
362        metrics_logger: metrics::MetricsLogger,
363    ) -> GestureArena {
364        let test_node = inspector.root().create_child("test_node");
365        Self::new_internal(
366            contender_factory,
367            inspector.root(),
368            max_inspect_log_entries,
369            &test_node,
370            metrics_logger,
371        )
372    }
373
374    fn new_internal(
375        contender_factory: Box<dyn ContenderFactory>,
376        inspect_node: &InspectNode,
377        max_inspect_log_entries: usize,
378        input_handlers_node: &InspectNode,
379        metrics_logger: metrics::MetricsLogger,
380    ) -> GestureArena {
381        let inspect_status = InputHandlerStatus::new(
382            input_handlers_node,
383            "gesture_arena",
384            /* generates_events */ true,
385        );
386        GestureArena {
387            contender_factory,
388            mutable_state: RefCell::new(MutableState::Idle),
389            inspect_log: RefCell::new(BoundedListNode::new(
390                inspect_node.create_child("gestures_event_log"),
391                max_inspect_log_entries,
392            )),
393            metrics_logger,
394            inspect_status,
395        }
396    }
397
398    #[cfg(test)]
399    pub(super) fn has_buffered_events(self: std::rc::Rc<Self>) -> bool {
400        match &*self.mutable_state.borrow() {
401            MutableState::Matching { buffered_events, .. } => buffered_events.len() > 0,
402            _ => false,
403        }
404    }
405}
406
407impl TouchpadEvent {
408    fn log_inspect(&self, log_entry_node: &InspectNode) {
409        let touchpad_event_node = log_entry_node.create_child("touchpad_event");
410
411        // Create an inspect array from the pressed buttons.
412        let pressed_buttons_node =
413            touchpad_event_node.create_uint_array("pressed_buttons", self.pressed_buttons.len());
414        self.pressed_buttons.iter().enumerate().for_each(|(i, &button_id)| {
415            pressed_buttons_node.set(i, button_id);
416        });
417
418        // Populate the touchpad event details
419        log_common(&touchpad_event_node, self.timestamp);
420        touchpad_event_node.record(pressed_buttons_node);
421        touchpad_event_node.record_child("contacts", |contact_set_node| {
422            self.contacts.iter().for_each(|contact| {
423                contact_set_node.record_child(contact.id.to_string(), |contact_node| {
424                    contact_node.record_double("pos_x_mm", f64::from(contact.position.x));
425                    contact_node.record_double("pos_y_mm", f64::from(contact.position.y));
426                    if let Some(contact_size) = contact.contact_size {
427                        contact_node.record_double("width_mm", f64::from(contact_size.width));
428                        contact_node.record_double("height_mm", f64::from(contact_size.height));
429                    }
430                })
431            })
432        });
433        touchpad_event_node.record_child("filtered_palm_contacts", |contact_set_node| {
434            self.filtered_palm_contacts.iter().for_each(|contact| {
435                contact_set_node.record_child(contact.id.to_string(), |contact_node| {
436                    contact_node.record_double("pos_x_mm", f64::from(contact.position.x));
437                    contact_node.record_double("pos_y_mm", f64::from(contact.position.y));
438                    if let Some(contact_size) = contact.contact_size {
439                        contact_node.record_double("width_mm", f64::from(contact_size.width));
440                        contact_node.record_double("height_mm", f64::from(contact_size.height));
441                    }
442                })
443            })
444        });
445
446        // Pass ownership of the touchpad event node to the log.
447        log_entry_node.record(touchpad_event_node);
448    }
449}
450
451impl RecognizedGesture {
452    fn to_str(&self) -> &'static str {
453        match self {
454            RecognizedGesture::_Unrecognized => "_unrecognized",
455            RecognizedGesture::PrimaryTap => "primary_tap",
456            RecognizedGesture::SecondaryTap => "secondary_tap",
457            RecognizedGesture::Motion => "motion",
458            RecognizedGesture::Scroll => "scroll",
459            RecognizedGesture::OneButtonDown => "one_button_down",
460            RecognizedGesture::SecondaryButtonDown => "secondary_button_down",
461        }
462    }
463}
464
465fn log_common(inspect_node: &InspectNode, driver_timestamp: zx::MonotonicInstant) {
466    inspect_node.record_int("driver_monotonic_nanos", driver_timestamp.into_nanos());
467    inspect_node.record_int(
468        "entry_latency_micros",
469        // Use lower precision for latency, to minimize space.
470        (MonotonicInstant::now().into_zx() - driver_timestamp).into_micros(),
471    );
472}
473
474impl MutableState {
475    fn get_state_name(&self) -> StateName {
476        match self {
477            Self::Idle => StateName::Idle,
478            Self::Chain => StateName::Chain,
479            Self::Matching { .. } => StateName::Matching,
480            Self::Forwarding { .. } => StateName::Forwarding,
481            Self::Invalid => StateName::Invalid,
482        }
483    }
484}
485
486fn parse_touchpad_event(
487    event_time: &zx::MonotonicInstant,
488    touchpad_event: &touch_binding::TouchpadEvent,
489    touchpad_descriptor: &touch_binding::TouchpadDeviceDescriptor,
490) -> Result<TouchpadEvent, Error> {
491    let position_divisor =
492        get_position_divisor_to_mm(touchpad_descriptor).context("failed to compute divisor")?;
493    Ok(TouchpadEvent {
494        timestamp: *event_time,
495        pressed_buttons: touchpad_event.pressed_buttons.iter().copied().collect::<Vec<_>>(),
496        contacts: touchpad_event
497            .injector_contacts
498            .iter()
499            .map(|contact| touch_binding::TouchContact {
500                position: contact.position / position_divisor,
501                contact_size: match contact.contact_size {
502                    Some(size) => Some(Size {
503                        width: size.width / position_divisor,
504                        height: size.height / position_divisor,
505                    }),
506                    None => None,
507                },
508                ..*contact
509            })
510            .collect(),
511        filtered_palm_contacts: vec![],
512    })
513}
514
515fn filter_palm_contact(touchpad_event: TouchpadEvent) -> TouchpadEvent {
516    // Button down will make the contact of finger larger. We are not able to
517    // use the same threshold to distinguish finger or palm.
518    if touchpad_event.pressed_buttons.len() > 0 {
519        return touchpad_event;
520    }
521    let (contacts, filtered_palm_contacts) = touchpad_event.contacts.into_iter().fold(
522        (
523            Vec::<touch_binding::TouchContact>::default(),
524            Vec::<touch_binding::TouchContact>::default(),
525        ),
526        |mut out, contact| {
527            match contact.contact_size {
528                Some(size) => {
529                    if size.width < args::MIN_PALM_SIZE_MM && size.height < args::MIN_PALM_SIZE_MM {
530                        out.0.push(contact);
531                    } else {
532                        out.1.push(contact);
533                    }
534                }
535                None => {
536                    out.0.push(contact);
537                }
538            }
539            out
540        },
541    );
542
543    TouchpadEvent { contacts, filtered_palm_contacts, ..touchpad_event }
544}
545
546const COUNTS_PER_MM: u32 = 12;
547
548impl std::convert::From<MouseEvent> for input_device::InputEvent {
549    fn from(mouse_event: MouseEvent) -> input_device::InputEvent {
550        input_device::InputEvent {
551            // TODO(https://fxbug.dev/42056058): Convert `mouse_event.mouse_data.wheel_delta_v`
552            // and `mouse_event.mouse_data.wheel_delta_h` from micrometers to counts.
553            device_event: input_device::InputDeviceEvent::Mouse(mouse_event.mouse_data),
554            device_descriptor: input_device::InputDeviceDescriptor::Mouse(
555                mouse_binding::MouseDeviceDescriptor {
556                    // Use a large number for the `device_id` for the gesture arena's
557                    // virtual mouse, to avoid conflicts with real devices (which start
558                    // at ID 0).
559                    //
560                    // However, don't use u32::MAX, since the `InputDeviceRegistry`
561                    // FIDL handling code uses that for the first registered device
562                    // (and works downwards).
563                    device_id: u32::MAX / 2,
564                    absolute_x_range: Some(fidl_input_report::Range { min: -127, max: 127 }),
565                    absolute_y_range: Some(fidl_input_report::Range { min: -127, max: 127 }),
566                    wheel_v_range: Some(fidl_input_report::Axis {
567                        // Set range based on the range of `position_y` values for the
568                        // Atlas touchpad. The touchpad is 2.48 inches tall, and the
569                        // position is reported in micrometers. That gives `position_y`
570                        // a range of (0, 62992).
571                        //
572                        // Note that this range is likely larger than needed, since users
573                        // probably won't swipe all the way up/down the pad in a single
574                        // sampling interval (8msec).
575                        //
576                        // TODO(https://fxbug.dev/42056058): Adjust this range to reflect
577                        // the range in counts instead of micrometers.
578                        range: fidl_input_report::Range { min: -65535, max: 65535 },
579                        unit: fidl_input_report::Unit {
580                            type_: fidl_fuchsia_input_report::UnitType::Other,
581                            exponent: 0,
582                        },
583                    }),
584                    wheel_h_range: None,
585                    buttons: Some(vec![PRIMARY_BUTTON, SECONDARY_BUTTON]),
586                    // Downstream stages ignore `counts_per_mm`, so any value is fine.
587                    // TODO(https://fxbug.dev/42072124): remove `counts_per_mm` from descriptor.
588                    counts_per_mm: COUNTS_PER_MM,
589                },
590            ),
591            event_time: mouse_event.timestamp,
592            handled: input_device::Handled::No,
593            trace_id: None,
594        }
595    }
596}
597
598impl GestureArena {
599    /// Interprets `TouchpadEvent`s, and sends corresponding `MouseEvent`s downstream.
600    fn handle_touchpad_event(
601        self: std::rc::Rc<Self>,
602        event_time: &zx::MonotonicInstant,
603        touchpad_event: &touch_binding::TouchpadEvent,
604        device_descriptor: &touch_binding::TouchpadDeviceDescriptor,
605    ) -> Result<Vec<input_device::InputEvent>, Error> {
606        let touchpad_event = parse_touchpad_event(event_time, touchpad_event, device_descriptor)
607            .context("dropping touchpad event")?;
608        let touchpad_event = filter_palm_contact(touchpad_event);
609        self.inspect_log.borrow_mut().add_entry(|node| {
610            touchpad_event.log_inspect(node);
611        });
612
613        let old_state_name = self.mutable_state.borrow().get_state_name();
614        let (new_state, generated_events) = match self.mutable_state.replace(MutableState::Invalid)
615        {
616            MutableState::Idle => self.handle_event_while_idle(touchpad_event),
617            MutableState::Chain => self.handle_event_while_chain(touchpad_event),
618            MutableState::Matching {
619                contenders,
620                matched_contenders,
621                first_event_timestamp,
622                buffered_events,
623            } => self.handle_event_while_matching(
624                contenders,
625                matched_contenders,
626                first_event_timestamp,
627                buffered_events,
628                touchpad_event,
629            ),
630            MutableState::Forwarding {
631                winner,
632                current_gesture,
633                gesture_start_timestamp,
634                num_events,
635            } => self.handle_event_while_forwarding(
636                winner,
637                current_gesture,
638                gesture_start_timestamp,
639                num_events + 1,
640                touchpad_event,
641            ),
642            MutableState::Invalid => {
643                unreachable!();
644            }
645        };
646        log::debug!("gesture_arena: {:?} -> {:?}", old_state_name, new_state.get_state_name());
647        self.mutable_state.replace(new_state);
648        // self.log_mutable_state();  // uncomment to log contender set
649        Ok(generated_events.into_iter().map(input_device::InputEvent::from).collect())
650    }
651
652    fn handle_event_while_idle(&self, new_event: TouchpadEvent) -> (MutableState, Vec<MouseEvent>) {
653        let (contenders, matched_contenders) = self
654            .contender_factory
655            .make_contenders()
656            .into_iter()
657            .map(|contender| (contender.get_type_name(), contender.examine_event(&new_event)))
658            .fold(
659                (vec![], vec![]),
660                |(mut contenders, mut matched_contenders), (type_name, examine_result)| {
661                    match examine_result {
662                        ExamineEventResult::Contender(contender) => {
663                            contenders.push(contender);
664                        }
665                        ExamineEventResult::MatchedContender(matched_contender) => {
666                            matched_contenders.push(matched_contender);
667                        }
668                        ExamineEventResult::Mismatch(mismatch_data) => {
669                            self.inspect_log.borrow_mut().add_entry(|node| {
670                                log_mismatch_reason(node, type_name, mismatch_data);
671                            });
672                        }
673                    }
674                    (contenders, matched_contenders)
675                },
676            );
677        let event_timestamp = new_event.timestamp;
678        self.transit_based_on_contenders_matched_contenders(
679            vec![],
680            new_event,
681            event_timestamp,
682            contenders,
683            matched_contenders,
684        )
685    }
686
687    fn handle_event_while_chain(
688        &self,
689        new_event: TouchpadEvent,
690    ) -> (MutableState, Vec<MouseEvent>) {
691        let (contenders, matched_contenders) = self
692            .contender_factory
693            .make_contenders()
694            .into_iter()
695            .filter(|contender| !contender.start_from_idle())
696            .map(|contender| (contender.get_type_name(), contender.examine_event(&new_event)))
697            .fold(
698                (vec![], vec![]),
699                |(mut contenders, mut matched_contenders), (type_name, examine_result)| {
700                    match examine_result {
701                        ExamineEventResult::Contender(contender) => {
702                            contenders.push(contender);
703                        }
704                        ExamineEventResult::MatchedContender(matched_contender) => {
705                            matched_contenders.push(matched_contender);
706                        }
707                        ExamineEventResult::Mismatch(mismatch_data) => {
708                            self.inspect_log.borrow_mut().add_entry(|node| {
709                                log_mismatch_reason(node, type_name, mismatch_data);
710                            });
711                        }
712                    }
713                    (contenders, matched_contenders)
714                },
715            );
716        let event_timestamp = new_event.timestamp;
717        self.transit_based_on_contenders_matched_contenders(
718            vec![],
719            new_event,
720            event_timestamp,
721            contenders,
722            matched_contenders,
723        )
724    }
725
726    fn handle_event_while_matching(
727        &self,
728        contenders: Vec<Box<dyn Contender>>,
729        matched_contenders: Vec<Box<dyn MatchedContender>>,
730        first_event_timestamp: zx::MonotonicInstant,
731        buffered_events: Vec<TouchpadEvent>,
732        new_event: TouchpadEvent,
733    ) -> (MutableState, Vec<MouseEvent>) {
734        // Note: This function processes `contenders` after `matched_contenders` to ensure
735        // that a recognizer doesn't get invoked twice with the same event. Otherwise, it
736        // would be possible for the following sequence of events to occur:
737        //
738        // 1. This function calls some_recognizer::Contender.examine_event(&new_event).
739        // 2. The call returns some_recognizer::MatchedContender.
740        // 3. This function calls some_recognizer::MatchedContender.verify_event(&new_event).
741        //
742        // See also: `does_not_repeat_event_to_matched_contender_returned_by_examine_event` test.
743        let matched_contenders = matched_contenders
744            .into_iter()
745            .map(|matched_contender| {
746                (matched_contender.get_type_name(), matched_contender.verify_event(&new_event))
747            })
748            .fold(vec![], |mut matched_contenders, (type_name, verify_result)| {
749                match verify_result {
750                    VerifyEventResult::MatchedContender(m) => {
751                        matched_contenders.push(m);
752                    }
753                    VerifyEventResult::Mismatch(mismatch_data) => {
754                        self.inspect_log.borrow_mut().add_entry(|node| {
755                            log_mismatch_reason(node, type_name, mismatch_data);
756                        });
757                    }
758                }
759                matched_contenders
760            });
761        let (contenders, matched_contenders) = contenders
762            .into_iter()
763            .map(|contender| (contender.get_type_name(), contender.examine_event(&new_event)))
764            .fold(
765                (vec![], matched_contenders),
766                |(mut contenders, mut matched_contenders), (type_name, examine_result)| {
767                    match examine_result {
768                        ExamineEventResult::Contender(contender) => {
769                            contenders.push(contender);
770                        }
771                        ExamineEventResult::MatchedContender(matched_contender) => {
772                            matched_contenders.push(matched_contender);
773                        }
774                        ExamineEventResult::Mismatch(mismatch_data) => {
775                            self.inspect_log.borrow_mut().add_entry(|node| {
776                                log_mismatch_reason(node, type_name, mismatch_data);
777                            });
778                        }
779                    }
780                    (contenders, matched_contenders)
781                },
782            );
783
784        self.transit_based_on_contenders_matched_contenders(
785            buffered_events,
786            new_event,
787            first_event_timestamp,
788            contenders,
789            matched_contenders,
790        )
791    }
792
793    fn transit_based_on_contenders_matched_contenders(
794        &self,
795        buffered_events: Vec<TouchpadEvent>,
796        new_event: TouchpadEvent,
797        first_event_timestamp: zx::MonotonicInstant,
798        contenders: Vec<Box<dyn Contender>>,
799        matched_contenders: Vec<Box<dyn MatchedContender>>,
800    ) -> (MutableState, Vec<MouseEvent>) {
801        let has_finger_contact = new_event.contacts.len() > 0;
802        let new_event_timestamp = new_event.timestamp;
803
804        match (
805            u8::try_from(contenders.len()).unwrap_or(u8::MAX),
806            u8::try_from(matched_contenders.len()).unwrap_or(u8::MAX),
807        ) {
808            // No `Contender`s or `MatchedContender`s. The contest has ended without
809            // identifying a gesture.
810            (0, 0) => {
811                if has_finger_contact {
812                    // One more contacts transit to chain. This is required when user
813                    // give an unknown gesture for example a 2 fingers swipe in no
814                    // direction and movement larger than threshold. Then finger lift,
815                    // Chain state will prevent it to be recognized as secondary tap.
816                    (MutableState::Chain, vec![])
817                } else {
818                    (MutableState::Idle, vec![])
819                }
820            }
821            // No `Contender`s, and exactly one `MatchedContender`. The contest has ended
822            // with a recognized gesture.
823            (0, 1) => {
824                let num_previous_events = buffered_events.len();
825                let mut buffered_events = buffered_events;
826                buffered_events.push(new_event);
827
828                let matched_contender = {
829                    let mut matched_contenders = matched_contenders;
830                    matched_contenders.remove(0)
831                };
832                let type_name = matched_contender.get_type_name();
833                let ProcessBufferedEventsResult { generated_events, winner, recognized_gesture } =
834                    matched_contender.process_buffered_events(buffered_events);
835                self.inspect_log.borrow_mut().add_entry(|node| {
836                    log_gesture_start(
837                        node,
838                        recognized_gesture,
839                        num_previous_events,
840                        new_event_timestamp - first_event_timestamp,
841                    );
842                });
843
844                match winner {
845                    Some(winner) => (
846                        MutableState::Forwarding {
847                            winner,
848                            current_gesture: recognized_gesture,
849                            gesture_start_timestamp: new_event_timestamp,
850                            num_events: 0,
851                        },
852                        generated_events,
853                    ),
854                    None => {
855                        self.inspect_log.borrow_mut().add_entry(|node| {
856                            log_gesture_end(
857                                node,
858                                type_name,
859                                recognized_gesture,
860                                Reason::Basic("discrete-recognizer"),
861                                0,
862                                zx::MonotonicDuration::from_nanos(0),
863                            );
864                        });
865                        if has_finger_contact {
866                            (MutableState::Chain, generated_events)
867                        } else {
868                            (MutableState::Idle, generated_events)
869                        }
870                    }
871                }
872            }
873            // At least 1 `Contender`, or 2 `MatchedContender`s; continue the contest.
874            (1.., _) | (_, 2..) => {
875                let mut buffered_events = buffered_events;
876                buffered_events.push(new_event);
877                (
878                    MutableState::Matching {
879                        contenders,
880                        matched_contenders,
881                        first_event_timestamp,
882                        buffered_events,
883                    },
884                    vec![],
885                )
886            }
887        }
888    }
889
890    fn handle_event_while_forwarding(
891        &self,
892        winner: Box<dyn Winner>,
893        current_gesture: RecognizedGesture,
894        gesture_start_timestamp: zx::MonotonicInstant,
895        num_events: usize,
896        new_event: TouchpadEvent,
897    ) -> (MutableState, Vec<MouseEvent>) {
898        let type_name = winner.get_type_name();
899        let has_finger_contact = new_event.contacts.len() > 0;
900        let new_event_timestamp = new_event.timestamp;
901
902        match winner.process_new_event(new_event) {
903            ProcessNewEventResult::ContinueGesture(generated_event, winner) => (
904                MutableState::Forwarding {
905                    winner,
906                    current_gesture,
907                    gesture_start_timestamp,
908                    num_events,
909                },
910                generated_event.into_iter().collect(),
911            ),
912            ProcessNewEventResult::EndGesture(
913                EndGestureEvent::GeneratedEvent(generated_event),
914                reason,
915            ) => {
916                log::debug!("touchpad: {} ended: {:?}", type_name, reason);
917                self.inspect_log.borrow_mut().add_entry(|node| {
918                    log_gesture_end(
919                        node,
920                        type_name,
921                        current_gesture,
922                        reason,
923                        num_events,
924                        new_event_timestamp - gesture_start_timestamp,
925                    );
926                });
927                if has_finger_contact {
928                    (MutableState::Chain, vec![generated_event])
929                } else {
930                    (MutableState::Idle, vec![generated_event])
931                }
932            }
933            ProcessNewEventResult::EndGesture(
934                EndGestureEvent::UnconsumedEvent(unconsumed_event),
935                reason,
936            ) => {
937                log::debug!("touchpad: {} ended: {:?}", type_name, reason);
938                self.inspect_log.borrow_mut().add_entry(|node| {
939                    log_gesture_end(
940                        node,
941                        type_name,
942                        current_gesture,
943                        reason,
944                        num_events,
945                        new_event_timestamp - gesture_start_timestamp,
946                    );
947                });
948                if unconsumed_event.contacts.len() > 0 {
949                    self.handle_event_while_chain(unconsumed_event)
950                } else {
951                    self.handle_event_while_idle(unconsumed_event)
952                }
953            }
954            ProcessNewEventResult::EndGesture(EndGestureEvent::NoEvent, reason) => {
955                log::debug!("touchpad: {} ended: {:?}", type_name, reason);
956                self.inspect_log.borrow_mut().add_entry(|node| {
957                    log_gesture_end(
958                        node,
959                        type_name,
960                        current_gesture,
961                        reason,
962                        num_events,
963                        new_event_timestamp - gesture_start_timestamp,
964                    );
965                });
966                if has_finger_contact {
967                    (MutableState::Chain, vec![])
968                } else {
969                    (MutableState::Idle, vec![])
970                }
971            }
972        }
973    }
974
975    #[allow(dead_code)] // only called in developer debug builds and tests.
976    pub(super) fn mutable_state_to_str(&self) -> String {
977        match &*self.mutable_state.borrow() {
978            MutableState::Idle => format!("touchpad: Idle"),
979            MutableState::Chain => format!("touchpad: Chain"),
980            MutableState::Matching {
981                contenders,
982                matched_contenders,
983                first_event_timestamp,
984                buffered_events,
985            } => {
986                format!(
987                    "touchpad: Matching {{ \
988                                contenders: [ {} ], \
989                                matched_contenders: [ {} ], \
990                                first_event_timestamp_nanos: {}, \
991                                n_buffered_events: {} \
992                              }}",
993                    contenders.iter().fold(String::new(), |accum, item| {
994                        accum + &format!("{}, ", item.get_type_name())
995                    }),
996                    matched_contenders.iter().fold(String::new(), |accum, item| {
997                        accum + &format!("{}, ", item.get_type_name())
998                    }),
999                    first_event_timestamp.into_nanos(),
1000                    buffered_events.len()
1001                )
1002            }
1003            MutableState::Forwarding {
1004                winner,
1005                current_gesture,
1006                gesture_start_timestamp,
1007                num_events,
1008            } => {
1009                format!(
1010                    "touchpad: Forwarding {{ \
1011                        winner: {}, \
1012                        current_gesture: {:?}, \
1013                        gesture_start_timestamp_nanos: {}, \
1014                        num_events: {} \
1015                    }}",
1016                    winner.get_type_name(),
1017                    current_gesture,
1018                    gesture_start_timestamp.into_nanos(),
1019                    num_events
1020                )
1021            }
1022            MutableState::Invalid => format!("touchpad: Invalid"),
1023        }
1024    }
1025
1026    #[allow(dead_code)] // only called in developer debug builds
1027    fn log_mutable_state(&self) {
1028        log::info!("{}", self.mutable_state_to_str());
1029    }
1030}
1031
1032impl Handler for GestureArena {
1033    fn set_handler_healthy(self: std::rc::Rc<Self>) {
1034        self.inspect_status.health_node.borrow_mut().set_ok();
1035    }
1036
1037    fn set_handler_unhealthy(self: std::rc::Rc<Self>, msg: &str) {
1038        self.inspect_status.health_node.borrow_mut().set_unhealthy(msg);
1039    }
1040
1041    fn get_name(&self) -> &'static str {
1042        "GestureArena"
1043    }
1044
1045    fn interest(&self) -> Vec<input_device::InputEventType> {
1046        vec![input_device::InputEventType::Touchpad, input_device::InputEventType::Keyboard]
1047    }
1048}
1049
1050#[async_trait(?Send)]
1051impl InputHandler for GestureArena {
1052    /// Handle `input_event`:
1053    /// * For an unhandled touchpad event, try to interpret as a touchpad gesture.
1054    /// * For a keyboard event, log the timestamp, and pass the event onwards.
1055    /// * For any other event, pass onwards without logging.
1056    async fn handle_input_event(
1057        self: std::rc::Rc<Self>,
1058        input_event: input_device::InputEvent,
1059    ) -> Vec<input_device::InputEvent> {
1060        fuchsia_trace::duration!("input", "gesture_arena");
1061        match input_event {
1062            input_device::InputEvent {
1063                device_event: input_device::InputDeviceEvent::Touchpad(ref touchpad_event),
1064                ref event_time,
1065                device_descriptor:
1066                    input_device::InputDeviceDescriptor::Touchpad(ref touchpad_descriptor),
1067                handled: input_device::Handled::No,
1068                ..
1069            } => {
1070                fuchsia_trace::duration!("input", "gesture_arena[processing]");
1071                self.inspect_status.count_received_event(&event_time);
1072                match self.handle_touchpad_event(event_time, touchpad_event, touchpad_descriptor) {
1073                    Ok(r) => r,
1074                    Err(e) => {
1075                        log::error!("{}", e);
1076                        vec![input_event]
1077                    }
1078                }
1079            }
1080            input_device::InputEvent {
1081                device_event: input_device::InputDeviceEvent::Keyboard(_),
1082                event_time,
1083                ..
1084            } => {
1085                fuchsia_trace::duration!("input", "gesture_arena[processing]");
1086                self.inspect_log.borrow_mut().add_entry(|node| {
1087                    log_keyboard_event_timestamp(node, event_time);
1088                });
1089                vec![input_event]
1090            }
1091            _ => {
1092                self.metrics_logger.log_error(
1093                    InputPipelineErrorMetricDimensionEvent::HandlerReceivedUninterestedEvent,
1094                    std::format!(
1095                        "{} uninterested input event: {:?}",
1096                        self.get_name(),
1097                        input_event.get_event_type()
1098                    ),
1099                );
1100                vec![input_event]
1101            }
1102        }
1103    }
1104}
1105
1106/// Returns the multiplier to translate position data for the device described by
1107/// `device_descriptor`, from the units in the corresponding `TouchpadEvent`, to
1108/// millimeters.
1109///
1110/// For example, if this function returns 1000, then the original data are in
1111/// micrometers, and dividing by 1000 will yield millimeters.
1112fn get_position_divisor_to_mm(
1113    touchpad_descriptor: &touch_binding::TouchpadDeviceDescriptor,
1114) -> Result<f32, Error> {
1115    const EXPONENT_MILLIS: i32 = -3;
1116    let divisors =
1117        touchpad_descriptor.contacts.iter().enumerate().map(|(i, contact_descriptor)| {
1118            match (contact_descriptor.x_unit, contact_descriptor.y_unit) {
1119                (
1120                    fidl_input_report::Unit {
1121                        type_: fidl_input_report::UnitType::Meters,
1122                        exponent: exponent_x,
1123                    },
1124                    fidl_input_report::Unit {
1125                        type_: fidl_input_report::UnitType::Meters,
1126                        exponent: exponent_y,
1127                    },
1128                ) => {
1129                    if exponent_x == exponent_y {
1130                        Ok(f32::powi(10.0, EXPONENT_MILLIS - exponent_x))
1131                    } else {
1132                        Err(format!(
1133                            "contact {}: mismatched exponents x={}, y={}",
1134                            i, exponent_x, exponent_y
1135                        ))
1136                    }
1137                }
1138                (
1139                    fidl_input_report::Unit { type_: x_unit_type, .. },
1140                    fidl_input_report::Unit { type_: fidl_input_report::UnitType::Meters, .. },
1141                ) => Err(format!(
1142                    "contact {}: expected x-unit-type of Meters but got {:?}",
1143                    i, x_unit_type
1144                )),
1145                (
1146                    fidl_input_report::Unit { type_: fidl_input_report::UnitType::Meters, .. },
1147                    fidl_input_report::Unit { type_: y_unit_type, .. },
1148                ) => Err(format!(
1149                    "contact {}: expected y-unit-type of Meters but got {:?}",
1150                    i, y_unit_type
1151                )),
1152                (
1153                    fidl_input_report::Unit { type_: x_unit_type, .. },
1154                    fidl_input_report::Unit { type_: y_unit_type, .. },
1155                ) => Err(format!(
1156                    "contact {}: expected x and y unit-types of Meters but got x={:?} and y={:?}",
1157                    i, x_unit_type, y_unit_type
1158                )),
1159            }
1160        });
1161
1162    let (divisors, errors): (Vec<_>, Vec<_>) =
1163        divisors.fold((vec![], vec![]), |(mut divisors, mut errors), divisor| {
1164            match divisor {
1165                Ok(d) => divisors.push(d),
1166                Err(e) => errors.push(e),
1167            };
1168            (divisors, errors)
1169        });
1170
1171    if !errors.is_empty() {
1172        return Err(format_err!(
1173            errors.into_iter().fold(String::new(), |prev_err_msgs, this_err_msg| prev_err_msgs
1174                + &this_err_msg
1175                + ", ")
1176        ));
1177    }
1178
1179    let first_divisor = match divisors.first() {
1180        Some(&divisor) => divisor,
1181        None => return Err(format_err!("no contact descriptors!")),
1182    };
1183
1184    if divisors.iter().any(|&divisor| divisor != first_divisor) {
1185        return Err(format_err!(
1186            divisors
1187                .into_iter()
1188                .enumerate()
1189                .filter(|(_i, divisor)| *divisor != first_divisor)
1190                .map(|(i, divisor)| format!(
1191                    "contact {} has a different divisor than the first contact ({:?} != {:?})",
1192                    i, divisor, first_divisor,
1193                ))
1194                .fold(String::new(), |prev_err_msgs, this_err_msg| prev_err_msgs
1195                    + &this_err_msg
1196                    + ", ")
1197        ));
1198    }
1199
1200    Ok(first_divisor)
1201}
1202
1203fn log_keyboard_event_timestamp(
1204    log_entry_node: &InspectNode,
1205    driver_timestamp: zx::MonotonicInstant,
1206) {
1207    log_entry_node.record_child("key_event", |key_event_node| {
1208        log_common(key_event_node, driver_timestamp);
1209    });
1210}
1211
1212fn log_basic_reason(reason_node: &InspectNode, text: &'static str) {
1213    reason_node.record_string("reason", text);
1214}
1215
1216fn log_detailed_reason_uint(reason_node: &InspectNode, reason_details: DetailedReasonUint) {
1217    reason_node.record_string("criterion", reason_details.criterion);
1218    reason_node.record_uint("actual", u64::try_from(reason_details.actual).unwrap_or(u64::MAX));
1219    reason_details.min.map(|min| reason_node.record_uint("min_allowed", min));
1220    reason_details.max.map(|max| reason_node.record_uint("max_allowed", max));
1221}
1222
1223fn log_detailed_reason_float(reason_node: &InspectNode, reason_details: DetailedReasonFloat) {
1224    reason_node.record_string("criterion", reason_details.criterion);
1225    reason_node.record_double("actual", f64::from(reason_details.actual));
1226    reason_details.min.map(|min| reason_node.record_double("min_allowed", f64::from(min)));
1227    reason_details.max.map(|max| reason_node.record_double("max_allowed", f64::from(max)));
1228}
1229
1230fn log_detailed_reason_int(reason_node: &InspectNode, reason_details: DetailedReasonInt) {
1231    reason_node.record_string("criterion", reason_details.criterion);
1232    reason_node.record_int("actual", reason_details.actual);
1233    reason_details.min.map(|min| reason_node.record_int("min_allowed", min));
1234    reason_details.max.map(|max| reason_node.record_int("max_allowed", max));
1235}
1236
1237fn log_reason(reason_node: &InspectNode, contender_name: &'static str, reason: Reason) {
1238    // E.g. "input_pipeline_lib_test::gestures::gesture_arena::tests::utils::StubContender"
1239    // -> "utils::StubContender".
1240    let contender_name = match contender_name.rmatch_indices("::").nth(1) {
1241        Some((i, _matched_substr)) => &contender_name[i + 2..],
1242        None => contender_name,
1243    };
1244    reason_node.record_string("contender", contender_name);
1245    match reason {
1246        Reason::Basic(text) => log_basic_reason(reason_node, text),
1247        Reason::DetailedUint(mismatch_details) => {
1248            log_detailed_reason_uint(reason_node, mismatch_details)
1249        }
1250        Reason::DetailedFloat(mismatch_details) => {
1251            log_detailed_reason_float(reason_node, mismatch_details)
1252        }
1253        Reason::DetailedInt(mismatch_details) => {
1254            log_detailed_reason_int(reason_node, mismatch_details)
1255        }
1256    }
1257}
1258
1259fn log_mismatch_reason(log_entry_node: &InspectNode, contender_name: &'static str, reason: Reason) {
1260    log::debug!("touchpad: {} mismatched: {:?}", contender_name, reason);
1261    log_entry_node.record_child("mismatch_event", |mismatch_event_node| {
1262        log_reason(mismatch_event_node, contender_name, reason);
1263    });
1264}
1265
1266fn log_gesture_start(
1267    log_entry_node: &InspectNode,
1268    recognized_gesture: RecognizedGesture,
1269    num_previous_events: usize,
1270    elapsed_from_first_event: zx::MonotonicDuration,
1271) {
1272    log::debug!("touchpad: recognized start {:?}", recognized_gesture);
1273    log_entry_node.record_child("gesture_start", |gesture_start_node| {
1274        gesture_start_node.record_string("gesture_name", recognized_gesture.to_str());
1275        gesture_start_node.record_int(
1276            "latency_micros",
1277            // Reduce precision, to minimize space.
1278            elapsed_from_first_event.into_micros(),
1279        );
1280        gesture_start_node.record_uint(
1281            "latency_event_count",
1282            u64::try_from(num_previous_events).unwrap_or(u64::MAX),
1283        );
1284    });
1285}
1286
1287fn log_gesture_end(
1288    log_entry_node: &InspectNode,
1289    contender_name: &'static str,
1290    current_gesture: RecognizedGesture,
1291    reason: Reason,
1292    num_events: usize,
1293    elapsed_from_gesture_start: zx::MonotonicDuration,
1294) {
1295    log::debug!("touchpad: recognized end {:?}", current_gesture);
1296    log_entry_node.record_child("gesture_end", |gesture_end_node| {
1297        gesture_end_node.record_string("gesture_name", current_gesture.to_str());
1298        gesture_end_node.record_int(
1299            "duration_micros",
1300            // Reduce precision, to minimize space.
1301            elapsed_from_gesture_start.into_micros(),
1302        );
1303        gesture_end_node.record_uint("event_count", u64::try_from(num_events).unwrap_or(u64::MAX));
1304        log_reason(gesture_end_node, contender_name, reason)
1305    });
1306}
1307
1308#[cfg(test)]
1309mod tests {
1310    mod utils {
1311        use super::super::{
1312            COUNTS_PER_MM, Contender, ContenderFactory, ExamineEventResult, MatchedContender,
1313            PRIMARY_BUTTON, ProcessBufferedEventsResult, ProcessNewEventResult, TouchpadEvent,
1314            VerifyEventResult, Winner, args,
1315        };
1316        use crate::utils::Size;
1317        use crate::{Position, input_device, keyboard_binding, mouse_binding, touch_binding};
1318        use assert_matches::assert_matches;
1319        use fidl_fuchsia_input_report as fidl_input_report;
1320        use maplit::hashset;
1321        use std::cell::{Cell, RefCell};
1322        use std::rc::Rc;
1323
1324        /// The gesture arena is mostly agnostic to the event details. Consequently, most
1325        /// tests can use the same lightly populated touchpad event.
1326        pub(super) fn make_unhandled_touchpad_event() -> input_device::InputEvent {
1327            input_device::InputEvent {
1328                device_event: input_device::InputDeviceEvent::Touchpad(
1329                    touch_binding::TouchpadEvent {
1330                        injector_contacts: vec![],
1331                        pressed_buttons: hashset! {},
1332                    },
1333                ),
1334                device_descriptor: make_touchpad_descriptor(),
1335                event_time: zx::MonotonicInstant::ZERO,
1336                trace_id: None,
1337                handled: input_device::Handled::No,
1338            }
1339        }
1340
1341        /// The gesture arena is mostly agnostic to the event details. Consequently, most
1342        /// tests can use the same lightly populated mouse event.
1343        pub(super) fn make_unhandled_mouse_event() -> input_device::InputEvent {
1344            input_device::InputEvent {
1345                device_event: input_device::InputDeviceEvent::Mouse(mouse_binding::MouseEvent {
1346                    location: mouse_binding::MouseLocation::Relative(
1347                        mouse_binding::RelativeLocation { millimeters: Position::zero() },
1348                    ),
1349                    wheel_delta_h: None,
1350                    wheel_delta_v: None,
1351                    phase: mouse_binding::MousePhase::Move,
1352                    affected_buttons: hashset! {},
1353                    pressed_buttons: hashset! {},
1354                    is_precision_scroll: None,
1355                    wake_lease: None.into(),
1356                }),
1357                device_descriptor: make_mouse_descriptor(),
1358                event_time: zx::MonotonicInstant::ZERO,
1359                trace_id: None,
1360                handled: input_device::Handled::No,
1361            }
1362        }
1363
1364        /// The gesture arena is mostly agnostic to the event details. Consequently, most
1365        /// tests can use the same lightly populated keyboard event.
1366        pub(super) fn make_unhandled_keyboard_event() -> input_device::InputEvent {
1367            input_device::InputEvent {
1368                device_event: input_device::InputDeviceEvent::Keyboard(
1369                    keyboard_binding::KeyboardEvent::new(
1370                        fidl_fuchsia_input::Key::A,
1371                        fidl_fuchsia_ui_input3::KeyEventType::Pressed,
1372                    ),
1373                ),
1374                device_descriptor: make_keyboard_descriptor(),
1375                event_time: zx::MonotonicInstant::ZERO,
1376                trace_id: None,
1377                handled: input_device::Handled::No,
1378            }
1379        }
1380
1381        pub(super) fn make_touchpad_descriptor() -> input_device::InputDeviceDescriptor {
1382            input_device::InputDeviceDescriptor::Touchpad(touch_binding::TouchpadDeviceDescriptor {
1383                device_id: 1,
1384                contacts: vec![touch_binding::ContactDeviceDescriptor {
1385                    x_range: fidl_input_report::Range { min: 0, max: 10_000 },
1386                    y_range: fidl_input_report::Range { min: 0, max: 10_000 },
1387                    x_unit: fidl_input_report::Unit {
1388                        type_: fidl_input_report::UnitType::Meters,
1389                        exponent: -6,
1390                    },
1391                    y_unit: fidl_input_report::Unit {
1392                        type_: fidl_input_report::UnitType::Meters,
1393                        exponent: -6,
1394                    },
1395                    pressure_range: None,
1396                    width_range: Some(fidl_input_report::Range { min: 0, max: 10_000 }),
1397                    height_range: Some(fidl_input_report::Range { min: 0, max: 10_000 }),
1398                }],
1399            })
1400        }
1401
1402        pub(super) fn make_mouse_descriptor() -> input_device::InputDeviceDescriptor {
1403            input_device::InputDeviceDescriptor::Mouse(mouse_binding::MouseDeviceDescriptor {
1404                device_id: 2,
1405                absolute_x_range: Some(fidl_input_report::Range { min: -127, max: 127 }),
1406                absolute_y_range: Some(fidl_input_report::Range { min: -127, max: 127 }),
1407                wheel_v_range: None,
1408                wheel_h_range: None,
1409                buttons: Some(vec![PRIMARY_BUTTON]),
1410                counts_per_mm: COUNTS_PER_MM,
1411            })
1412        }
1413
1414        fn make_keyboard_descriptor() -> input_device::InputDeviceDescriptor {
1415            input_device::InputDeviceDescriptor::Keyboard(
1416                keyboard_binding::KeyboardDeviceDescriptor {
1417                    device_id: 3,
1418                    device_information: fidl_fuchsia_input_report::DeviceInformation {
1419                        vendor_id: Some(0),
1420                        product_id: Some(0),
1421                        version: Some(0),
1422                        polling_rate: Some(0),
1423                        ..Default::default()
1424                    },
1425                    keys: vec![fidl_fuchsia_input::Key::A],
1426                },
1427            )
1428        }
1429
1430        #[derive(Clone, Debug)]
1431        /// Provides the ability to
1432        ///
1433        /// 1. Plumb a fake `Contender` into a `GestureArena`
1434        /// 2. Fake interactions between the two
1435        /// 3. Inspect interactions between the two
1436        ///
1437        /// To plumb the fake, pass a `Clone` of the `StubContender` to
1438        /// `ContenderFactoryOnceOrPanic`. To fake or inspect interactions, call the
1439        /// inherent methods on the struct.
1440        pub(super) struct StubContender {
1441            inner: Rc<RefCell<StubContenderInner>>,
1442            start_from_idle: bool,
1443        }
1444
1445        impl StubContender {
1446            pub(super) fn new() -> Self {
1447                Self {
1448                    inner: Rc::new(RefCell::new(StubContenderInner {
1449                        next_result: None,
1450                        calls_received: 0,
1451                        last_touchpad_event: None,
1452                    })),
1453                    start_from_idle: false,
1454                }
1455            }
1456
1457            pub(super) fn new_start_from_idle() -> Self {
1458                Self {
1459                    inner: Rc::new(RefCell::new(StubContenderInner {
1460                        next_result: None,
1461                        calls_received: 0,
1462                        last_touchpad_event: None,
1463                    })),
1464                    start_from_idle: true,
1465                }
1466            }
1467
1468            /// Set the value to be returned on the next call to `examine_event()`.
1469            /// Aborts if a value is already set, since that suggests that a previously
1470            /// expected call was never made.
1471            pub(super) fn set_next_result(&self, next_result: ExamineEventResult) {
1472                self.assert_next_result_is_none();
1473                self.inner.borrow_mut().next_result = Some(next_result);
1474            }
1475
1476            pub(super) fn assert_next_result_is_none(&self) {
1477                assert_matches!(self.inner.borrow().next_result, None);
1478            }
1479
1480            pub(super) fn calls_received(&self) -> usize {
1481                self.inner.borrow().calls_received
1482            }
1483
1484            pub(super) fn ref_count(&self) -> usize {
1485                Rc::strong_count(&self.inner)
1486            }
1487
1488            pub(super) fn get_last_touchpad_event(&self) -> Option<TouchpadEvent> {
1489                self.inner.borrow_mut().last_touchpad_event.take()
1490            }
1491        }
1492
1493        #[derive(Debug)]
1494        struct StubContenderInner {
1495            next_result: Option<ExamineEventResult>,
1496            calls_received: usize,
1497            last_touchpad_event: Option<TouchpadEvent>,
1498        }
1499
1500        /// A factory that returns `Vec<Box<dyn Contender>>` from `contenders` on the
1501        /// first call, and `panic()`-s on the second call.
1502        ///
1503        /// Useful because
1504        /// a) `GestureArena` requires that the factory be invocable multiple times, BUT
1505        /// b) most of the gesture arena tests don't expect that to happen, SO
1506        /// c) the tests don't have logic to handle that case.
1507        ///
1508        /// To use: pass a lambda which invokes `make_contenders()` to
1509        /// `GestureArena::new_for_test()`.
1510        pub(super) struct ContenderFactoryOnceOrPanic {
1511            contenders: Cell<Option<Vec<Box<dyn Contender>>>>,
1512        }
1513
1514        impl ContenderFactoryOnceOrPanic {
1515            pub(super) fn new(contenders: Vec<Box<dyn Contender>>) -> Self {
1516                Self { contenders: Cell::new(Some(contenders)) }
1517            }
1518
1519            /// `ContenderFactoryOnceOrPanic::new_panic_when_call` will panic when calls
1520            /// `make_contenders`.
1521            pub(super) fn new_panic_when_call() -> Self {
1522                Self { contenders: Cell::new(None) }
1523            }
1524        }
1525
1526        impl ContenderFactory for ContenderFactoryOnceOrPanic {
1527            fn make_contenders(&self) -> Vec<Box<dyn Contender>> {
1528                self.contenders
1529                    .take()
1530                    .expect("`contenders` has been consumed")
1531                    .into_iter()
1532                    .collect()
1533            }
1534        }
1535
1536        impl Contender for StubContender {
1537            fn examine_event(self: Box<Self>, event: &TouchpadEvent) -> ExamineEventResult {
1538                let mut inner = self.inner.borrow_mut();
1539                inner.calls_received += 1;
1540                inner.last_touchpad_event = Some(event.clone());
1541                inner.next_result.take().unwrap_or_else(|| {
1542                    panic!("missing `next_result` on call {}", inner.calls_received)
1543                })
1544            }
1545
1546            fn start_from_idle(&self) -> bool {
1547                self.start_from_idle
1548            }
1549        }
1550
1551        #[derive(Clone)]
1552        pub(super) struct ContenderFactoryCalled {
1553            pub called: Rc<RefCell<bool>>,
1554        }
1555
1556        impl ContenderFactoryCalled {
1557            pub(super) fn new() -> Self {
1558                Self { called: Rc::new(RefCell::new(false)) }
1559            }
1560
1561            pub(super) fn was_called(&self) -> bool {
1562                *self.called.borrow()
1563            }
1564        }
1565
1566        impl ContenderFactory for ContenderFactoryCalled {
1567            fn make_contenders(&self) -> Vec<Box<dyn Contender>> {
1568                self.called.replace(true);
1569                vec![]
1570            }
1571        }
1572
1573        // A fake contender that always returns itself. Useful for keeping the
1574        // gesture arena from exiting the matching state. Keeping the arena in
1575        // the matching state eliminates the need for some tests to provide
1576        // return values for `process_buffered_events()`.
1577        #[derive(Debug)]
1578        pub(super) struct ContenderForever {}
1579
1580        impl Contender for ContenderForever {
1581            fn examine_event(self: Box<Self>, _event: &TouchpadEvent) -> ExamineEventResult {
1582                ExamineEventResult::Contender(self)
1583            }
1584        }
1585
1586        #[derive(Clone, Debug)]
1587        pub(super) struct StubMatchedContender {
1588            inner: Rc<RefCell<StubMatchedContenderInner>>,
1589        }
1590
1591        impl StubMatchedContender {
1592            pub(super) fn new() -> Self {
1593                Self {
1594                    inner: Rc::new(RefCell::new(StubMatchedContenderInner {
1595                        next_verify_event_result: None,
1596                        next_process_buffered_events_result: None,
1597                        verify_event_calls_received: 0,
1598                        process_buffered_events_calls_received: 0,
1599                        last_process_buffered_events_args: None,
1600                    })),
1601                }
1602            }
1603
1604            /// Set the value to be returned on the next call to `verify_event()`.
1605            /// Aborts if a value is already set, since that suggests that a previously
1606            /// expected call was never made.
1607            pub(super) fn set_next_verify_event_result(&self, next_result: VerifyEventResult) {
1608                self.assert_next_verify_event_result_is_none();
1609                self.inner.borrow_mut().next_verify_event_result = Some(next_result);
1610            }
1611
1612            fn assert_next_verify_event_result_is_none(&self) {
1613                assert_matches!(self.inner.borrow().next_verify_event_result, None);
1614            }
1615
1616            pub(super) fn verify_event_calls_received(&self) -> usize {
1617                self.inner.borrow().verify_event_calls_received
1618            }
1619
1620            /// Set the value to be returned on the next call to `process_buffered_events()`.
1621            /// Aborts if a value is already set, since that suggests that a previously
1622            /// expected call was never made.
1623            pub(super) fn set_next_process_buffered_events_result(
1624                &self,
1625                next_result: ProcessBufferedEventsResult,
1626            ) {
1627                self.assert_next_process_buffered_events_result_is_none();
1628                self.inner.borrow_mut().next_process_buffered_events_result = Some(next_result);
1629            }
1630
1631            pub(super) fn get_last_processed_buffered_events_args(
1632                &self,
1633            ) -> Option<Vec<TouchpadEvent>> {
1634                self.inner.borrow_mut().last_process_buffered_events_args.take()
1635            }
1636
1637            fn assert_next_process_buffered_events_result_is_none(&self) {
1638                assert_matches!(self.inner.borrow().next_process_buffered_events_result, None);
1639            }
1640
1641            pub(super) fn ref_count(&self) -> usize {
1642                Rc::strong_count(&self.inner)
1643            }
1644        }
1645
1646        #[derive(Debug)]
1647        struct StubMatchedContenderInner {
1648            next_verify_event_result: Option<VerifyEventResult>,
1649            next_process_buffered_events_result: Option<ProcessBufferedEventsResult>,
1650            verify_event_calls_received: usize,
1651            process_buffered_events_calls_received: usize,
1652            last_process_buffered_events_args: Option<Vec<TouchpadEvent>>,
1653        }
1654
1655        impl MatchedContender for StubMatchedContender {
1656            fn verify_event(self: Box<Self>, _event: &TouchpadEvent) -> VerifyEventResult {
1657                let mut inner = self.inner.borrow_mut();
1658                inner.verify_event_calls_received += 1;
1659                inner.next_verify_event_result.take().unwrap_or_else(|| {
1660                    panic!(
1661                        "missing `next_verify_event_result` on call {}",
1662                        inner.verify_event_calls_received
1663                    )
1664                })
1665            }
1666
1667            fn process_buffered_events(
1668                self: Box<Self>,
1669                events: Vec<TouchpadEvent>,
1670            ) -> ProcessBufferedEventsResult {
1671                let mut inner = self.inner.borrow_mut();
1672                inner.last_process_buffered_events_args = Some(events);
1673                inner.process_buffered_events_calls_received += 1;
1674                inner.next_process_buffered_events_result.take().unwrap_or_else(|| {
1675                    panic!(
1676                        "missing `next_process_buffered_events_result` on call {}",
1677                        inner.process_buffered_events_calls_received
1678                    )
1679                })
1680            }
1681        }
1682
1683        #[derive(Clone, Debug)]
1684        pub(super) struct StubWinner {
1685            inner: Rc<RefCell<StubWinnerInner>>,
1686        }
1687
1688        impl StubWinner {
1689            pub(super) fn new() -> Self {
1690                Self {
1691                    inner: Rc::new(RefCell::new(StubWinnerInner {
1692                        next_result: None,
1693                        calls_received: 0,
1694                    })),
1695                }
1696            }
1697
1698            /// Set the value to be returned on the next call to `examine_event()`.
1699            pub(super) fn set_next_result(&self, next_result: ProcessNewEventResult) {
1700                self.inner.borrow_mut().next_result = Some(next_result);
1701            }
1702
1703            pub(super) fn calls_received(&self) -> usize {
1704                self.inner.borrow().calls_received
1705            }
1706        }
1707
1708        #[derive(Debug)]
1709        struct StubWinnerInner {
1710            next_result: Option<ProcessNewEventResult>,
1711            calls_received: usize,
1712        }
1713
1714        impl Winner for StubWinner {
1715            fn process_new_event(self: Box<Self>, _event: TouchpadEvent) -> ProcessNewEventResult {
1716                let mut inner = self.inner.borrow_mut();
1717                inner.calls_received += 1;
1718                inner.next_result.take().unwrap_or_else(|| {
1719                    panic!("missing `next_result` on call {}", inner.calls_received)
1720                })
1721            }
1722        }
1723
1724        impl From<StubContender> for Box<dyn Contender> {
1725            fn from(stub_contender: StubContender) -> Box<dyn Contender> {
1726                Box::new(stub_contender)
1727            }
1728        }
1729
1730        impl From<ContenderForever> for Box<dyn Contender> {
1731            fn from(contender_forever: ContenderForever) -> Box<dyn Contender> {
1732                Box::new(contender_forever)
1733            }
1734        }
1735
1736        impl From<StubMatchedContender> for Box<dyn MatchedContender> {
1737            fn from(stub_matched_contender: StubMatchedContender) -> Box<dyn MatchedContender> {
1738                Box::new(stub_matched_contender)
1739            }
1740        }
1741
1742        impl From<StubWinner> for Box<dyn Winner> {
1743            fn from(stub_winner: StubWinner) -> Box<dyn Winner> {
1744                Box::new(stub_winner)
1745            }
1746        }
1747
1748        pub(super) const TOUCH_CONTACT_INDEX_FINGER: touch_binding::TouchContact =
1749            touch_binding::TouchContact {
1750                id: 0,
1751                position: Position { x: 0.0, y: 0.0 },
1752                pressure: None,
1753                contact_size: Some(Size {
1754                    width: args::MIN_PALM_SIZE_MM / 2.0,
1755                    height: args::MIN_PALM_SIZE_MM / 2.0,
1756                }),
1757            };
1758    }
1759
1760    mod idle_chain_states {
1761        use super::super::{
1762            ExamineEventResult, GestureArena, InputHandler, MutableState,
1763            ProcessBufferedEventsResult, Reason, RecognizedGesture, TouchpadEvent, args,
1764        };
1765        use super::utils::{
1766            ContenderFactoryCalled, ContenderFactoryOnceOrPanic, StubContender,
1767            StubMatchedContender, StubWinner, TOUCH_CONTACT_INDEX_FINGER, make_touchpad_descriptor,
1768            make_unhandled_keyboard_event, make_unhandled_mouse_event,
1769            make_unhandled_touchpad_event,
1770        };
1771        use crate::input_handler::InputHandlerStatus;
1772        use crate::utils::Size;
1773        use crate::{Position, input_device, metrics, touch_binding};
1774        use assert_matches::assert_matches;
1775
1776        use maplit::hashset;
1777        use std::cell::RefCell;
1778        use std::rc::Rc;
1779        use test_case::test_case;
1780
1781        fn make_gesture_arena_with_state(
1782            contender_factory: ContenderFactoryOnceOrPanic,
1783            state: MutableState,
1784        ) -> Rc<GestureArena> {
1785            Rc::new(GestureArena {
1786                contender_factory: Box::new(contender_factory),
1787                mutable_state: RefCell::new(state),
1788                inspect_log: RefCell::new(fuchsia_inspect_contrib::nodes::BoundedListNode::new(
1789                    fuchsia_inspect::Inspector::default().root().create_child("some_key"),
1790                    1,
1791                )),
1792                metrics_logger: metrics::MetricsLogger::default(),
1793                inspect_status: InputHandlerStatus::default(),
1794            })
1795        }
1796
1797        #[test_case(MutableState::Idle; "idle")]
1798        #[test_case(MutableState::Chain; "chain")]
1799        #[fuchsia::test(allow_stalls = false)]
1800        async fn invokes_contender_factory_on_touchpad_event(state: MutableState) {
1801            let contender_factory = Box::new(ContenderFactoryCalled::new());
1802            let arena = Rc::new(GestureArena {
1803                contender_factory: contender_factory.clone(),
1804                mutable_state: RefCell::new(state),
1805                inspect_log: RefCell::new(fuchsia_inspect_contrib::nodes::BoundedListNode::new(
1806                    fuchsia_inspect::Inspector::default().root().create_child("some_key"),
1807                    1,
1808                )),
1809                metrics_logger: metrics::MetricsLogger::default(),
1810                inspect_status: InputHandlerStatus::default(),
1811            });
1812            arena.handle_input_event(make_unhandled_touchpad_event()).await;
1813            assert!(contender_factory.was_called());
1814        }
1815
1816        #[test_case(MutableState::Idle; "idle")]
1817        #[test_case(MutableState::Chain; "chain")]
1818        #[fuchsia::test(allow_stalls = false)]
1819        async fn does_not_invoke_contender_factory_on_mouse_event(state: MutableState) {
1820            let contender_factory = Box::new(ContenderFactoryCalled::new());
1821            let arena = Rc::new(GestureArena {
1822                contender_factory: contender_factory.clone(),
1823                mutable_state: RefCell::new(state),
1824                inspect_log: RefCell::new(fuchsia_inspect_contrib::nodes::BoundedListNode::new(
1825                    fuchsia_inspect::Inspector::default().root().create_child("some_key"),
1826                    1,
1827                )),
1828                metrics_logger: metrics::MetricsLogger::default(),
1829                inspect_status: InputHandlerStatus::default(),
1830            });
1831            arena.handle_input_event(make_unhandled_mouse_event()).await;
1832            assert!(!contender_factory.was_called());
1833        }
1834
1835        #[test_case(MutableState::Idle; "idle")]
1836        #[test_case(MutableState::Chain; "chain")]
1837        #[fuchsia::test(allow_stalls = false)]
1838        async fn does_not_invoke_contender_factory_on_keyboard_event(state: MutableState) {
1839            let contender_factory = Box::new(ContenderFactoryCalled::new());
1840
1841            let arena = Rc::new(GestureArena {
1842                contender_factory: contender_factory.clone(),
1843                mutable_state: RefCell::new(state),
1844                inspect_log: RefCell::new(fuchsia_inspect_contrib::nodes::BoundedListNode::new(
1845                    fuchsia_inspect::Inspector::default().root().create_child("some_key"),
1846                    1,
1847                )),
1848                metrics_logger: metrics::MetricsLogger::default(),
1849                inspect_status: InputHandlerStatus::default(),
1850            });
1851            arena.handle_input_event(make_unhandled_keyboard_event()).await;
1852            assert!(!contender_factory.was_called());
1853        }
1854
1855        #[test_case(MutableState::Idle; "idle")]
1856        #[test_case(MutableState::Chain; "chain")]
1857        #[fuchsia::test(allow_stalls = false)]
1858        async fn calls_examine_event_on_contender(state: MutableState) {
1859            let contender = Box::new(StubContender::new());
1860            let contender_factory = ContenderFactoryOnceOrPanic::new(vec![contender.clone()]);
1861            let arena = make_gesture_arena_with_state(contender_factory, state);
1862            contender.set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
1863            arena.handle_input_event(make_unhandled_touchpad_event()).await;
1864            pretty_assertions::assert_eq!(contender.calls_received(), 1);
1865        }
1866
1867        #[fuchsia::test(allow_stalls = false)]
1868        async fn calls_examine_event_on_idle_only_contender_while_idle() {
1869            let contender = Box::new(StubContender::new_start_from_idle());
1870            let contender_factory = ContenderFactoryOnceOrPanic::new(vec![contender.clone()]);
1871            let arena = make_gesture_arena_with_state(contender_factory, MutableState::Idle);
1872            contender.set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
1873            arena.handle_input_event(make_unhandled_touchpad_event()).await;
1874            pretty_assertions::assert_eq!(contender.calls_received(), 1);
1875        }
1876
1877        #[fuchsia::test(allow_stalls = false)]
1878        async fn does_not_calls_examine_event_on_idle_only_contender_while_chain() {
1879            let contender = Box::new(StubContender::new_start_from_idle());
1880            let contender_factory = ContenderFactoryOnceOrPanic::new(vec![contender.clone()]);
1881            let arena = make_gesture_arena_with_state(contender_factory, MutableState::Chain);
1882            contender.set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
1883            arena.handle_input_event(make_unhandled_touchpad_event()).await;
1884            pretty_assertions::assert_eq!(contender.calls_received(), 0);
1885        }
1886
1887        #[test_case(MutableState::Idle; "idle")]
1888        #[test_case(MutableState::Chain; "chain")]
1889        #[fuchsia::test(allow_stalls = false)]
1890        async fn calls_examine_event_on_all_contenders_even_if_first_matches(state: MutableState) {
1891            let first_contender = Box::new(StubContender::new());
1892            let second_contender = Box::new(StubContender::new());
1893            let contender_factory = ContenderFactoryOnceOrPanic::new(vec![
1894                first_contender.clone(),
1895                second_contender.clone(),
1896            ]);
1897            let arena = make_gesture_arena_with_state(contender_factory, state);
1898            first_contender.set_next_result(ExamineEventResult::MatchedContender(
1899                StubMatchedContender::new().into(),
1900            ));
1901            // Second contender keeps gesture arena in Matching state.
1902            second_contender
1903                .set_next_result(ExamineEventResult::Contender(StubContender::new().into()));
1904            arena.handle_input_event(make_unhandled_touchpad_event()).await;
1905            pretty_assertions::assert_eq!(first_contender.calls_received(), 1);
1906            pretty_assertions::assert_eq!(second_contender.calls_received(), 1);
1907        }
1908
1909        #[test_case(MutableState::Idle; "idle")]
1910        #[test_case(MutableState::Chain; "chain")]
1911        #[fuchsia::test(allow_stalls = false)]
1912        async fn retains_reference_to_replacement_contender(state: MutableState) {
1913            // Create a gesture arena which will instantiate a `StubContender`.
1914            let initial_contender = Box::new(StubContender::new());
1915            let contender_factory =
1916                ContenderFactoryOnceOrPanic::new(vec![initial_contender.clone()]);
1917            let arena = make_gesture_arena_with_state(contender_factory, state);
1918
1919            // Configure `initial_contender` to return a new `StubContender` when
1920            // `examine_event()` is called.
1921            let replacement_contender = StubContender::new();
1922            initial_contender.set_next_result(ExamineEventResult::Contender(
1923                replacement_contender.clone().into(),
1924            ));
1925
1926            // Process a touchpad event. This should cause `arena` to consume the
1927            // `ExamineEventResult` set above.
1928            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
1929
1930            // Verify that the `ExamineEventResult` was, in fact, consumed.
1931            initial_contender.assert_next_result_is_none();
1932
1933            // Finally, verify that `replacement_contender` has two references.
1934            //
1935            // Note that:
1936            // * One of the references is from `replacement_contender`.
1937            // * The second one cannot be from the `ExamineEventResult` above,
1938            //   because the `ExamineEventResult` was consumed.
1939            //
1940            // Hence: the second reference (if it exists) must be in the gesture arena.
1941            pretty_assertions::assert_eq!(replacement_contender.ref_count(), 2);
1942        }
1943
1944        #[test_case(MutableState::Idle; "idle")]
1945        #[test_case(MutableState::Chain; "chain")]
1946        #[fuchsia::test(allow_stalls = false)]
1947        async fn retains_reference_to_matched_contender(state: MutableState) {
1948            // Create a gesture arena which will instantiate a `StubContender`.
1949            let initial_contender = Box::new(StubContender::new());
1950            // Second contender keeps gesture arena in Matching state.
1951            let second_contender = Box::new(StubContender::new());
1952            let contender_factory = ContenderFactoryOnceOrPanic::new(vec![
1953                initial_contender.clone(),
1954                second_contender.clone(),
1955            ]);
1956            let arena = make_gesture_arena_with_state(contender_factory, state);
1957            // Configure `initial_contender` to return a `StubMatchedContender` when
1958            // `examine_event()` is called.
1959            let replacement_contender = StubMatchedContender::new();
1960            initial_contender.set_next_result(ExamineEventResult::MatchedContender(
1961                replacement_contender.clone().into(),
1962            ));
1963            second_contender
1964                .set_next_result(ExamineEventResult::Contender(StubContender::new().into()));
1965
1966            // Process a touchpad event. This should cause `arena` to consume the
1967            // `ExamineEventResult` set above.
1968            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
1969
1970            // Verify that the `ExamineEventResult` was, in fact, consumed.
1971            initial_contender.assert_next_result_is_none();
1972
1973            // Finally, verify that `replacement_contender` has two references.
1974            //
1975            // Note that:
1976            // * One of the references is from `replacement_contender`.
1977            // * The second one cannot be from the `ExamineEventResult` above,
1978            //   because the `ExamineEventResult` was consumed.
1979            //
1980            // Hence: the second reference (if it exists) must be in the gesture arena.
1981            pretty_assertions::assert_eq!(replacement_contender.ref_count(), 2);
1982        }
1983
1984        #[test_case(MutableState::Idle; "idle")]
1985        #[test_case(MutableState::Chain; "chain")]
1986        #[fuchsia::test(allow_stalls = false)]
1987        async fn retains_touchpad_event_when_entering_matching(state: MutableState) {
1988            // Create a gesture arena which will instantiate a `StubContender`.
1989            let initial_contender = Box::new(StubContender::new());
1990            // Second contender keeps gesture arena in Matching state.
1991            let second_contender = Box::new(StubContender::new());
1992            let contender_factory = ContenderFactoryOnceOrPanic::new(vec![
1993                initial_contender.clone(),
1994                second_contender.clone(),
1995            ]);
1996            let arena = make_gesture_arena_with_state(contender_factory, state);
1997            // Create the event which will be sent to the arena.
1998            let touchpad_event = input_device::InputEvent {
1999                event_time: zx::MonotonicInstant::from_nanos(123456),
2000                device_event: input_device::InputDeviceEvent::Touchpad(
2001                    touch_binding::TouchpadEvent {
2002                        injector_contacts: vec![TOUCH_CONTACT_INDEX_FINGER],
2003                        pressed_buttons: hashset! {0},
2004                    },
2005                ),
2006                device_descriptor: make_touchpad_descriptor(),
2007                trace_id: None,
2008                handled: input_device::Handled::No,
2009            };
2010
2011            // Configure `initial_contender` to return a `StubMatchedContender` when
2012            // `examine_event()` is called.
2013            initial_contender.set_next_result(ExamineEventResult::MatchedContender(
2014                StubMatchedContender::new().into(),
2015            ));
2016            second_contender
2017                .set_next_result(ExamineEventResult::Contender(StubContender::new().into()));
2018
2019            // Process `touchpad_event`. Because `initial_contender` returns
2020            // `ExamineEventResult::MatchedContender`, the gesture arena will enter
2021            // the `Matching` state.
2022            arena.clone().handle_input_event(touchpad_event).await;
2023
2024            // Verify that `arena` retained the details of `touchpad_event`.
2025            assert_matches!(
2026                &*arena.mutable_state.borrow(),
2027                MutableState::Matching {
2028                    contenders: _,
2029                    matched_contenders: _,
2030                    buffered_events,
2031                    first_event_timestamp: _,
2032                } => pretty_assertions::assert_eq!(
2033                    buffered_events.as_slice(),
2034                    [TouchpadEvent {
2035                        timestamp: zx::MonotonicInstant::from_nanos(123456),
2036                        pressed_buttons: vec![0],
2037                        contacts: vec![
2038                            touch_binding::TouchContact {
2039                                contact_size: Some(Size{ width: args::MIN_PALM_SIZE_MM / 2000.0, height: args::MIN_PALM_SIZE_MM / 2000.0 }),
2040                                ..TOUCH_CONTACT_INDEX_FINGER
2041                            },
2042                        ],
2043                        filtered_palm_contacts: vec![],
2044                    }]
2045                )
2046            );
2047        }
2048
2049        #[test_case(MutableState::Idle; "idle")]
2050        #[test_case(MutableState::Chain; "chain")]
2051        #[fuchsia::test(allow_stalls = false)]
2052        async fn generates_no_events_on_mismatch_entering_chain(state: MutableState) {
2053            let contender = Box::new(StubContender::new());
2054            let contender_factory = ContenderFactoryOnceOrPanic::new(vec![contender.clone()]);
2055            let arena = make_gesture_arena_with_state(contender_factory, state);
2056            contender.set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
2057
2058            let touchpad_event = input_device::InputEvent {
2059                event_time: zx::MonotonicInstant::from_nanos(123456),
2060                device_event: input_device::InputDeviceEvent::Touchpad(
2061                    touch_binding::TouchpadEvent {
2062                        injector_contacts: vec![touch_binding::TouchContact {
2063                            id: 1,
2064                            position: Position { x: 0.0, y: 0.0 },
2065                            ..TOUCH_CONTACT_INDEX_FINGER
2066                        }],
2067                        pressed_buttons: hashset! {},
2068                    },
2069                ),
2070                device_descriptor: make_touchpad_descriptor(),
2071                trace_id: None,
2072                handled: input_device::Handled::No,
2073            };
2074
2075            pretty_assertions::assert_eq!(
2076                arena.clone().handle_input_event(touchpad_event).await,
2077                vec![]
2078            );
2079
2080            assert_matches!(*arena.mutable_state.borrow(), MutableState::Chain);
2081        }
2082
2083        #[test_case(MutableState::Idle; "idle")]
2084        #[test_case(MutableState::Chain; "chain")]
2085        #[fuchsia::test(allow_stalls = false)]
2086        async fn generates_no_events_on_mismatch_entering_idle(state: MutableState) {
2087            let contender = Box::new(StubContender::new());
2088            let contender_factory = ContenderFactoryOnceOrPanic::new(vec![contender.clone()]);
2089            let arena = make_gesture_arena_with_state(contender_factory, state);
2090            contender.set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
2091            pretty_assertions::assert_eq!(
2092                arena.clone().handle_input_event(make_unhandled_touchpad_event()).await,
2093                vec![]
2094            );
2095
2096            assert_matches!(*arena.mutable_state.borrow(), MutableState::Idle);
2097        }
2098
2099        #[test_case(MutableState::Idle; "idle")]
2100        #[test_case(MutableState::Chain; "chain")]
2101        #[fuchsia::test(allow_stalls = false)]
2102        async fn generates_no_events_when_entering_matching(state: MutableState) {
2103            let contender = Box::new(StubContender::new());
2104            let contender_factory = ContenderFactoryOnceOrPanic::new(vec![contender.clone()]);
2105            let arena = make_gesture_arena_with_state(contender_factory, state);
2106            contender.set_next_result(ExamineEventResult::Contender(StubContender::new().into()));
2107            pretty_assertions::assert_eq!(
2108                arena.handle_input_event(make_unhandled_touchpad_event()).await,
2109                vec![]
2110            );
2111        }
2112
2113        #[test_case(MutableState::Idle; "idle")]
2114        #[test_case(MutableState::Chain; "chain")]
2115        #[fuchsia::test(allow_stalls = false)]
2116        async fn enters_idle_on_mismatch(state: MutableState) {
2117            let contender = Box::new(StubContender::new());
2118            let contender_factory = ContenderFactoryOnceOrPanic::new(vec![contender.clone()]);
2119            let arena = make_gesture_arena_with_state(contender_factory, state);
2120            contender.set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
2121            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
2122            assert_matches!(*arena.mutable_state.borrow(), MutableState::Idle);
2123        }
2124
2125        #[test_case(MutableState::Idle; "idle")]
2126        #[test_case(MutableState::Chain; "chain")]
2127        #[fuchsia::test(allow_stalls = false)]
2128        async fn enters_matching_on_contender_result(state: MutableState) {
2129            let contender = Box::new(StubContender::new());
2130            let contender_factory = ContenderFactoryOnceOrPanic::new(vec![contender.clone()]);
2131            let arena = make_gesture_arena_with_state(contender_factory, state);
2132            contender.set_next_result(ExamineEventResult::Contender(StubContender::new().into()));
2133            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
2134            assert_matches!(*arena.mutable_state.borrow(), MutableState::Matching { .. });
2135        }
2136
2137        #[test_case(MutableState::Idle; "idle")]
2138        #[test_case(MutableState::Chain; "chain")]
2139        #[fuchsia::test(allow_stalls = false)]
2140        async fn enters_matching_on_matched_contender_result(state: MutableState) {
2141            let first_contender = Box::new(StubContender::new());
2142            // Second contender keeps gesture arena in Matching state.
2143            let second_contender = Box::new(StubContender::new());
2144            let contender_factory = ContenderFactoryOnceOrPanic::new(vec![
2145                first_contender.clone(),
2146                second_contender.clone(),
2147            ]);
2148            let arena = make_gesture_arena_with_state(contender_factory, state);
2149
2150            first_contender.set_next_result(ExamineEventResult::MatchedContender(
2151                StubMatchedContender::new().into(),
2152            ));
2153            second_contender
2154                .set_next_result(ExamineEventResult::Contender(StubContender::new().into()));
2155            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
2156            assert_matches!(*arena.mutable_state.borrow(), MutableState::Matching { .. });
2157        }
2158
2159        #[test_case(MutableState::Idle; "idle")]
2160        #[test_case(MutableState::Chain; "chain")]
2161        #[fuchsia::test(allow_stalls = false)]
2162        async fn enters_forward_on_only_one_matched_contender_result(state: MutableState) {
2163            let contender = Box::new(StubContender::new());
2164            let contender_factory = ContenderFactoryOnceOrPanic::new(vec![contender.clone()]);
2165            let arena = make_gesture_arena_with_state(contender_factory, state);
2166
2167            let matched_contender = StubMatchedContender::new();
2168            matched_contender.set_next_process_buffered_events_result(
2169                ProcessBufferedEventsResult {
2170                    generated_events: vec![],
2171                    winner: Some(Box::new(StubWinner::new())),
2172                    recognized_gesture: RecognizedGesture::Motion,
2173                },
2174            );
2175            contender
2176                .set_next_result(ExamineEventResult::MatchedContender(matched_contender.into()));
2177            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
2178            assert_matches!(*arena.mutable_state.borrow(), MutableState::Forwarding { .. });
2179        }
2180    }
2181
2182    mod matching_state {
2183        use super::super::{
2184            Contender, ContenderFactory, ExamineEventResult, GestureArena, InputHandler,
2185            MouseEvent, MutableState, PRIMARY_BUTTON, ProcessBufferedEventsResult, Reason,
2186            RecognizedGesture, TouchpadEvent, VerifyEventResult,
2187        };
2188        use super::utils::{
2189            ContenderForever, StubContender, StubMatchedContender, StubWinner,
2190            TOUCH_CONTACT_INDEX_FINGER, make_touchpad_descriptor, make_unhandled_keyboard_event,
2191            make_unhandled_mouse_event, make_unhandled_touchpad_event,
2192        };
2193        use crate::input_handler::InputHandlerStatus;
2194        use crate::{Position, input_device, metrics, mouse_binding, touch_binding};
2195        use assert_matches::assert_matches;
2196
2197        use maplit::hashset;
2198        use pretty_assertions::assert_eq;
2199        use std::cell::RefCell;
2200        use std::rc::Rc;
2201        use test_case::test_case;
2202
2203        struct ContenderFactoryWarn {}
2204
2205        impl ContenderFactory for ContenderFactoryWarn {
2206            fn make_contenders(&self) -> Vec<Box<dyn Contender>> {
2207                // Note: printing instead of panic()-ing here yields better
2208                // failure messages from the tests.
2209                eprintln!("factory invoked in matching state");
2210                vec![]
2211            }
2212        }
2213
2214        fn make_matching_arena(
2215            contenders: Vec<StubContender>,
2216            matched_contenders: Vec<StubMatchedContender>,
2217            buffered_events: Vec<TouchpadEvent>,
2218            contender_forever: Option<ContenderForever>,
2219        ) -> Rc<GestureArena> {
2220            Rc::new(GestureArena {
2221                contender_factory: Box::new(ContenderFactoryWarn {}),
2222                mutable_state: RefCell::new(MutableState::Matching {
2223                    contenders: {
2224                        contenders
2225                            .into_iter()
2226                            .map(std::convert::From::<StubContender>::from)
2227                            .chain(
2228                                contender_forever
2229                                    .into_iter()
2230                                    .map(std::convert::From::<ContenderForever>::from),
2231                            )
2232                            .collect()
2233                    },
2234                    matched_contenders: {
2235                        matched_contenders
2236                            .into_iter()
2237                            .map(std::convert::From::<StubMatchedContender>::from)
2238                            .collect()
2239                    },
2240                    first_event_timestamp: zx::MonotonicInstant::ZERO,
2241                    buffered_events,
2242                }),
2243                inspect_log: RefCell::new(fuchsia_inspect_contrib::nodes::BoundedListNode::new(
2244                    fuchsia_inspect::Inspector::default().root().create_child("some_key"),
2245                    1,
2246                )),
2247                metrics_logger: metrics::MetricsLogger::default(),
2248                inspect_status: InputHandlerStatus::default(),
2249            })
2250        }
2251
2252        #[fuchsia::test(allow_stalls = false)]
2253        async fn invokes_examine_and_verify_event_on_touchpad_event() {
2254            let contender = StubContender::new();
2255            let matched_contender = StubMatchedContender::new();
2256            let arena = make_matching_arena(
2257                vec![contender.clone()],
2258                vec![matched_contender.clone()],
2259                vec![],
2260                None,
2261            );
2262            contender.set_next_result(ExamineEventResult::Contender(contender.clone().into()));
2263            matched_contender.set_next_verify_event_result(VerifyEventResult::Mismatch(
2264                Reason::Basic("some reason"),
2265            ));
2266            arena.handle_input_event(make_unhandled_touchpad_event()).await;
2267            assert_eq!(contender.calls_received(), 1);
2268            assert_eq!(matched_contender.verify_event_calls_received(), 1);
2269        }
2270
2271        #[fuchsia::test(allow_stalls = false)]
2272        async fn does_not_invoke_examine_or_verify_event_on_mouse_event() {
2273            let contender = StubContender::new();
2274            let matched_contender = StubMatchedContender::new();
2275            let arena = make_matching_arena(
2276                vec![contender.clone()],
2277                vec![matched_contender.clone()],
2278                vec![],
2279                None,
2280            );
2281            contender.set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
2282            matched_contender.set_next_verify_event_result(VerifyEventResult::Mismatch(
2283                Reason::Basic("some reason"),
2284            ));
2285            arena.handle_input_event(make_unhandled_mouse_event()).await;
2286            assert_eq!(contender.calls_received(), 0);
2287            assert_eq!(matched_contender.verify_event_calls_received(), 0);
2288        }
2289
2290        #[fuchsia::test(allow_stalls = false)]
2291        async fn does_not_invoke_examine_or_verify_event_on_keyboard_event() {
2292            let contender = StubContender::new();
2293            let matched_contender = StubMatchedContender::new();
2294            let arena = make_matching_arena(
2295                vec![contender.clone()],
2296                vec![matched_contender.clone()],
2297                vec![],
2298                None,
2299            );
2300            contender.set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
2301            matched_contender.set_next_verify_event_result(VerifyEventResult::Mismatch(
2302                Reason::Basic("some reason"),
2303            ));
2304            arena.handle_input_event(make_unhandled_keyboard_event()).await;
2305            assert_eq!(contender.calls_received(), 0);
2306            assert_eq!(matched_contender.verify_event_calls_received(), 0);
2307        }
2308
2309        #[fuchsia::test(allow_stalls = false)]
2310        async fn does_not_repeat_event_to_matched_contender_returned_by_examine_event() {
2311            let contender = StubContender::new();
2312            let arena = make_matching_arena(
2313                vec![contender.clone()],
2314                vec![],
2315                vec![],
2316                // Make sure that `arena` does not progress to the forwarding state,
2317                // even if the logic _does_ repeat the event.
2318                Some(ContenderForever {}),
2319            );
2320
2321            // Configure `contender` to give a `MatchContender` to `arena`.
2322            let matched_contender = StubMatchedContender::new();
2323            contender.set_next_result(ExamineEventResult::MatchedContender(
2324                matched_contender.clone().into(),
2325            ));
2326
2327            // Set the return value for `matched_contender`. If the implementation
2328            // is buggy, and `verify_event()` is called, having a return value for
2329            // that call makes this test fail at the final assertion, which is easier
2330            // to understand.
2331            matched_contender.set_next_verify_event_result(VerifyEventResult::Mismatch(
2332                Reason::Basic("some reason"),
2333            ));
2334
2335            // Send the touchpad event, and validate that the arena did not call
2336            // `verify_event()` on the newly returned `MatchedContender`.
2337            arena.handle_input_event(make_unhandled_touchpad_event()).await;
2338            assert_eq!(matched_contender.verify_event_calls_received(), 0);
2339        }
2340
2341        #[fuchsia::test(allow_stalls = false)]
2342        async fn invokes_examine_event_for_new_event_with_contender_replaced_by_contender() {
2343            // Set up an arena with a `StubContender` that will continue the
2344            // matching process with a new `StubContender`.
2345            let initial_contender = StubContender::new();
2346            let arena = make_matching_arena(vec![initial_contender.clone()], vec![], vec![], None);
2347            let replacement_contender = StubContender::new();
2348            initial_contender.set_next_result(ExamineEventResult::Contender(
2349                replacement_contender.clone().into(),
2350            ));
2351
2352            // Process a touchpad event. This should cause `arena` to replace
2353            // `initial_contender` with `replacement_contender`.
2354            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
2355
2356            // Process another touchpad event. This should cause `arena` to invoke
2357            // `examine_event()` on `replacement_contender`.
2358            replacement_contender
2359                .set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
2360            arena.handle_input_event(make_unhandled_touchpad_event()).await;
2361
2362            // Verify that `replacement_contender` was called.
2363            assert_eq!(replacement_contender.calls_received(), 1);
2364        }
2365
2366        #[fuchsia::test(allow_stalls = false)]
2367        async fn invokes_verify_event_for_new_event_with_contender_replaced_by_matched_contender() {
2368            // Set up an arena with
2369            // * a `StubContender` that will continue the matching process with
2370            //   a new `StubMatchedContender`
2371            // * a `ContenderForever`, which will keep the arena in the matching
2372            //   state
2373            let initial_contender = StubContender::new();
2374            let arena = make_matching_arena(
2375                vec![initial_contender.clone()],
2376                vec![],
2377                vec![],
2378                Some(ContenderForever {}),
2379            );
2380            let replacement_contender = StubMatchedContender::new();
2381            initial_contender.set_next_result(ExamineEventResult::MatchedContender(
2382                replacement_contender.clone().into(),
2383            ));
2384
2385            // Process a touchpad event. This should cause `arena` to replace
2386            // `initial_contender` with `replacement_contender`.
2387            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
2388
2389            // Process another touchpad event. This should cause `arena` to invoke
2390            // `examine_event()` on `replacement_contender`.
2391            replacement_contender.set_next_verify_event_result(VerifyEventResult::Mismatch(
2392                Reason::Basic("some reason"),
2393            ));
2394            arena.handle_input_event(make_unhandled_touchpad_event()).await;
2395
2396            // Verify that `replacement_contender` was called.
2397            assert_eq!(replacement_contender.verify_event_calls_received(), 1);
2398        }
2399
2400        #[fuchsia::test(allow_stalls = false)]
2401        async fn invokes_verify_event_for_new_event_with_matched_contender_replaced_by_matched_contender()
2402         {
2403            let matched_contender = StubMatchedContender::new();
2404            let arena = make_matching_arena(
2405                vec![],
2406                vec![matched_contender.clone()],
2407                vec![],
2408                // Ensure that `arena` remains in the matching state. This simplifies
2409                // the test by eliminating the need to provide a response to a
2410                // potential `process_buffered_events()` call.
2411                Some(ContenderForever {}),
2412            );
2413
2414            // Configure `matched_contender` to replace itself with
2415            // `replacement_matched_contender`.
2416            let replacement_matched_contender = StubMatchedContender::new();
2417            matched_contender.set_next_verify_event_result(VerifyEventResult::MatchedContender(
2418                replacement_matched_contender.clone().into(),
2419            ));
2420
2421            // Process a touchpad event. This should cause `arena` to retain
2422            // replace `contender` with `replacement_contender`.
2423            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
2424
2425            // Set a return value for the expected call on `replacement_contender`.
2426            replacement_matched_contender.set_next_verify_event_result(
2427                VerifyEventResult::Mismatch(Reason::Basic("some reason")),
2428            );
2429
2430            // Process another touchpad event, and verify that `replacement_contender`
2431            // is called.
2432            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
2433            assert_eq!(replacement_matched_contender.verify_event_calls_received(), 1);
2434        }
2435
2436        #[fuchsia::test(allow_stalls = false)]
2437        async fn generates_no_events_on_mismatch_entering_idle() {
2438            let contender = StubContender::new();
2439            let arena = make_matching_arena(vec![contender.clone()], vec![], vec![], None);
2440            contender.set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
2441            assert_eq!(
2442                arena.clone().handle_input_event(make_unhandled_touchpad_event()).await,
2443                vec![]
2444            );
2445            assert_matches!(*arena.mutable_state.borrow(), MutableState::Idle);
2446        }
2447
2448        #[fuchsia::test(allow_stalls = false)]
2449        async fn generates_no_events_on_mismatch_entering_chain() {
2450            let contender = StubContender::new();
2451            let arena = make_matching_arena(vec![contender.clone()], vec![], vec![], None);
2452            contender.set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
2453
2454            let touchpad_event = input_device::InputEvent {
2455                event_time: zx::MonotonicInstant::from_nanos(123456),
2456                device_event: input_device::InputDeviceEvent::Touchpad(
2457                    touch_binding::TouchpadEvent {
2458                        injector_contacts: vec![touch_binding::TouchContact {
2459                            id: 1,
2460                            position: Position { x: 0.0, y: 0.0 },
2461                            ..TOUCH_CONTACT_INDEX_FINGER
2462                        }],
2463                        pressed_buttons: hashset! {},
2464                    },
2465                ),
2466                device_descriptor: make_touchpad_descriptor(),
2467                trace_id: None,
2468                handled: input_device::Handled::No,
2469            };
2470
2471            pretty_assertions::assert_eq!(
2472                arena.clone().handle_input_event(touchpad_event).await,
2473                vec![]
2474            );
2475
2476            assert_matches!(*arena.mutable_state.borrow(), MutableState::Chain);
2477        }
2478
2479        #[fuchsia::test(allow_stalls = false)]
2480        async fn generates_no_events_on_contender() {
2481            let contender = StubContender::new();
2482            let arena = make_matching_arena(vec![contender.clone()], vec![], vec![], None);
2483            contender.set_next_result(ExamineEventResult::Contender(contender.clone().into()));
2484            assert_eq!(arena.handle_input_event(make_unhandled_touchpad_event()).await, vec![]);
2485        }
2486
2487        #[fuchsia::test(allow_stalls = false)]
2488        async fn generates_no_events_on_multiple_matched_contenders() {
2489            let first_matched_contender = StubMatchedContender::new();
2490            let second_matched_contender = StubMatchedContender::new();
2491            let arena = make_matching_arena(
2492                vec![],
2493                vec![first_matched_contender.clone(), second_matched_contender.clone()],
2494                vec![],
2495                None,
2496            );
2497            first_matched_contender.set_next_verify_event_result(
2498                VerifyEventResult::MatchedContender(first_matched_contender.clone().into()),
2499            );
2500            second_matched_contender.set_next_verify_event_result(
2501                VerifyEventResult::MatchedContender(second_matched_contender.clone().into()),
2502            );
2503            assert_eq!(arena.handle_input_event(make_unhandled_touchpad_event()).await, vec![]);
2504        }
2505
2506        #[test_case(Some(StubWinner::new()); "with_winner")]
2507        #[test_case(None; "without_winner")]
2508        #[fuchsia::test(allow_stalls = false)]
2509        async fn generates_events_from_process_buffered_events_on_single_matched_contender(
2510            winner: Option<StubWinner>,
2511        ) {
2512            let matched_contender = StubMatchedContender::new();
2513            let arena = make_matching_arena(vec![], vec![matched_contender.clone()], vec![], None);
2514            matched_contender.set_next_verify_event_result(VerifyEventResult::MatchedContender(
2515                matched_contender.clone().into(),
2516            ));
2517            matched_contender.set_next_process_buffered_events_result(
2518                ProcessBufferedEventsResult {
2519                    generated_events: vec![
2520                        MouseEvent {
2521                            timestamp: zx::MonotonicInstant::from_nanos(123),
2522                            mouse_data: mouse_binding::MouseEvent {
2523                                location: mouse_binding::MouseLocation::Relative(
2524                                    mouse_binding::RelativeLocation {
2525                                        millimeters: Position::zero(),
2526                                    },
2527                                ),
2528                                wheel_delta_v: None,
2529                                wheel_delta_h: None,
2530                                phase: mouse_binding::MousePhase::Down,
2531                                affected_buttons: hashset! { PRIMARY_BUTTON },
2532                                pressed_buttons: hashset! { PRIMARY_BUTTON },
2533                                is_precision_scroll: None,
2534                                wake_lease: None.into(),
2535                            },
2536                        },
2537                        MouseEvent {
2538                            timestamp: zx::MonotonicInstant::from_nanos(456),
2539                            mouse_data: mouse_binding::MouseEvent {
2540                                location: mouse_binding::MouseLocation::Relative(
2541                                    mouse_binding::RelativeLocation {
2542                                        millimeters: Position::zero(),
2543                                    },
2544                                ),
2545                                wheel_delta_v: None,
2546                                wheel_delta_h: None,
2547                                phase: mouse_binding::MousePhase::Up,
2548                                affected_buttons: hashset! { PRIMARY_BUTTON },
2549                                pressed_buttons: hashset! {},
2550                                is_precision_scroll: None,
2551                                wake_lease: None.into(),
2552                            },
2553                        },
2554                    ],
2555                    winner: winner.map(std::convert::From::<StubWinner>::from),
2556                    recognized_gesture: RecognizedGesture::Motion,
2557                },
2558            );
2559            assert_matches!(
2560                arena
2561                    .handle_input_event(make_unhandled_touchpad_event())
2562                    .await
2563                    .as_slice(),
2564                [
2565                    input_device::InputEvent {
2566                        handled: input_device::Handled::No,
2567                        device_event: input_device::InputDeviceEvent::Mouse(
2568                            mouse_binding::MouseEvent {
2569                                pressed_buttons: first_pressed_buttons, ..
2570                            }
2571                        ),
2572                        ..
2573                    },
2574                    input_device::InputEvent {
2575                        handled: input_device::Handled::No,
2576                        device_event: input_device::InputDeviceEvent::Mouse(
2577                            mouse_binding::MouseEvent {
2578                                pressed_buttons: second_pressed_buttons, ..
2579                            }
2580                        ),
2581                        ..
2582                    },
2583                ] => {
2584                    pretty_assertions::assert_eq!(*first_pressed_buttons, hashset! { PRIMARY_BUTTON });
2585                    pretty_assertions::assert_eq!(*second_pressed_buttons, hashset! {});
2586                }
2587            );
2588        }
2589
2590        #[fuchsia::test(allow_stalls = false)]
2591        async fn passes_all_buffered_events_to_process_buffered_events() {
2592            // Create an arena, and seed it a buffered event (emulating what happens
2593            // as the arena transitions from Idle to Matching).
2594            let contender = StubContender::new();
2595            let matched_contender = StubMatchedContender::new();
2596            let arena = make_matching_arena(
2597                vec![contender.clone()],
2598                vec![matched_contender.clone()],
2599                vec![TouchpadEvent {
2600                    timestamp: zx::MonotonicInstant::from_nanos(123),
2601                    contacts: vec![],
2602                    pressed_buttons: vec![],
2603                    filtered_palm_contacts: vec![],
2604                }],
2605                None,
2606            );
2607
2608            // Send another event to the arena, to exercise the case of an event
2609            // being buffered because the contest continued.
2610            contender.set_next_result(ExamineEventResult::Contender(contender.clone().into()));
2611            matched_contender.set_next_verify_event_result(VerifyEventResult::MatchedContender(
2612                matched_contender.clone().into(),
2613            ));
2614            arena
2615                .clone()
2616                .handle_input_event(input_device::InputEvent {
2617                    event_time: zx::MonotonicInstant::from_nanos(456),
2618                    device_event: input_device::InputDeviceEvent::Touchpad(
2619                        touch_binding::TouchpadEvent {
2620                            injector_contacts: vec![],
2621                            pressed_buttons: hashset! {},
2622                        },
2623                    ),
2624                    device_descriptor: make_touchpad_descriptor(),
2625                    trace_id: None,
2626                    handled: input_device::Handled::No,
2627                })
2628                .await;
2629
2630            // Send the event that concludes the contest.
2631            contender.set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
2632            matched_contender.set_next_verify_event_result(VerifyEventResult::MatchedContender(
2633                matched_contender.clone().into(),
2634            ));
2635            matched_contender.set_next_process_buffered_events_result(
2636                ProcessBufferedEventsResult {
2637                    generated_events: vec![],
2638                    winner: None,
2639                    recognized_gesture: RecognizedGesture::Motion,
2640                },
2641            );
2642            arena
2643                .handle_input_event(input_device::InputEvent {
2644                    event_time: zx::MonotonicInstant::from_nanos(789),
2645                    device_event: input_device::InputDeviceEvent::Touchpad(
2646                        touch_binding::TouchpadEvent {
2647                            injector_contacts: vec![],
2648                            pressed_buttons: hashset! {},
2649                        },
2650                    ),
2651                    device_descriptor: make_touchpad_descriptor(),
2652                    trace_id: None,
2653                    handled: input_device::Handled::No,
2654                })
2655                .await;
2656
2657            // Verify that the contender received all three events.
2658            assert_eq!(
2659                matched_contender
2660                    .get_last_processed_buffered_events_args()
2661                    .map(|vec| vec
2662                        .into_iter()
2663                        .map(|event| event.timestamp.into_nanos())
2664                        .collect::<Vec<_>>())
2665                    .as_deref(),
2666                Some([123, 456, 789].as_slice())
2667            );
2668        }
2669
2670        #[fuchsia::test(allow_stalls = false)]
2671        async fn transitions_to_idle_when_sole_contender_does_not_match() {
2672            let contender = StubContender::new();
2673            let arena = make_matching_arena(vec![contender.clone()], vec![], vec![], None);
2674            contender.set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
2675            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
2676            assert_matches!(*arena.mutable_state.borrow(), MutableState::Idle);
2677        }
2678
2679        #[fuchsia::test(allow_stalls = false)]
2680        async fn transitions_to_idle_when_sole_matched_contender_does_not_match() {
2681            let matched_contender = StubMatchedContender::new();
2682            let arena = make_matching_arena(vec![], vec![matched_contender.clone()], vec![], None);
2683            matched_contender.set_next_verify_event_result(VerifyEventResult::Mismatch(
2684                Reason::Basic("some reason"),
2685            ));
2686            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
2687            assert_matches!(*arena.mutable_state.borrow(), MutableState::Idle);
2688        }
2689
2690        #[fuchsia::test(allow_stalls = false)]
2691        async fn remains_in_matching_when_a_contender_remains() {
2692            let contender = StubContender::new();
2693            let arena = make_matching_arena(vec![contender.clone()], vec![], vec![], None);
2694            contender.set_next_result(ExamineEventResult::Contender(contender.clone().into()));
2695            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
2696            assert_matches!(*arena.mutable_state.borrow(), MutableState::Matching { .. });
2697        }
2698
2699        #[fuchsia::test(allow_stalls = false)]
2700        async fn remains_in_matching_when_multiple_matched_contenders_remain() {
2701            let matched_contender_a = StubMatchedContender::new();
2702            let matched_contender_b = StubMatchedContender::new();
2703            let arena = make_matching_arena(
2704                vec![],
2705                vec![matched_contender_a.clone(), matched_contender_b.clone()],
2706                vec![],
2707                None,
2708            );
2709            matched_contender_a.set_next_verify_event_result(VerifyEventResult::MatchedContender(
2710                matched_contender_a.clone().into(),
2711            ));
2712            matched_contender_b.set_next_verify_event_result(VerifyEventResult::MatchedContender(
2713                matched_contender_b.clone().into(),
2714            ));
2715            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
2716            assert_matches!(*arena.mutable_state.borrow(), MutableState::Matching { .. });
2717        }
2718
2719        #[fuchsia::test(allow_stalls = false)]
2720        async fn transitions_to_idle_when_sole_matched_contender_returns_no_winner() {
2721            let matched_contender = StubMatchedContender::new();
2722            let arena = make_matching_arena(vec![], vec![matched_contender.clone()], vec![], None);
2723            matched_contender.set_next_verify_event_result(VerifyEventResult::MatchedContender(
2724                matched_contender.clone().into(),
2725            ));
2726            matched_contender.set_next_process_buffered_events_result(
2727                ProcessBufferedEventsResult {
2728                    generated_events: vec![],
2729                    winner: None,
2730                    recognized_gesture: RecognizedGesture::Motion,
2731                },
2732            );
2733            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
2734            assert_matches!(*arena.mutable_state.borrow(), MutableState::Idle);
2735        }
2736
2737        #[fuchsia::test(allow_stalls = false)]
2738        async fn transitions_to_forwarding_when_sole_matched_contender_returns_a_winner() {
2739            let matched_contender = StubMatchedContender::new();
2740            let arena = make_matching_arena(vec![], vec![matched_contender.clone()], vec![], None);
2741            matched_contender.set_next_verify_event_result(VerifyEventResult::MatchedContender(
2742                matched_contender.clone().into(),
2743            ));
2744            matched_contender.set_next_process_buffered_events_result(
2745                ProcessBufferedEventsResult {
2746                    generated_events: vec![],
2747                    winner: Some(StubWinner::new().into()),
2748                    recognized_gesture: RecognizedGesture::Motion,
2749                },
2750            );
2751            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
2752            assert_matches!(*arena.mutable_state.borrow(), MutableState::Forwarding { .. });
2753        }
2754    }
2755
2756    mod forwarding_state {
2757        use super::super::{
2758            Contender, EndGestureEvent, ExamineEventResult, GestureArena, InputHandler, MouseEvent,
2759            MutableState, ProcessNewEventResult, Reason, RecognizedGesture, TouchpadEvent,
2760        };
2761        use super::utils::{
2762            ContenderFactoryOnceOrPanic, ContenderForever, StubContender, StubWinner,
2763            TOUCH_CONTACT_INDEX_FINGER, make_touchpad_descriptor, make_unhandled_keyboard_event,
2764            make_unhandled_mouse_event, make_unhandled_touchpad_event,
2765        };
2766        use crate::input_handler::InputHandlerStatus;
2767        use crate::{Position, input_device, metrics, mouse_binding, touch_binding};
2768        use assert_matches::assert_matches;
2769
2770        use maplit::hashset;
2771        use pretty_assertions::assert_eq;
2772        use std::cell::RefCell;
2773        use std::rc::Rc;
2774        use test_case::test_case;
2775
2776        /// Creates an arena in the forwarding state, with
2777        /// a) the given `winner`, and
2778        /// b) an appropriate contender factory
2779        ///
2780        /// If `contender` is `None`, the contender factory will abort on the first
2781        /// call.
2782        ///
2783        /// If `contender` is `Some`, the contender factory will return the content
2784        /// of the `Option` on the first call, and abort on the second call.
2785        ///
2786        /// The former is the common case for the tests. The latter is useful for
2787        /// tests that exercise the arena's handling of an `EndGesture(Some)`.
2788        fn make_forwarding_arena(
2789            winner: StubWinner,
2790            contender: Option<Box<dyn Contender>>,
2791        ) -> Rc<GestureArena> {
2792            let contender_factory = match contender {
2793                Some(c) => Box::new(ContenderFactoryOnceOrPanic::new(vec![c])),
2794                None => Box::new(ContenderFactoryOnceOrPanic::new_panic_when_call()),
2795            };
2796
2797            Rc::new(GestureArena {
2798                contender_factory,
2799                mutable_state: RefCell::new(MutableState::Forwarding {
2800                    winner: winner.into(),
2801                    current_gesture: RecognizedGesture::Motion,
2802                    // Tests that care about `gesture_start_timestamp` should take care
2803                    // to set that value themselves. Default to a value that should cause
2804                    // any test that relies on a good value to fail.
2805                    gesture_start_timestamp: zx::MonotonicInstant::INFINITE_PAST,
2806                    num_events: 0,
2807                }),
2808                inspect_log: RefCell::new(fuchsia_inspect_contrib::nodes::BoundedListNode::new(
2809                    fuchsia_inspect::Inspector::default().root().create_child("some_key"),
2810                    1,
2811                )),
2812                metrics_logger: metrics::MetricsLogger::default(),
2813                inspect_status: InputHandlerStatus::default(),
2814            })
2815        }
2816
2817        #[fuchsia::test(allow_stalls = false)]
2818        async fn invokes_process_new_event_on_touchpad_event() {
2819            let winner = StubWinner::new();
2820            let arena = make_forwarding_arena(winner.clone(), None);
2821            winner.set_next_result(ProcessNewEventResult::EndGesture(
2822                EndGestureEvent::NoEvent,
2823                Reason::Basic("some reason"),
2824            ));
2825            arena.handle_input_event(make_unhandled_touchpad_event()).await;
2826            assert_eq!(winner.calls_received(), 1);
2827        }
2828
2829        #[fuchsia::test(allow_stalls = false)]
2830        async fn does_not_invoke_process_new_event_on_mouse_event() {
2831            let winner = StubWinner::new();
2832            let arena = make_forwarding_arena(winner.clone(), None);
2833            winner.set_next_result(ProcessNewEventResult::EndGesture(
2834                EndGestureEvent::NoEvent,
2835                Reason::Basic("some reason"),
2836            ));
2837            arena.handle_input_event(make_unhandled_mouse_event()).await;
2838            assert_eq!(winner.calls_received(), 0);
2839        }
2840
2841        #[fuchsia::test(allow_stalls = false)]
2842        async fn does_not_invoke_process_new_event_on_keyboard_event() {
2843            let winner = StubWinner::new();
2844            let arena = make_forwarding_arena(winner.clone(), None);
2845            winner.set_next_result(ProcessNewEventResult::EndGesture(
2846                EndGestureEvent::NoEvent,
2847                Reason::Basic("some reason"),
2848            ));
2849            arena.handle_input_event(make_unhandled_keyboard_event()).await;
2850            assert_eq!(winner.calls_received(), 0);
2851        }
2852
2853        #[fuchsia::test(allow_stalls = false)]
2854        async fn invokes_process_new_event_for_multiple_new_events() {
2855            // Create `arena` with `winner` and a `ContenderForever`. The latter
2856            // makes the test fail in a more useful way if the `GestureArena`
2857            // is buggy.
2858            let winner = StubWinner::new();
2859            let arena = make_forwarding_arena(winner.clone(), Some(ContenderForever {}.into()));
2860
2861            // Send two events to `arena`.
2862            winner.set_next_result(ProcessNewEventResult::ContinueGesture(
2863                None,
2864                winner.clone().into(),
2865            ));
2866            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
2867            winner.set_next_result(ProcessNewEventResult::ContinueGesture(
2868                None,
2869                winner.clone().into(),
2870            ));
2871            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
2872
2873            // Verify `winner` was called as expected.
2874            assert_eq!(winner.calls_received(), 2);
2875        }
2876
2877        #[fuchsia::test(allow_stalls = false)]
2878        async fn generates_event_on_continue_gesture_with_mouse_event() {
2879            let winner = StubWinner::new();
2880            let arena = make_forwarding_arena(winner.clone(), None);
2881            winner.set_next_result(ProcessNewEventResult::ContinueGesture(
2882                Some(MouseEvent {
2883                    timestamp: zx::MonotonicInstant::from_nanos(123),
2884                    mouse_data: mouse_binding::MouseEvent {
2885                        location: mouse_binding::MouseLocation::Relative(
2886                            mouse_binding::RelativeLocation { millimeters: Position::zero() },
2887                        ),
2888                        wheel_delta_v: None,
2889                        wheel_delta_h: None,
2890                        phase: mouse_binding::MousePhase::Move,
2891                        affected_buttons: hashset! {},
2892                        pressed_buttons: hashset! {},
2893                        is_precision_scroll: None,
2894                        wake_lease: None.into(),
2895                    },
2896                }),
2897                winner.clone().into(),
2898            ));
2899            assert_matches!(
2900                arena.handle_input_event(make_unhandled_touchpad_event()).await.as_slice(),
2901                [
2902                    input_device::InputEvent {
2903                        event_time,
2904                        handled: input_device::Handled::No,
2905                        device_event: input_device::InputDeviceEvent::Mouse(_),
2906                        ..
2907                    },
2908                ] => pretty_assertions::assert_eq!(*event_time, zx::MonotonicInstant::from_nanos(123))
2909            );
2910        }
2911
2912        #[fuchsia::test(allow_stalls = false)]
2913        async fn generates_no_events_on_continue_gesture_without_mouse_event() {
2914            let winner = StubWinner::new();
2915            let arena = make_forwarding_arena(winner.clone(), None);
2916            winner.set_next_result(ProcessNewEventResult::ContinueGesture(
2917                None,
2918                winner.clone().into(),
2919            ));
2920            pretty_assertions::assert_eq!(
2921                arena.handle_input_event(make_unhandled_touchpad_event()).await.as_slice(),
2922                vec![]
2923            );
2924        }
2925
2926        #[fuchsia::test(allow_stalls = false)]
2927        async fn generates_no_events_on_end_gesture_without_touchpad_event() {
2928            let winner = StubWinner::new();
2929            let arena = make_forwarding_arena(winner.clone(), None);
2930            winner.set_next_result(ProcessNewEventResult::EndGesture(
2931                EndGestureEvent::NoEvent,
2932                Reason::Basic("some reason"),
2933            ));
2934            pretty_assertions::assert_eq!(
2935                arena.handle_input_event(make_unhandled_touchpad_event()).await.as_slice(),
2936                vec![]
2937            );
2938        }
2939
2940        #[fuchsia::test(allow_stalls = false)]
2941        async fn generates_no_events_on_end_gesture_with_unconsumed_touchpad_event_entering_idle() {
2942            // Create `arena` with a `StubContender` for processing the unconsumed
2943            // `TouchpadEvent`.
2944            let winner = StubWinner::new();
2945            let contender = StubContender::new();
2946            let arena = make_forwarding_arena(winner.clone(), Some(contender.clone().into()));
2947            winner.set_next_result(ProcessNewEventResult::EndGesture(
2948                EndGestureEvent::UnconsumedEvent(TouchpadEvent {
2949                    contacts: vec![],
2950                    pressed_buttons: vec![],
2951                    timestamp: zx::MonotonicInstant::ZERO,
2952                    filtered_palm_contacts: vec![],
2953                }),
2954                Reason::Basic("some reason"),
2955            ));
2956
2957            // Set a return value for the `examine_event()` call.
2958            contender.set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
2959
2960            // Verify no events were generated.
2961            pretty_assertions::assert_eq!(
2962                arena.clone().handle_input_event(make_unhandled_touchpad_event()).await.as_slice(),
2963                vec![]
2964            );
2965
2966            // Unconsumed event without contact will invoked in handle_event_while_idle,
2967            // but no contender want this event so stay in Idle state.
2968            assert_matches!(*arena.mutable_state.borrow(), MutableState::Idle { .. });
2969        }
2970
2971        #[fuchsia::test(allow_stalls = false)]
2972        async fn generates_no_events_on_end_gesture_with_unconsumed_touchpad_event_entering_chain()
2973        {
2974            // Create `arena` with a `StubContender` for processing the unconsumed
2975            // `TouchpadEvent`.
2976            let winner = StubWinner::new();
2977            let contender = StubContender::new();
2978            let arena = make_forwarding_arena(winner.clone(), Some(contender.clone().into()));
2979            winner.set_next_result(ProcessNewEventResult::EndGesture(
2980                EndGestureEvent::UnconsumedEvent(TouchpadEvent {
2981                    contacts: vec![touch_binding::TouchContact {
2982                        id: 1,
2983                        position: Position { x: 0.0, y: 0.0 },
2984                        pressure: None,
2985                        contact_size: None,
2986                    }],
2987                    pressed_buttons: vec![],
2988                    timestamp: zx::MonotonicInstant::from_nanos(123456),
2989                    filtered_palm_contacts: vec![],
2990                }),
2991                Reason::Basic("some reason"),
2992            ));
2993
2994            // Set a return value for the `examine_event()` call.
2995            contender.set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
2996
2997            let touchpad_event = input_device::InputEvent {
2998                event_time: zx::MonotonicInstant::from_nanos(123456),
2999                device_event: input_device::InputDeviceEvent::Touchpad(
3000                    touch_binding::TouchpadEvent {
3001                        injector_contacts: vec![touch_binding::TouchContact {
3002                            id: 1,
3003                            position: Position { x: 0.0, y: 0.0 },
3004                            ..TOUCH_CONTACT_INDEX_FINGER
3005                        }],
3006                        pressed_buttons: hashset! {},
3007                    },
3008                ),
3009                device_descriptor: make_touchpad_descriptor(),
3010                trace_id: None,
3011                handled: input_device::Handled::No,
3012            };
3013
3014            // Verify no events were generated.
3015            pretty_assertions::assert_eq!(
3016                arena.clone().handle_input_event(touchpad_event).await.as_slice(),
3017                vec![]
3018            );
3019
3020            // Unconsumed event with contact will invoked in handle_event_while_chain,
3021            // but no contender want this event so stay in Chain state.
3022            assert_matches!(*arena.mutable_state.borrow(), MutableState::Chain { .. });
3023        }
3024
3025        #[fuchsia::test(allow_stalls = false)]
3026        async fn generates_event_on_end_gesture_with_touchpad_event() {
3027            // Create `arena` with a `StubContender` for processing the unconsumed
3028            // `TouchpadEvent`.
3029            let winner = StubWinner::new();
3030            let arena = make_forwarding_arena(winner.clone(), None);
3031            let mouse_event = MouseEvent {
3032                timestamp: zx::MonotonicInstant::from_nanos(123),
3033                mouse_data: mouse_binding::MouseEvent {
3034                    location: mouse_binding::MouseLocation::Relative(
3035                        mouse_binding::RelativeLocation { millimeters: Position::zero() },
3036                    ),
3037                    wheel_delta_v: None,
3038                    wheel_delta_h: None,
3039                    phase: mouse_binding::MousePhase::Move,
3040                    affected_buttons: hashset! {},
3041                    pressed_buttons: hashset! {},
3042                    is_precision_scroll: None,
3043                    wake_lease: None.into(),
3044                },
3045            };
3046            winner.set_next_result(ProcessNewEventResult::EndGesture(
3047                EndGestureEvent::GeneratedEvent(mouse_event),
3048                Reason::Basic("some reason"),
3049            ));
3050
3051            // Verify events were generated.
3052            assert_matches!(
3053                arena.handle_input_event(make_unhandled_touchpad_event()).await.as_slice(),
3054                [
3055                    input_device::InputEvent {
3056                        event_time,
3057                        handled: input_device::Handled::No,
3058                        device_event: input_device::InputDeviceEvent::Mouse(_),
3059                        ..
3060                    },
3061                ] => pretty_assertions::assert_eq!(*event_time, zx::MonotonicInstant::from_nanos(123))
3062            );
3063        }
3064
3065        #[test_case(Some(MouseEvent{
3066            timestamp: zx::MonotonicInstant::from_nanos(123),
3067            mouse_data: mouse_binding::MouseEvent {
3068                location: mouse_binding::MouseLocation::Relative(
3069                    mouse_binding::RelativeLocation {
3070                        millimeters: Position::zero(),
3071                    },
3072                ),
3073                wheel_delta_v: None,
3074                wheel_delta_h: None,
3075                phase: mouse_binding::MousePhase::Move,
3076                affected_buttons: hashset! {},
3077                pressed_buttons: hashset! {},
3078                is_precision_scroll: None,
3079                    wake_lease: None.into(),
3080
3081            },
3082        }); "with_mouse_event")]
3083        #[test_case(None; "without_mouse_event")]
3084        #[fuchsia::test(allow_stalls = false)]
3085        async fn remains_in_forwarding_on_continue_gesture(mouse_event: Option<MouseEvent>) {
3086            let winner = StubWinner::new();
3087            let arena = make_forwarding_arena(winner.clone(), None);
3088            winner.set_next_result(ProcessNewEventResult::ContinueGesture(
3089                mouse_event,
3090                winner.clone().into(),
3091            ));
3092            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
3093            assert_matches!(*arena.mutable_state.borrow(), MutableState::Forwarding { .. });
3094        }
3095
3096        #[fuchsia::test(allow_stalls = false)]
3097        async fn transitions_to_idle_on_end_gesture_with_touchpad_event() {
3098            let winner = StubWinner::new();
3099            let arena = make_forwarding_arena(winner.clone(), None);
3100            let mouse_event = MouseEvent {
3101                timestamp: zx::MonotonicInstant::from_nanos(123),
3102                mouse_data: mouse_binding::MouseEvent {
3103                    location: mouse_binding::MouseLocation::Relative(
3104                        mouse_binding::RelativeLocation { millimeters: Position::zero() },
3105                    ),
3106                    wheel_delta_v: None,
3107                    wheel_delta_h: None,
3108                    phase: mouse_binding::MousePhase::Move,
3109                    affected_buttons: hashset! {},
3110                    pressed_buttons: hashset! {},
3111                    is_precision_scroll: None,
3112                    wake_lease: None.into(),
3113                },
3114            };
3115            winner.set_next_result(ProcessNewEventResult::EndGesture(
3116                EndGestureEvent::GeneratedEvent(mouse_event),
3117                Reason::Basic("some reason"),
3118            ));
3119            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
3120            assert_matches!(*arena.mutable_state.borrow(), MutableState::Idle { .. });
3121        }
3122
3123        #[fuchsia::test(allow_stalls = false)]
3124        async fn transitions_to_chain_on_end_gesture_with_touchpad_event() {
3125            let winner = StubWinner::new();
3126            let arena = make_forwarding_arena(winner.clone(), None);
3127            let mouse_event = MouseEvent {
3128                timestamp: zx::MonotonicInstant::from_nanos(123),
3129                mouse_data: mouse_binding::MouseEvent {
3130                    location: mouse_binding::MouseLocation::Relative(
3131                        mouse_binding::RelativeLocation { millimeters: Position::zero() },
3132                    ),
3133                    wheel_delta_v: None,
3134                    wheel_delta_h: None,
3135                    phase: mouse_binding::MousePhase::Move,
3136                    affected_buttons: hashset! {},
3137                    pressed_buttons: hashset! {},
3138                    is_precision_scroll: None,
3139                    wake_lease: None.into(),
3140                },
3141            };
3142            winner.set_next_result(ProcessNewEventResult::EndGesture(
3143                EndGestureEvent::GeneratedEvent(mouse_event),
3144                Reason::Basic("some reason"),
3145            ));
3146            let touchpad_event = input_device::InputEvent {
3147                event_time: zx::MonotonicInstant::from_nanos(123456),
3148                device_event: input_device::InputDeviceEvent::Touchpad(
3149                    touch_binding::TouchpadEvent {
3150                        injector_contacts: vec![touch_binding::TouchContact {
3151                            id: 1,
3152                            position: Position { x: 0.0, y: 0.0 },
3153                            ..TOUCH_CONTACT_INDEX_FINGER
3154                        }],
3155                        pressed_buttons: hashset! {},
3156                    },
3157                ),
3158                device_descriptor: make_touchpad_descriptor(),
3159                trace_id: None,
3160                handled: input_device::Handled::No,
3161            };
3162            arena.clone().handle_input_event(touchpad_event).await;
3163            assert_matches!(*arena.mutable_state.borrow(), MutableState::Chain);
3164        }
3165
3166        #[fuchsia::test(allow_stalls = false)]
3167        async fn transitions_to_idle_on_end_gesture_without_touchpad_event() {
3168            let winner = StubWinner::new();
3169            let arena = make_forwarding_arena(winner.clone(), None);
3170            winner.set_next_result(ProcessNewEventResult::EndGesture(
3171                EndGestureEvent::NoEvent,
3172                Reason::Basic("reason"),
3173            ));
3174            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
3175            assert_matches!(*arena.mutable_state.borrow(), MutableState::Idle);
3176        }
3177
3178        #[fuchsia::test(allow_stalls = false)]
3179        async fn starts_new_contest_on_end_gesture_with_touchpad_event() {
3180            // Set up an arena in the forwarding state with `winner`, and
3181            // with a factory that will return `contender`. The latter should
3182            // be called at the start of the new contest.
3183            let winner = StubWinner::new();
3184            let contender = StubContender::new();
3185            let arena = make_forwarding_arena(winner.clone(), Some(contender.clone().into()));
3186
3187            // Set up `winner` to end the gesture and return an unconsumed event.
3188            winner.set_next_result(ProcessNewEventResult::EndGesture(
3189                EndGestureEvent::UnconsumedEvent(TouchpadEvent {
3190                    timestamp: zx::MonotonicInstant::ZERO,
3191                    contacts: vec![],
3192                    pressed_buttons: vec![],
3193                    filtered_palm_contacts: vec![],
3194                }),
3195                Reason::Basic("reason"),
3196            ));
3197
3198            // Set up `contender` to reply to the `examine_event()` call.
3199            contender.set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
3200
3201            // Send an event into the arena.
3202            arena.handle_input_event(make_unhandled_touchpad_event()).await;
3203
3204            // Verify that the arena started a new contest.
3205            assert_eq!(contender.calls_received(), 1);
3206        }
3207    }
3208
3209    mod touchpad_event_payload {
3210        use super::super::{ExamineEventResult, GestureArena, InputHandler, Reason, args};
3211        use super::utils::{
3212            ContenderFactoryOnceOrPanic, StubContender, TOUCH_CONTACT_INDEX_FINGER,
3213        };
3214        use crate::utils::Size;
3215        use crate::{Position, input_device, metrics, touch_binding};
3216        use assert_matches::assert_matches;
3217        use fidl_fuchsia_input_report::{self as fidl_input_report, UnitType};
3218
3219        use maplit::hashset;
3220        use std::rc::Rc;
3221        use test_case::test_case;
3222        use test_util::assert_near;
3223
3224        fn make_touchpad_descriptor(
3225            units: Vec<(fidl_input_report::Unit, fidl_input_report::Unit)>,
3226        ) -> input_device::InputDeviceDescriptor {
3227            let contacts: Vec<_> = units
3228                .into_iter()
3229                .map(|(x_unit, y_unit)| touch_binding::ContactDeviceDescriptor {
3230                    x_range: fidl_input_report::Range { min: 0, max: 1_000_000 },
3231                    y_range: fidl_input_report::Range { min: 0, max: 1_000_000 },
3232                    x_unit,
3233                    y_unit,
3234                    pressure_range: None,
3235                    width_range: Some(fidl_input_report::Range { min: 0, max: 10_000 }),
3236                    height_range: Some(fidl_input_report::Range { min: 0, max: 10_000 }),
3237                })
3238                .collect();
3239            input_device::InputDeviceDescriptor::Touchpad(touch_binding::TouchpadDeviceDescriptor {
3240                device_id: 1,
3241                contacts,
3242            })
3243        }
3244
3245        fn make_unhandled_touchpad_event_with_contacts(
3246            contact_position_units: Vec<(fidl_input_report::Unit, fidl_input_report::Unit)>,
3247            injector_contacts: Vec<touch_binding::TouchContact>,
3248        ) -> input_device::InputEvent {
3249            input_device::InputEvent {
3250                device_event: input_device::InputDeviceEvent::Touchpad(
3251                    touch_binding::TouchpadEvent {
3252                        injector_contacts,
3253                        pressed_buttons: hashset! {},
3254                    },
3255                ),
3256                device_descriptor: make_touchpad_descriptor(contact_position_units),
3257                event_time: zx::MonotonicInstant::ZERO,
3258                trace_id: None,
3259                handled: input_device::Handled::No,
3260            }
3261        }
3262
3263        fn make_unhandled_touchpad_event_with_positions(
3264            contact_position_units: Vec<(fidl_input_report::Unit, fidl_input_report::Unit)>,
3265            positions: Vec<Position>,
3266        ) -> input_device::InputEvent {
3267            let injector_contacts: Vec<_> = positions
3268                .into_iter()
3269                .enumerate()
3270                .map(|(i, position)| touch_binding::TouchContact {
3271                    id: u32::try_from(i).unwrap(),
3272                    position,
3273                    contact_size: None,
3274                    pressure: None,
3275                })
3276                .collect();
3277            make_unhandled_touchpad_event_with_contacts(contact_position_units, injector_contacts)
3278        }
3279
3280        #[test_case(
3281            vec![(
3282                fidl_input_report::Unit{ type_: UnitType::Meters, exponent: -6 },
3283                fidl_input_report::Unit{ type_: UnitType::Meters, exponent: -6 },
3284            )],
3285            vec![
3286                touch_binding::TouchContact{
3287                    id: 1,
3288                    position: Position { x: 200000.0, y: 100000.0 },
3289                    contact_size: Some(Size {
3290                        width: 2500.0,
3291                        height: 1500.0,
3292                    }),
3293                    pressure: None,
3294                }
3295            ]; "from_micrometers")]
3296        #[test_case(
3297            vec![(
3298                fidl_input_report::Unit{ type_: UnitType::Meters, exponent: -2 },
3299                fidl_input_report::Unit{ type_: UnitType::Meters, exponent: -2 },
3300            )],
3301            vec![
3302                touch_binding::TouchContact{
3303                    id: 1,
3304                    position: Position { x: 20.0, y: 10.0 },
3305                    contact_size: Some(Size {
3306                        width: 0.25,
3307                        height: 0.15,
3308                    }),
3309                    pressure: None,
3310                }
3311            ]; "from_centimeters")]
3312        #[fuchsia::test(allow_stalls = false)]
3313        async fn provides_recognizer_position_size_in_millimeters(
3314            contact_position_units: Vec<(fidl_input_report::Unit, fidl_input_report::Unit)>,
3315            contacts: Vec<touch_binding::TouchContact>,
3316        ) {
3317            let contender = Box::new(StubContender::new());
3318            let contender_factory = ContenderFactoryOnceOrPanic::new(vec![contender.clone()]);
3319            let arena = Rc::new(GestureArena::new_for_test(
3320                Box::new(contender_factory),
3321                &fuchsia_inspect::Inspector::default(),
3322                1,
3323                metrics::MetricsLogger::default(),
3324            ));
3325            contender.set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
3326            arena
3327                .handle_input_event(make_unhandled_touchpad_event_with_contacts(
3328                    contact_position_units,
3329                    contacts,
3330                ))
3331                .await;
3332            assert_matches!(
3333                contender.get_last_touchpad_event().unwrap().contacts.as_slice(),
3334                [touch_binding::TouchContact { position, contact_size: Some(size), .. }] => {
3335                    assert_near!(position.x, 200.0, 1.0);
3336                    assert_near!(position.y, 100.0, 1.0);
3337                    assert_near!(size.width, 2.5, 0.1);
3338                    assert_near!(size.height, 1.5, 0.1);
3339                }
3340            );
3341        }
3342
3343        #[test_case(
3344            touch_binding::TouchpadEvent {
3345                injector_contacts: vec![
3346                    touch_binding::TouchContact{
3347                        id: 1,
3348                        position: Position { x: 0.0, y: 0.0 },
3349                        contact_size: Some(Size {
3350                            width: args::MIN_PALM_SIZE_MM,
3351                            height: 0.15,
3352                        }),
3353                        pressure: None,
3354                    }
3355                ],
3356                pressed_buttons: hashset! {},
3357            }; "only palm contact"
3358        )]
3359        #[fuchsia::test(allow_stalls = false)]
3360        async fn ignore_palm_contact(event: touch_binding::TouchpadEvent) {
3361            let contender = Box::new(StubContender::new());
3362            let contender_factory = ContenderFactoryOnceOrPanic::new(vec![contender.clone()]);
3363            let arena = Rc::new(GestureArena::new_for_test(
3364                Box::new(contender_factory),
3365                &fuchsia_inspect::Inspector::default(),
3366                1,
3367                metrics::MetricsLogger::default(),
3368            ));
3369            contender.set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
3370
3371            let input_event = input_device::InputEvent {
3372                device_event: input_device::InputDeviceEvent::Touchpad(event),
3373                device_descriptor: make_touchpad_descriptor(vec![(
3374                    fidl_input_report::Unit { type_: UnitType::Meters, exponent: -3 },
3375                    fidl_input_report::Unit { type_: UnitType::Meters, exponent: -3 },
3376                )]),
3377                event_time: zx::MonotonicInstant::ZERO,
3378                trace_id: None,
3379                handled: input_device::Handled::No,
3380            };
3381
3382            arena.handle_input_event(input_event).await;
3383            assert_matches!(contender.get_last_touchpad_event().unwrap().contacts.as_slice(), []);
3384        }
3385
3386        #[test_case(
3387            touch_binding::TouchpadEvent {
3388                injector_contacts: vec![
3389                    TOUCH_CONTACT_INDEX_FINGER,
3390                    touch_binding::TouchContact{
3391                        id: 1,
3392                        position: Position { x: 0.0, y: 0.0 },
3393                        contact_size: Some(Size {
3394                            width: args::MIN_PALM_SIZE_MM,
3395                            height: 0.15,
3396                        }),
3397                        pressure: None,
3398                    }
3399                ],
3400                pressed_buttons: hashset! {},
3401            }, vec![]; "palm contact and finger"
3402        )]
3403        #[fuchsia::test(allow_stalls = false)]
3404        async fn ignore_palm_contact_keep_finger(
3405            event: touch_binding::TouchpadEvent,
3406            expect_buttons: Vec<u8>,
3407        ) {
3408            let contender = Box::new(StubContender::new());
3409            let contender_factory = ContenderFactoryOnceOrPanic::new(vec![contender.clone()]);
3410            let arena = Rc::new(GestureArena::new_for_test(
3411                Box::new(contender_factory),
3412                &fuchsia_inspect::Inspector::default(),
3413                1,
3414                metrics::MetricsLogger::default(),
3415            ));
3416            contender.set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
3417
3418            let input_event = input_device::InputEvent {
3419                device_event: input_device::InputDeviceEvent::Touchpad(event),
3420                device_descriptor: make_touchpad_descriptor(vec![(
3421                    fidl_input_report::Unit { type_: UnitType::Meters, exponent: -3 },
3422                    fidl_input_report::Unit { type_: UnitType::Meters, exponent: -3 },
3423                )]),
3424                event_time: zx::MonotonicInstant::ZERO,
3425                trace_id: None,
3426                handled: input_device::Handled::No,
3427            };
3428
3429            arena.handle_input_event(input_event).await;
3430            let got = contender.get_last_touchpad_event().unwrap();
3431            assert_eq!(got.contacts.as_slice(), [TOUCH_CONTACT_INDEX_FINGER]);
3432            assert_eq!(got.pressed_buttons, expect_buttons);
3433        }
3434
3435        #[test_case(
3436            touch_binding::TouchpadEvent {
3437                injector_contacts: vec![
3438                    touch_binding::TouchContact{
3439                        id: 1,
3440                        position: Position { x: 0.0, y: 0.0 },
3441                        contact_size: Some(Size {
3442                            width: args::MIN_PALM_SIZE_MM,
3443                            height: 0.15,
3444                        }),
3445                        pressure: None,
3446                    }
3447                ],
3448                pressed_buttons: hashset! {1},
3449            }; "palm contact"
3450        )]
3451        #[test_case(
3452            touch_binding::TouchpadEvent {
3453                injector_contacts: vec![
3454                    touch_binding::TouchContact{
3455                        id: 1,
3456                        position: Position { x: 0.0, y: 0.0 },
3457                        contact_size: Some(Size {
3458                            width: args::MIN_PALM_SIZE_MM,
3459                            height: 0.15,
3460                        }),
3461                        pressure: None,
3462                    },
3463                    touch_binding::TouchContact{
3464                        id: 2,
3465                        position: Position { x: 5.0, y: 5.0 },
3466                        contact_size: Some(Size {
3467                            width: args::MIN_PALM_SIZE_MM / 2.0,
3468                            height: 0.15,
3469                        }),
3470                        pressure: None,
3471                    },
3472                ],
3473                pressed_buttons: hashset! {1},
3474            }; "palm and finger contact"
3475        )]
3476        #[fuchsia::test(allow_stalls = false)]
3477        async fn skip_palm_detection_when_button_down(event: touch_binding::TouchpadEvent) {
3478            let contender = Box::new(StubContender::new());
3479            let contender_factory = ContenderFactoryOnceOrPanic::new(vec![contender.clone()]);
3480            let arena = Rc::new(GestureArena::new_for_test(
3481                Box::new(contender_factory),
3482                &fuchsia_inspect::Inspector::default(),
3483                1,
3484                metrics::MetricsLogger::default(),
3485            ));
3486            contender.set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
3487
3488            let count_of_contact = event.injector_contacts.len();
3489            let input_event = input_device::InputEvent {
3490                device_event: input_device::InputDeviceEvent::Touchpad(event),
3491                device_descriptor: make_touchpad_descriptor(vec![(
3492                    fidl_input_report::Unit { type_: UnitType::Meters, exponent: -3 },
3493                    fidl_input_report::Unit { type_: UnitType::Meters, exponent: -3 },
3494                )]),
3495                event_time: zx::MonotonicInstant::ZERO,
3496                trace_id: None,
3497                handled: input_device::Handled::No,
3498            };
3499
3500            arena.handle_input_event(input_event).await;
3501            assert_eq!(
3502                contender.get_last_touchpad_event().unwrap().contacts.len(),
3503                count_of_contact
3504            );
3505        }
3506
3507        #[test_case(
3508            vec![(
3509                fidl_input_report::Unit{ type_: UnitType::None, exponent: -6 },
3510                fidl_input_report::Unit{ type_: UnitType::None, exponent: -6 },
3511            )],
3512            vec![];
3513            "both units unspecified")]
3514        #[test_case(
3515            vec![(
3516                fidl_input_report::Unit{ type_: UnitType::None, exponent: -6 },
3517                fidl_input_report::Unit{ type_: UnitType::Meters, exponent: -6 },
3518            )],
3519            vec![];
3520            "x unit unspecified")]
3521        #[test_case(
3522            vec![(
3523                fidl_input_report::Unit{ type_: UnitType::Meters, exponent: -6 },
3524                fidl_input_report::Unit{ type_: UnitType::None, exponent: -6 },
3525            )],
3526            vec![];
3527            "y unit unspecified")]
3528        #[test_case(
3529            vec![(
3530                fidl_input_report::Unit{ type_: UnitType::Meters, exponent: -3 },
3531                fidl_input_report::Unit{ type_: UnitType::Meters, exponent: -6 },
3532            )],
3533            vec![];
3534            "mismatched exponents")]
3535        #[test_case(
3536            vec![
3537                (
3538                    fidl_input_report::Unit{ type_: UnitType::Meters, exponent: -3 },
3539                    fidl_input_report::Unit{ type_: UnitType::Meters, exponent: -3 },
3540                ),
3541                (
3542                    fidl_input_report::Unit{ type_: UnitType::Meters, exponent: -6 },
3543                    fidl_input_report::Unit{ type_: UnitType::Meters, exponent: -6 },
3544                ),
3545            ],
3546            vec![];
3547            "unequal divisors")]
3548        #[fuchsia::test(allow_stalls = false)]
3549        async fn skips_contender_on_bad_descriptor(
3550            contact_position_units: Vec<(fidl_input_report::Unit, fidl_input_report::Unit)>,
3551            positions: Vec<Position>,
3552        ) {
3553            let contender = Box::new(StubContender::new());
3554            let contender_factory = ContenderFactoryOnceOrPanic::new(vec![contender.clone()]);
3555            let arena = Rc::new(GestureArena::new_for_test(
3556                Box::new(contender_factory),
3557                &fuchsia_inspect::Inspector::default(),
3558                1,
3559                metrics::MetricsLogger::default(),
3560            ));
3561            contender.set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
3562            arena
3563                .handle_input_event(make_unhandled_touchpad_event_with_positions(
3564                    contact_position_units,
3565                    positions,
3566                ))
3567                .await;
3568            assert_eq!(contender.calls_received(), 0);
3569        }
3570    }
3571
3572    mod inspect {
3573        use super::super::{
3574            Contender, ContenderFactory, DetailedReasonFloat, DetailedReasonInt,
3575            DetailedReasonUint, EndGestureEvent, ExamineEventResult, GestureArena, InputHandler,
3576            MouseEvent, ProcessBufferedEventsResult, ProcessNewEventResult, Reason,
3577            RecognizedGesture, TouchpadEvent, args,
3578        };
3579        use super::utils::{
3580            ContenderFactoryOnceOrPanic, StubContender, StubMatchedContender, StubWinner,
3581            make_touchpad_descriptor, make_unhandled_keyboard_event, make_unhandled_mouse_event,
3582            make_unhandled_touchpad_event,
3583        };
3584        use crate::{
3585            Position, Size, input_device, keyboard_binding, metrics, mouse_binding, touch_binding,
3586        };
3587        use assert_matches::assert_matches;
3588        use fidl_fuchsia_input_report as fidl_input_report;
3589        use fuchsia_async as fasync;
3590        use maplit::hashset;
3591        use std::rc::Rc;
3592        use test_case::test_case;
3593
3594        struct EmptyContenderFactory {}
3595
3596        impl ContenderFactory for EmptyContenderFactory {
3597            fn make_contenders(&self) -> Vec<Box<dyn crate::gestures::gesture_arena::Contender>> {
3598                vec![]
3599            }
3600        }
3601
3602        #[fuchsia::test]
3603        async fn gesture_arena_initialized_with_inspect_node() {
3604            let inspector = fuchsia_inspect::Inspector::default();
3605            let fake_handlers_node = inspector.root().create_child("input_handlers_node");
3606            let _handler = GestureArena::new_internal(
3607                Box::new(EmptyContenderFactory {}),
3608                &inspector.root(),
3609                2,
3610                &fake_handlers_node,
3611                metrics::MetricsLogger::default(),
3612            );
3613            diagnostics_assertions::assert_data_tree!(inspector, root: {
3614                gestures_event_log: {},
3615                input_handlers_node: {
3616                    gesture_arena: {
3617                        events_received_count: 0u64,
3618                        events_handled_count: 0u64,
3619                        last_received_timestamp_ns: 0u64,
3620                        "fuchsia.inspect.Health": {
3621                            status: "STARTING_UP",
3622                            // Timestamp value is unpredictable and not relevant in this context,
3623                            // so we only assert that the property is present.
3624                            start_timestamp_nanos: diagnostics_assertions::AnyProperty
3625                        },
3626                    }
3627                }
3628            });
3629        }
3630
3631        #[fasync::run_singlethreaded(test)]
3632        async fn gesture_arena_inspect_counts_events() {
3633            let inspector = fuchsia_inspect::Inspector::default();
3634            let fake_handlers_node = inspector.root().create_child("input_handlers_node");
3635            let arena = Rc::new(GestureArena::new_internal(
3636                Box::new(EmptyContenderFactory {}),
3637                &inspector.root(),
3638                1,
3639                &fake_handlers_node,
3640                metrics::MetricsLogger::default(),
3641            ));
3642
3643            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
3644            arena.clone().handle_input_event(make_unhandled_mouse_event()).await;
3645            arena.clone().handle_input_event(make_unhandled_keyboard_event()).await;
3646            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
3647
3648            diagnostics_assertions::assert_data_tree!(inspector, root: contains {
3649                input_handlers_node: {
3650                    gesture_arena: {
3651                        events_received_count: 2u64,
3652                        events_handled_count: 0u64,
3653                        last_received_timestamp_ns: 0u64,
3654                        "fuchsia.inspect.Health": {
3655                            status: "STARTING_UP",
3656                            // Timestamp value is unpredictable and not relevant in this context,
3657                            // so we only assert that the property is present.
3658                            start_timestamp_nanos: diagnostics_assertions::AnyProperty
3659                        },
3660                    }
3661                }
3662            });
3663        }
3664
3665        #[fuchsia::test]
3666        fn logs_to_inspect() {
3667            let mut executor = fasync::TestExecutor::new_with_fake_time();
3668            let basic_mismatch_contender = Box::new(StubContender::new());
3669            let detailed_uint_mismatch_contender = Box::new(StubContender::new());
3670            let detailed_float_mismatch_contender = Box::new(StubContender::new());
3671            let detailed_int_mismatch_contender = Box::new(StubContender::new());
3672            let gesture_matching_contender = Box::new(StubContender::new());
3673            basic_mismatch_contender
3674                .set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
3675            detailed_uint_mismatch_contender.set_next_result(ExamineEventResult::Mismatch(
3676                Reason::DetailedUint(DetailedReasonUint {
3677                    criterion: "num_goats_teleported",
3678                    min: Some(10),
3679                    max: Some(30),
3680                    actual: 42,
3681                }),
3682            ));
3683            detailed_float_mismatch_contender.set_next_result(ExamineEventResult::Mismatch(
3684                Reason::DetailedFloat(DetailedReasonFloat {
3685                    criterion: "teleportation_distance_kilometers",
3686                    min: Some(10.125),
3687                    max: Some(30.5),
3688                    actual: 42.0,
3689                }),
3690            ));
3691            detailed_int_mismatch_contender.set_next_result(ExamineEventResult::Mismatch(
3692                Reason::DetailedInt(DetailedReasonInt {
3693                    criterion: "budget_surplus_trillions",
3694                    min: Some(-10),
3695                    max: Some(1),
3696                    actual: -42,
3697                }),
3698            ));
3699
3700            let inspector = fuchsia_inspect::Inspector::default();
3701            let contender_factory = Box::new(ContenderFactoryOnceOrPanic::new(vec![
3702                basic_mismatch_contender,
3703                detailed_uint_mismatch_contender,
3704                detailed_float_mismatch_contender,
3705                detailed_int_mismatch_contender,
3706                gesture_matching_contender.clone(),
3707            ]));
3708            let arena = Rc::new(GestureArena::new_for_test(
3709                contender_factory,
3710                &inspector,
3711                100,
3712                metrics::MetricsLogger::default(),
3713            ));
3714            let touchpad_descriptor = input_device::InputDeviceDescriptor::Touchpad(
3715                touch_binding::TouchpadDeviceDescriptor {
3716                    device_id: 1,
3717                    contacts: vec![touch_binding::ContactDeviceDescriptor {
3718                        x_range: fidl_input_report::Range { min: 0, max: 10_000 },
3719                        y_range: fidl_input_report::Range { min: 0, max: 10_000 },
3720                        x_unit: fidl_input_report::Unit {
3721                            // Use millimeters to avoid floating-point rounding.
3722                            type_: fidl_input_report::UnitType::Meters,
3723                            exponent: -3,
3724                        },
3725                        y_unit: fidl_input_report::Unit {
3726                            // Use millimeters to avoid floating-point rounding.
3727                            type_: fidl_input_report::UnitType::Meters,
3728                            exponent: -3,
3729                        },
3730                        pressure_range: None,
3731                        width_range: Some(fidl_input_report::Range { min: 0, max: 10_000 }),
3732                        height_range: Some(fidl_input_report::Range { min: 0, max: 10_000 }),
3733                    }],
3734                },
3735            );
3736            let keyboard_descriptor = input_device::InputDeviceDescriptor::Keyboard(
3737                keyboard_binding::KeyboardDeviceDescriptor {
3738                    device_id: 2,
3739                    device_information: fidl_fuchsia_input_report::DeviceInformation {
3740                        vendor_id: Some(0),
3741                        product_id: Some(0),
3742                        version: Some(0),
3743                        polling_rate: Some(0),
3744                        ..Default::default()
3745                    },
3746                    keys: vec![fidl_fuchsia_input::Key::A, fidl_fuchsia_input::Key::B],
3747                },
3748            );
3749
3750            // Process a touchpad event without width/height.
3751            let mut handle_event_fut = arena.clone().handle_input_event(input_device::InputEvent {
3752                device_event: input_device::InputDeviceEvent::Touchpad(
3753                    touch_binding::TouchpadEvent {
3754                        injector_contacts: vec![
3755                            touch_binding::TouchContact {
3756                                id: 1u32,
3757                                position: Position { x: 2.0, y: 3.0 },
3758                                contact_size: None,
3759                                pressure: None,
3760                            },
3761                            touch_binding::TouchContact {
3762                                id: 2u32,
3763                                position: Position { x: 40.0, y: 50.0 },
3764                                contact_size: None,
3765                                pressure: None,
3766                            },
3767                        ],
3768                        pressed_buttons: hashset! {1},
3769                    },
3770                ),
3771                device_descriptor: touchpad_descriptor.clone(),
3772                event_time: zx::MonotonicInstant::from_nanos(12_300),
3773                trace_id: None,
3774                handled: input_device::Handled::No,
3775            });
3776            executor.set_fake_time(fasync::MonotonicInstant::from_nanos(10_000_000));
3777            gesture_matching_contender
3778                .set_next_result(ExamineEventResult::Contender(gesture_matching_contender.clone()));
3779            assert_matches!(
3780                executor.run_until_stalled(&mut handle_event_fut),
3781                std::task::Poll::Ready(_)
3782            );
3783
3784            // Process a handled key event.
3785            let mut handle_event_fut = arena.clone().handle_input_event(input_device::InputEvent {
3786                device_event: input_device::InputDeviceEvent::Keyboard(
3787                    keyboard_binding::KeyboardEvent::new(
3788                        fidl_fuchsia_input::Key::A,
3789                        fidl_fuchsia_ui_input3::KeyEventType::Pressed,
3790                    ),
3791                ),
3792                device_descriptor: keyboard_descriptor.clone(),
3793                event_time: zx::MonotonicInstant::from_nanos(11_000_000),
3794                trace_id: None,
3795                handled: input_device::Handled::Yes,
3796            });
3797            executor.set_fake_time(fasync::MonotonicInstant::from_nanos(12_000_000));
3798            assert_matches!(
3799                executor.run_until_stalled(&mut handle_event_fut),
3800                std::task::Poll::Ready(_)
3801            );
3802
3803            // Process an unhandled key event.
3804            let mut handle_event_fut = arena.clone().handle_input_event(input_device::InputEvent {
3805                device_event: input_device::InputDeviceEvent::Keyboard(
3806                    keyboard_binding::KeyboardEvent::new(
3807                        fidl_fuchsia_input::Key::B,
3808                        fidl_fuchsia_ui_input3::KeyEventType::Pressed,
3809                    ),
3810                ),
3811                device_descriptor: keyboard_descriptor,
3812                event_time: zx::MonotonicInstant::from_nanos(13_000_000),
3813                trace_id: None,
3814                handled: input_device::Handled::No,
3815            });
3816            executor.set_fake_time(fasync::MonotonicInstant::from_nanos(14_000_000));
3817            assert_matches!(
3818                executor.run_until_stalled(&mut handle_event_fut),
3819                std::task::Poll::Ready(_)
3820            );
3821
3822            // Process a touchpad event with width/height, and end the contest with a match.
3823            let mut handle_event_fut = arena.clone().handle_input_event(input_device::InputEvent {
3824                device_event: input_device::InputDeviceEvent::Touchpad(
3825                    touch_binding::TouchpadEvent {
3826                        injector_contacts: vec![touch_binding::TouchContact {
3827                            id: 1u32,
3828                            position: Position { x: 2.0, y: 3.0 },
3829                            contact_size: Some(Size { width: 3.0, height: 4.0 }),
3830                            pressure: None,
3831                        }],
3832                        pressed_buttons: hashset! {},
3833                    },
3834                ),
3835                device_descriptor: touchpad_descriptor.clone(),
3836                event_time: zx::MonotonicInstant::from_nanos(18_000_000),
3837                trace_id: None,
3838                handled: input_device::Handled::No,
3839            });
3840            let matched_contender = Box::new(StubMatchedContender::new());
3841            matched_contender.set_next_process_buffered_events_result(
3842                ProcessBufferedEventsResult {
3843                    generated_events: vec![],
3844                    winner: None,
3845                    recognized_gesture: RecognizedGesture::Motion,
3846                },
3847            );
3848            gesture_matching_contender
3849                .set_next_result(ExamineEventResult::MatchedContender(matched_contender));
3850            executor.set_fake_time(fasync::MonotonicInstant::from_nanos(19_000_000));
3851            assert_matches!(
3852                executor.run_until_stalled(&mut handle_event_fut),
3853                std::task::Poll::Ready(_)
3854            );
3855
3856            // Uncomment this block to generate a new example for the
3857            // documentation found at the bottom of this file.
3858            /*
3859            {
3860                use fuchsia_inspect::hierarchy::testing::JsonGetter;
3861                println!("{}", inspector.get_pretty_json());
3862            }
3863            */
3864
3865            diagnostics_assertions::assert_data_tree!(@executor executor, inspector, root: {
3866                gestures_event_log: {
3867                    "0": {
3868                        touchpad_event: {
3869                            driver_monotonic_nanos: 12_300i64,
3870                            entry_latency_micros: 9987i64,  // 10_000_000 - 12_300 = 9_987_700 nsec
3871                            pressed_buttons: vec![ 1u64 ],
3872                            contacts: {
3873                                "1": {
3874                                    pos_x_mm: 2.0,
3875                                    pos_y_mm: 3.0,
3876                                },
3877                                "2": {
3878                                    pos_x_mm: 40.0,
3879                                    pos_y_mm: 50.0,
3880                                },
3881                            },
3882                            filtered_palm_contacts: {},
3883                        }
3884                    },
3885                    "1": {
3886                        mismatch_event: {
3887                            contender: "utils::StubContender",
3888                            reason: "some reason",
3889                        }
3890                    },
3891                    "2": {
3892                        mismatch_event: {
3893                            contender: "utils::StubContender",
3894                            criterion: "num_goats_teleported",
3895                            min_allowed: 10u64,
3896                            max_allowed: 30u64,
3897                            actual: 42u64,
3898                        }
3899                    },
3900                    "3": {
3901                        mismatch_event: {
3902                            contender: "utils::StubContender",
3903                            criterion: "teleportation_distance_kilometers",
3904                            min_allowed: 10.125,
3905                            max_allowed: 30.5,
3906                            actual: 42.0,
3907                        }
3908                    },
3909                    "4": {
3910                        mismatch_event: {
3911                            contender: "utils::StubContender",
3912                            criterion: "budget_surplus_trillions",
3913                            min_allowed: -10i64,
3914                            max_allowed: 1i64,
3915                            actual: -42i64,
3916                        }
3917                    },
3918                    "5": {
3919                        key_event: {
3920                            driver_monotonic_nanos: 11_000_000i64,
3921                            entry_latency_micros: 1_000i64,  // 12_000_000 - 11_000_000 = 1_000_00 nsec
3922                        }
3923                    },
3924                    "6": {
3925                        key_event: {
3926                            driver_monotonic_nanos: 13_000_000i64,
3927                            entry_latency_micros: 1_000i64,  // 14_000_000 - 13_000_000 = 1_000_00 nsec
3928                        }
3929                    },
3930                    "7": {
3931                        touchpad_event: {
3932                            driver_monotonic_nanos: 18_000_000i64,
3933                            entry_latency_micros: 1_000i64,  // 19_000_000 - 18_000_000 = 1_000_00 nsec
3934                            pressed_buttons: Vec::<u64>::new(),
3935                            contacts: {
3936                                "1": {
3937                                    pos_x_mm: 2.0,
3938                                    pos_y_mm: 3.0,
3939                                    width_mm: 3.0,
3940                                    height_mm: 4.0,
3941                                },
3942                            },
3943                            filtered_palm_contacts: {},
3944                        }
3945                    },
3946                    "8": {
3947                        gesture_start: {
3948                          gesture_name: "motion",
3949                          latency_event_count: 1u64,
3950                          latency_micros: 17_987i64,  // 18_000_000 - 12_300 = 17_987_700
3951                        }
3952                    },
3953                    "9": {
3954                        gesture_end: {
3955                          gesture_name: "motion",
3956                          contender: "utils::StubMatchedContender",
3957                          event_count: 0u64,
3958                          duration_micros: 0i64,
3959                          reason: "discrete-recognizer",
3960                        }
3961                    }
3962                }
3963            });
3964        }
3965
3966        #[fuchsia::test(allow_stalls = false)]
3967        async fn negative_matching_latency_is_logged_correctly() {
3968            let inspector = fuchsia_inspect::Inspector::default();
3969            let gesture_matching_contender = Box::new(StubContender::new());
3970            let contender_factory = Box::new(ContenderFactoryOnceOrPanic::new(vec![
3971                gesture_matching_contender.clone(),
3972            ]));
3973            let arena = Rc::new(GestureArena::new_for_test(
3974                contender_factory,
3975                &inspector,
3976                100,
3977                metrics::MetricsLogger::default(),
3978            ));
3979
3980            gesture_matching_contender
3981                .set_next_result(ExamineEventResult::Contender(gesture_matching_contender.clone()));
3982            arena
3983                .clone()
3984                .handle_input_event(input_device::InputEvent {
3985                    event_time: zx::MonotonicInstant::from_nanos(15_000),
3986                    ..make_unhandled_touchpad_event()
3987                })
3988                .await;
3989
3990            let matched_contender = Box::new(StubMatchedContender::new());
3991            matched_contender.set_next_process_buffered_events_result(
3992                ProcessBufferedEventsResult {
3993                    generated_events: vec![],
3994                    winner: None,
3995                    recognized_gesture: RecognizedGesture::Motion,
3996                },
3997            );
3998            gesture_matching_contender
3999                .set_next_result(ExamineEventResult::MatchedContender(matched_contender));
4000            arena
4001                .clone()
4002                .handle_input_event(input_device::InputEvent {
4003                    event_time: zx::MonotonicInstant::from_nanos(6_000),
4004                    ..make_unhandled_touchpad_event()
4005                })
4006                .await;
4007
4008            diagnostics_assertions::assert_data_tree!(inspector, root: {
4009                gestures_event_log: {
4010                    "0": contains {},
4011                    "1": contains {},
4012                    "2": {
4013                        gesture_start: {
4014                          gesture_name: diagnostics_assertions::AnyProperty,
4015                          latency_event_count: 1u64,
4016                          latency_micros: -9i64,
4017                        }
4018                    },
4019                    "3": {
4020                        gesture_end: contains {}
4021                    },
4022                }
4023            })
4024        }
4025
4026        struct ContenderFactoryOnceThenEmpty {
4027            contenders: std::cell::Cell<Vec<Box<dyn Contender>>>,
4028        }
4029
4030        impl ContenderFactory for ContenderFactoryOnceThenEmpty {
4031            fn make_contenders(&self) -> Vec<Box<dyn Contender>> {
4032                self.contenders.take()
4033            }
4034        }
4035
4036        #[test_case(EndGestureEvent::NoEvent; "end_gesture_no_event")]
4037        #[test_case(EndGestureEvent::UnconsumedEvent(TouchpadEvent {
4038            timestamp: zx::MonotonicInstant::ZERO,
4039            pressed_buttons: vec![],
4040            contacts: vec![],
4041            filtered_palm_contacts: vec![],
4042        }); "end_gesture_unconsumed_event")]
4043        #[test_case(EndGestureEvent::GeneratedEvent(MouseEvent {
4044            timestamp: zx::MonotonicInstant::ZERO,
4045            mouse_data: mouse_binding::MouseEvent {
4046                location: mouse_binding::MouseLocation::Relative(
4047                    mouse_binding::RelativeLocation {
4048                        millimeters: Position::zero(),
4049                    },
4050                ),
4051                wheel_delta_v: None,
4052                wheel_delta_h: None,
4053                phase: mouse_binding::MousePhase::Move,
4054                affected_buttons: hashset! {},
4055                pressed_buttons: hashset! {},
4056                is_precision_scroll: None,
4057                    wake_lease: None.into(),
4058
4059            },
4060        }); "end_gesture_generated_event")]
4061        #[fuchsia::test(allow_stalls = false)]
4062        async fn multi_event_gesture_is_logged_correctly(end_gesture_event: EndGestureEvent) {
4063            // Set up the arena, and send the first touchpad event.
4064            let inspector = fuchsia_inspect::Inspector::default();
4065            let matching_contender = Box::new(StubContender::new());
4066            let arena = Rc::new(GestureArena::new_for_test(
4067                // In the `UnconsumedEvent` case, the gesture arena will try to start
4068                // a new contest. Hence, `ContenderFactoryOnceOrPanic` isn't appropriate
4069                // here. Nor is `ContenderFactoryOnceOrWarn`, since that would generate
4070                // a spurious warning, making this test harder to debug.
4071                Box::new(ContenderFactoryOnceThenEmpty {
4072                    contenders: std::cell::Cell::new(vec![matching_contender.clone()]),
4073                }),
4074                &inspector,
4075                100,
4076                metrics::MetricsLogger::default(),
4077            ));
4078            matching_contender
4079                .set_next_result(ExamineEventResult::Contender(matching_contender.clone()));
4080            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
4081
4082            // Plumb things up for the next event to trigger the start of a multi-event gesture,
4083            // and send the next event.
4084            let matched_contender = Box::new(StubMatchedContender::new());
4085            let winner = Box::new(StubWinner::new());
4086            matching_contender
4087                .set_next_result(ExamineEventResult::MatchedContender(matched_contender.clone()));
4088            matched_contender.set_next_process_buffered_events_result(
4089                ProcessBufferedEventsResult {
4090                    generated_events: vec![],
4091                    winner: Some(winner.clone()),
4092                    recognized_gesture: RecognizedGesture::Motion,
4093                },
4094            );
4095            winner.set_next_result(ProcessNewEventResult::ContinueGesture(None, winner.clone()));
4096            arena
4097                .clone()
4098                .handle_input_event(input_device::InputEvent {
4099                    device_event: input_device::InputDeviceEvent::Touchpad(
4100                        touch_binding::TouchpadEvent {
4101                            injector_contacts: vec![],
4102                            pressed_buttons: hashset! {},
4103                        },
4104                    ),
4105                    device_descriptor: make_touchpad_descriptor(),
4106                    event_time: zx::MonotonicInstant::from_nanos(123_000),
4107                    trace_id: None,
4108                    handled: input_device::Handled::No,
4109                })
4110                .await;
4111
4112            // Send another event as part of the gesture.
4113            winner.set_next_result(ProcessNewEventResult::ContinueGesture(None, winner.clone()));
4114            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
4115
4116            // Plumb things to end the gesture on the next event, and send that event.
4117            winner.set_next_result(ProcessNewEventResult::EndGesture(
4118                end_gesture_event,
4119                Reason::DetailedUint(DetailedReasonUint {
4120                    criterion: "num_goats_teleported",
4121                    min: Some(10),
4122                    max: Some(30),
4123                    actual: 42,
4124                }),
4125            ));
4126            arena
4127                .clone()
4128                .handle_input_event(input_device::InputEvent {
4129                    device_event: input_device::InputDeviceEvent::Touchpad(
4130                        touch_binding::TouchpadEvent {
4131                            injector_contacts: vec![],
4132                            pressed_buttons: hashset! {},
4133                        },
4134                    ),
4135                    device_descriptor: make_touchpad_descriptor(),
4136                    event_time: zx::MonotonicInstant::from_nanos(456_000),
4137                    trace_id: None,
4138                    handled: input_device::Handled::No,
4139                })
4140                .await;
4141
4142            diagnostics_assertions::assert_data_tree!(inspector, root: {
4143                gestures_event_log: {
4144                    "0": { touchpad_event: contains {} },
4145                    "1": { touchpad_event: contains {} },
4146                    "2": { gesture_start: contains {} },
4147                    "3": { touchpad_event: contains {} },
4148                    "4": { touchpad_event: contains {} },
4149                    "5": {
4150                        gesture_end: contains {
4151                            gesture_name: "motion",
4152                            contender: "utils::StubWinner",
4153                            criterion: "num_goats_teleported",
4154                            min_allowed: 10u64,
4155                            max_allowed: 30u64,
4156                            actual: 42u64,
4157                            duration_micros: 333i64, // 456_000 - 123_000 = 333_000 nsec
4158                            event_count: 2u64,
4159                        },
4160                    },
4161                }
4162            })
4163        }
4164
4165        #[fuchsia::test(allow_stalls = false)]
4166        async fn retains_latest_events_up_to_cap() {
4167            let inspector = fuchsia_inspect::Inspector::default();
4168            let arena = Rc::new(GestureArena::new_for_test(
4169                Box::new(EmptyContenderFactory {}),
4170                &inspector,
4171                2,
4172                metrics::MetricsLogger::default(),
4173            ));
4174            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await; // 0
4175            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await; // 1
4176            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await; // 2
4177            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await; // 3
4178            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await; // 4
4179            diagnostics_assertions::assert_data_tree!(inspector, root: {
4180                gestures_event_log: {
4181                    "3": contains {},
4182                    "4": contains {},
4183                }
4184            })
4185        }
4186
4187        #[fuchsia::test]
4188        fn retains_palm_contacts() {
4189            let mut executor = fasync::TestExecutor::new_with_fake_time();
4190            let inspector = fuchsia_inspect::Inspector::default();
4191            let arena = Rc::new(GestureArena::new_for_test(
4192                Box::new(EmptyContenderFactory {}),
4193                &inspector,
4194                2,
4195                metrics::MetricsLogger::default(),
4196            ));
4197            let mut handle_event_fut = arena.clone().handle_input_event(input_device::InputEvent {
4198                device_event: input_device::InputDeviceEvent::Touchpad(
4199                    touch_binding::TouchpadEvent {
4200                        injector_contacts: vec![touch_binding::TouchContact {
4201                            id: 1u32,
4202                            position: Position { x: 2_000.0, y: 1_000.0 },
4203                            contact_size: Some(Size {
4204                                width: (args::MIN_PALM_SIZE_MM + 0.1) * 1_000.0,
4205                                height: 4_000.0,
4206                            }),
4207                            pressure: None,
4208                        }],
4209                        pressed_buttons: hashset! {},
4210                    },
4211                ),
4212                device_descriptor: make_touchpad_descriptor(),
4213                event_time: zx::MonotonicInstant::ZERO,
4214                trace_id: None,
4215                handled: input_device::Handled::No,
4216            });
4217            executor.set_fake_time(fasync::MonotonicInstant::from_nanos(1_000_000));
4218            assert_matches!(
4219                executor.run_until_stalled(&mut handle_event_fut),
4220                std::task::Poll::Ready(_)
4221            );
4222            diagnostics_assertions::assert_data_tree!(@executor executor, inspector, root: {
4223                gestures_event_log: {
4224                    "0": {
4225                        touchpad_event: {
4226                            driver_monotonic_nanos: 0i64,
4227                            entry_latency_micros: 1_000i64,
4228                            pressed_buttons: Vec::<u64>::new(),
4229                            contacts: {},
4230                            filtered_palm_contacts: {
4231                                "1": {
4232                                    pos_x_mm: 2.0,
4233                                    pos_y_mm: 1.0,
4234                                    width_mm: (args::MIN_PALM_SIZE_MM + 0.1) as f64,
4235                                    height_mm: 4.0,
4236                                },
4237                            },
4238                        },
4239                    },
4240                },
4241            });
4242        }
4243    }
4244}
4245
4246// Example JSON dump of inspect tree generated by `gesture_arena`, from a unit test:
4247//
4248// ```json
4249// {
4250//   "root": {
4251//     "gestures_event_log": {
4252//       "0": {
4253//         "touchpad_event": {
4254//           "driver_monotonic_nanos": 12300,
4255//           "entry_latency_micros": 9987,
4256//           "pressed_buttons": [
4257//             1
4258//           ],
4259//           "contacts": {
4260//             "1": {
4261//               "pos_x_mm": 2.0,
4262//               "pos_y_mm": 3.0
4263//             },
4264//             "2": {
4265//               "pos_x_mm": 40.0,
4266//               "pos_y_mm": 50.0
4267//             }
4268//           }
4269//         }
4270//       },
4271//       "1": {
4272//         "mismatch_event": {
4273//           "contender": "utils::StubContender",
4274//           "reason": "some reason"
4275//         }
4276//       },
4277//       "2": {
4278//         "mismatch_event": {
4279//           "actual": 42,
4280//           "contender": "utils::StubContender",
4281//           "criterion": "num_goats_teleported",
4282//           "max_allowed": 30,
4283//           "min_allowed": 10
4284//         }
4285//       },
4286//       "3": {
4287//         "mismatch_event": {
4288//           "actual": 42.0,
4289//           "contender": "utils::StubContender",
4290//           "criterion": "teleportation_distance_kilometers",
4291//           "max_allowed": 30.5,
4292//           "min_allowed": 10.125
4293//         }
4294//       },
4295//       "4": {
4296//         "mismatch_event": {
4297//           "actual": -42,
4298//           "contender": "utils::StubContender",
4299//           "criterion": "budget_surplus_trillions",
4300//           "max_allowed": 1,
4301//           "min_allowed": -10
4302//         }
4303//       },
4304//       "5": {
4305//         "key_event": {
4306//           "driver_monotonic_nanos": 11000000,
4307//           "entry_latency_micros": 1000
4308//         }
4309//       },
4310//       "6": {
4311//         "key_event": {
4312//           "driver_monotonic_nanos": 13000000,
4313//           "entry_latency_micros": 1000
4314//         }
4315//       },
4316//       "7": {
4317//         "touchpad_event": {
4318//           "driver_monotonic_nanos": 18000000,
4319//           "entry_latency_micros": 1000,
4320//           "pressed_buttons": [],
4321//           "contacts": {
4322//             "1": {
4323//               "height_mm": 4.0,
4324//               "pos_x_mm": 2.0,
4325//               "pos_y_mm": 3.0,
4326//               "width_mm": 3.0
4327//             }
4328//           }
4329//         }
4330//       },
4331//       "8": {
4332//         "gesture_start": {
4333//           "gesture_name": "click",
4334//           "latency_event_count": 1,
4335//           "latency_micros": 17987
4336//         }
4337//       },
4338//       "9": {
4339//         "gesture_end": {
4340//           "contender": "utils::StubMatchedContender",
4341//           "duration_micros": 0,
4342//           "event_count": 0,
4343//           "gesture_name": "click",
4344//           "reason": "discrete-recognizer"
4345//         }
4346//       }
4347//     }
4348//   }
4349// }
4350// ```
4351
4352// Example `iquery` excerpt from a live device:
4353//
4354// ```json5
4355// core/ui/scene_manager:
4356//   metadata:
4357//     filename = fuchsia.inspect.Tree
4358//     component_url = fuchsia-pkg://fuchsia.com/scene_manager#meta/scene_manager.cm
4359//     timestamp = 375999103371
4360//   payload:
4361//     root:
4362//       fuchsia.inspect.Stats:
4363//         allocated_blocks = 9998
4364//         current_size = 163840
4365//         deallocated_blocks = 0
4366//         failed_allocations = 0
4367//         maximum_size = 307200
4368//         total_dynamic_children = 1
4369//       input_pipeline:
4370//         gestures_event_log:
4371//           0:
4372//             key_event:
4373//               driver_monotonic_nanos = 297873226402
4374//               entry_latency_micros = 32908
4375//           1:
4376//             key_event:
4377//               driver_monotonic_nanos = 297955554861
4378//               entry_latency_micros = 1403
4379//           /* ...many entries omitted... */
4380//           150:
4381//             touchpad_event:
4382//               driver_monotonic_nanos = 361816423302
4383//               entry_latency_micros = 14432
4384//               pressed_buttons = []
4385//               contacts:
4386//                 0:
4387//                   height_mm = 2.5840000
4388//                   pos_x_mm = 26.528000
4389//                   pos_y_mm = 23.712999
4390//                   width_mm = 2.9530000
4391//           /* mismatches on u64 properties */
4392//           151:
4393//             mismatch_event:
4394//               actual = 0
4395//               contender = one_finger_drag::InitialContender
4396//               criterion = num_pressed_buttons
4397//               max_allowed = 1
4398//               min_allowed = 1
4399//           152:
4400//             mismatch_event:
4401//               actual = 1
4402//               contender = scroll::InitialContender
4403//               criterion = num_contacts
4404//               max_allowed = 2
4405//               min_allowed = 2
4406//           /* ... many entries omitted ... */
4407//           159:
4408//             touchpad_event:
4409//               driver_monotonic_nanos = 361871136901
4410//               entry_latency_micros = 4745
4411//               pressed_buttons = []
4412//               contacts:
4413//                 0:
4414//                   height_mm = 2.5840000
4415//                   pos_x_mm = 27.162001
4416//                   pos_y_mm = 24.061001
4417//                   width_mm = 2.9530000
4418//           /* mismatches on float properties */
4419//           160:
4420//             mismatch_event:
4421//               actual = 0.723230
4422//               contender = click::UnpressedContender
4423//               criterion = displacement_mm
4424//               max_allowed = 0.500000
4425//           161:
4426//             mismatch_event:
4427//               actual = 0.723230
4428//               contender = primary_tap::FingerContactContender
4429//               criterion = displacement_mm
4430//               max_allowed = 0.500000
4431//           162:
4432//             mismatch_event:
4433//               actual = 0.723230
4434//               contender = secondary_tap::OneFingerContactContender
4435//               criterion = displacement_mm
4436//               max_allowed = 0.500000
4437//           /* gesture start */
4438//           163:
4439//             gesture_start:
4440//               gesture_name = motion
4441//               latency_event_count = 7
4442//               latency_micros = 54713
4443//           /* ... many entries omitted ... */
4444//           295:
4445//             touchpad_event:
4446//               driver_monotonic_nanos = 362903529428
4447//               entry_latency_micros = 3603
4448//               pressed_buttons = []
4449//               contacts:
4450//           /* gesture end */
4451//           296:
4452//             gesture_end:
4453//               actual = 0
4454//               contender = motion::Winner
4455//               criterion = num_contacts
4456//               duration_micros = 1032392
4457//               event_count = 132
4458//               gesture_name = motion
4459//               max_allowed = 1
4460//               min_allowed = 1
4461//           /* ... many entries omitted ... */
4462//           596:
4463//             touchpad_event:
4464//               driver_monotonic_nanos = 370902306135
4465//               entry_latency_micros = 4630
4466//               pressed_buttons = []
4467//               contacts:
4468//                 0:
4469//                   height_mm = 2.5840000
4470//                   pos_x_mm = 76.887001
4471//                   pos_y_mm = 25.962999
4472//                   width_mm = 2.9530000
4473//           /* ... many entries omitted ... */
4474//           752:
4475//             touchpad_event:
4476//               driver_monotonic_nanos = 372106779670
4477//               entry_latency_micros = 4607
4478//               pressed_buttons = []
4479//               contacts:
4480//                 0:
4481//                   height_mm = 3.2300000
4482//                   pos_x_mm = 76.949997
4483//                   pos_y_mm = 26.184999
4484//                   width_mm = 2.9530000
4485//           /* mismatches on i64 properties */
4486//           753:
4487//             mismatch_event:
4488//               actual = 1204473
4489//               contender = primary_tap::FingerContactContender
4490//               criterion = elapsed_time_micros
4491//               max_allowed = 1200000
4492//           754:
4493//             mismatch_event:
4494//               actual = 1204473
4495//               contender = secondary_tap::OneFingerContactContender
4496//               criterion = elapsed_time_micros
4497//               max_allowed = 1200000
4498// ```