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