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        fuchsia_trace::duration!("input", "gesture_arena");
1029        match input_event {
1030            input_device::InputEvent {
1031                device_event: input_device::InputDeviceEvent::Touchpad(ref touchpad_event),
1032                ref event_time,
1033                device_descriptor:
1034                    input_device::InputDeviceDescriptor::Touchpad(ref touchpad_descriptor),
1035                handled: input_device::Handled::No,
1036                ..
1037            } => {
1038                fuchsia_trace::duration!("input", "gesture_arena[processing]");
1039                self.inspect_status.count_received_event(&event_time);
1040                match self.handle_touchpad_event(event_time, touchpad_event, touchpad_descriptor) {
1041                    Ok(r) => r,
1042                    Err(e) => {
1043                        log::error!("{}", e);
1044                        vec![input_event]
1045                    }
1046                }
1047            }
1048            input_device::InputEvent {
1049                device_event: input_device::InputDeviceEvent::Keyboard(_),
1050                event_time,
1051                ..
1052            } => {
1053                fuchsia_trace::duration!("input", "gesture_arena[processing]");
1054                self.inspect_log.borrow_mut().add_entry(|node| {
1055                    log_keyboard_event_timestamp(node, event_time);
1056                });
1057                vec![input_event]
1058            }
1059            _ => {
1060                // TODO: b/478249522 - add cobalt logging
1061                log::warn!("Unhandled input event: {:?}", input_event.get_event_type());
1062                vec![input_event]
1063            }
1064        }
1065    }
1066
1067    fn set_handler_healthy(self: std::rc::Rc<Self>) {
1068        self.inspect_status.health_node.borrow_mut().set_ok();
1069    }
1070
1071    fn set_handler_unhealthy(self: std::rc::Rc<Self>, msg: &str) {
1072        self.inspect_status.health_node.borrow_mut().set_unhealthy(msg);
1073    }
1074
1075    fn get_name(&self) -> &'static str {
1076        "GestureArena"
1077    }
1078
1079    fn interest(&self) -> Vec<input_device::InputEventType> {
1080        vec![input_device::InputEventType::Touchpad, input_device::InputEventType::Keyboard]
1081    }
1082}
1083
1084/// Returns the multiplier to translate position data for the device described by
1085/// `device_descriptor`, from the units in the corresponding `TouchpadEvent`, to
1086/// millimeters.
1087///
1088/// For example, if this function returns 1000, then the original data are in
1089/// micrometers, and dividing by 1000 will yield millimeters.
1090fn get_position_divisor_to_mm(
1091    touchpad_descriptor: &touch_binding::TouchpadDeviceDescriptor,
1092) -> Result<f32, Error> {
1093    const EXPONENT_MILLIS: i32 = -3;
1094    let divisors =
1095        touchpad_descriptor.contacts.iter().enumerate().map(|(i, contact_descriptor)| {
1096            match (contact_descriptor.x_unit, contact_descriptor.y_unit) {
1097                (
1098                    fidl_input_report::Unit {
1099                        type_: fidl_input_report::UnitType::Meters,
1100                        exponent: exponent_x,
1101                    },
1102                    fidl_input_report::Unit {
1103                        type_: fidl_input_report::UnitType::Meters,
1104                        exponent: exponent_y,
1105                    },
1106                ) => {
1107                    if exponent_x == exponent_y {
1108                        Ok(f32::powi(10.0, EXPONENT_MILLIS - exponent_x))
1109                    } else {
1110                        Err(format!(
1111                            "contact {}: mismatched exponents x={}, y={}",
1112                            i, exponent_x, exponent_y
1113                        ))
1114                    }
1115                }
1116                (
1117                    fidl_input_report::Unit { type_: x_unit_type, .. },
1118                    fidl_input_report::Unit { type_: fidl_input_report::UnitType::Meters, .. },
1119                ) => Err(format!(
1120                    "contact {}: expected x-unit-type of Meters but got {:?}",
1121                    i, x_unit_type
1122                )),
1123                (
1124                    fidl_input_report::Unit { type_: fidl_input_report::UnitType::Meters, .. },
1125                    fidl_input_report::Unit { type_: y_unit_type, .. },
1126                ) => Err(format!(
1127                    "contact {}: expected y-unit-type of Meters but got {:?}",
1128                    i, y_unit_type
1129                )),
1130                (
1131                    fidl_input_report::Unit { type_: x_unit_type, .. },
1132                    fidl_input_report::Unit { type_: y_unit_type, .. },
1133                ) => Err(format!(
1134                    "contact {}: expected x and y unit-types of Meters but got x={:?} and y={:?}",
1135                    i, x_unit_type, y_unit_type
1136                )),
1137            }
1138        });
1139
1140    let (divisors, errors): (Vec<_>, Vec<_>) =
1141        divisors.fold((vec![], vec![]), |(mut divisors, mut errors), divisor| {
1142            match divisor {
1143                Ok(d) => divisors.push(d),
1144                Err(e) => errors.push(e),
1145            };
1146            (divisors, errors)
1147        });
1148
1149    if !errors.is_empty() {
1150        return Err(format_err!(
1151            errors.into_iter().fold(String::new(), |prev_err_msgs, this_err_msg| prev_err_msgs
1152                + &this_err_msg
1153                + ", ")
1154        ));
1155    }
1156
1157    let first_divisor = match divisors.first() {
1158        Some(&divisor) => divisor,
1159        None => return Err(format_err!("no contact descriptors!")),
1160    };
1161
1162    if divisors.iter().any(|&divisor| divisor != first_divisor) {
1163        return Err(format_err!(
1164            divisors
1165                .into_iter()
1166                .enumerate()
1167                .filter(|(_i, divisor)| *divisor != first_divisor)
1168                .map(|(i, divisor)| format!(
1169                    "contact {} has a different divisor than the first contact ({:?} != {:?})",
1170                    i, divisor, first_divisor,
1171                ))
1172                .fold(String::new(), |prev_err_msgs, this_err_msg| prev_err_msgs
1173                    + &this_err_msg
1174                    + ", ")
1175        ));
1176    }
1177
1178    Ok(first_divisor)
1179}
1180
1181fn log_keyboard_event_timestamp(
1182    log_entry_node: &InspectNode,
1183    driver_timestamp: zx::MonotonicInstant,
1184) {
1185    log_entry_node.record_child("key_event", |key_event_node| {
1186        log_common(key_event_node, driver_timestamp);
1187    });
1188}
1189
1190fn log_basic_reason(reason_node: &InspectNode, text: &'static str) {
1191    reason_node.record_string("reason", text);
1192}
1193
1194fn log_detailed_reason_uint(reason_node: &InspectNode, reason_details: DetailedReasonUint) {
1195    reason_node.record_string("criterion", reason_details.criterion);
1196    reason_node.record_uint("actual", u64::try_from(reason_details.actual).unwrap_or(u64::MAX));
1197    reason_details.min.map(|min| reason_node.record_uint("min_allowed", min));
1198    reason_details.max.map(|max| reason_node.record_uint("max_allowed", max));
1199}
1200
1201fn log_detailed_reason_float(reason_node: &InspectNode, reason_details: DetailedReasonFloat) {
1202    reason_node.record_string("criterion", reason_details.criterion);
1203    reason_node.record_double("actual", f64::from(reason_details.actual));
1204    reason_details.min.map(|min| reason_node.record_double("min_allowed", f64::from(min)));
1205    reason_details.max.map(|max| reason_node.record_double("max_allowed", f64::from(max)));
1206}
1207
1208fn log_detailed_reason_int(reason_node: &InspectNode, reason_details: DetailedReasonInt) {
1209    reason_node.record_string("criterion", reason_details.criterion);
1210    reason_node.record_int("actual", reason_details.actual);
1211    reason_details.min.map(|min| reason_node.record_int("min_allowed", min));
1212    reason_details.max.map(|max| reason_node.record_int("max_allowed", max));
1213}
1214
1215fn log_reason(reason_node: &InspectNode, contender_name: &'static str, reason: Reason) {
1216    // E.g. "input_pipeline_lib_test::gestures::gesture_arena::tests::utils::StubContender"
1217    // -> "utils::StubContender".
1218    let contender_name = match contender_name.rmatch_indices("::").nth(1) {
1219        Some((i, _matched_substr)) => &contender_name[i + 2..],
1220        None => contender_name,
1221    };
1222    reason_node.record_string("contender", contender_name);
1223    match reason {
1224        Reason::Basic(text) => log_basic_reason(reason_node, text),
1225        Reason::DetailedUint(mismatch_details) => {
1226            log_detailed_reason_uint(reason_node, mismatch_details)
1227        }
1228        Reason::DetailedFloat(mismatch_details) => {
1229            log_detailed_reason_float(reason_node, mismatch_details)
1230        }
1231        Reason::DetailedInt(mismatch_details) => {
1232            log_detailed_reason_int(reason_node, mismatch_details)
1233        }
1234    }
1235}
1236
1237fn log_mismatch_reason(log_entry_node: &InspectNode, contender_name: &'static str, reason: Reason) {
1238    log::debug!("touchpad: {} mismatched: {:?}", contender_name, reason);
1239    log_entry_node.record_child("mismatch_event", |mismatch_event_node| {
1240        log_reason(mismatch_event_node, contender_name, reason);
1241    });
1242}
1243
1244fn log_gesture_start(
1245    log_entry_node: &InspectNode,
1246    recognized_gesture: RecognizedGesture,
1247    num_previous_events: usize,
1248    elapsed_from_first_event: zx::MonotonicDuration,
1249) {
1250    log::debug!("touchpad: recognized start {:?}", recognized_gesture);
1251    log_entry_node.record_child("gesture_start", |gesture_start_node| {
1252        gesture_start_node.record_string("gesture_name", recognized_gesture.to_str());
1253        gesture_start_node.record_int(
1254            "latency_micros",
1255            // Reduce precision, to minimize space.
1256            elapsed_from_first_event.into_micros(),
1257        );
1258        gesture_start_node.record_uint(
1259            "latency_event_count",
1260            u64::try_from(num_previous_events).unwrap_or(u64::MAX),
1261        );
1262    });
1263}
1264
1265fn log_gesture_end(
1266    log_entry_node: &InspectNode,
1267    contender_name: &'static str,
1268    current_gesture: RecognizedGesture,
1269    reason: Reason,
1270    num_events: usize,
1271    elapsed_from_gesture_start: zx::MonotonicDuration,
1272) {
1273    log::debug!("touchpad: recognized end {:?}", current_gesture);
1274    log_entry_node.record_child("gesture_end", |gesture_end_node| {
1275        gesture_end_node.record_string("gesture_name", current_gesture.to_str());
1276        gesture_end_node.record_int(
1277            "duration_micros",
1278            // Reduce precision, to minimize space.
1279            elapsed_from_gesture_start.into_micros(),
1280        );
1281        gesture_end_node.record_uint("event_count", u64::try_from(num_events).unwrap_or(u64::MAX));
1282        log_reason(gesture_end_node, contender_name, reason)
1283    });
1284}
1285
1286#[cfg(test)]
1287mod tests {
1288    mod utils {
1289        use super::super::{
1290            COUNTS_PER_MM, Contender, ContenderFactory, ExamineEventResult, MatchedContender,
1291            PRIMARY_BUTTON, ProcessBufferedEventsResult, ProcessNewEventResult, TouchpadEvent,
1292            VerifyEventResult, Winner, args,
1293        };
1294        use crate::utils::Size;
1295        use crate::{Position, input_device, keyboard_binding, mouse_binding, touch_binding};
1296        use assert_matches::assert_matches;
1297        use fidl_fuchsia_input_report as fidl_input_report;
1298        use maplit::hashset;
1299        use std::cell::{Cell, RefCell};
1300        use std::rc::Rc;
1301
1302        /// The gesture arena is mostly agnostic to the event details. Consequently, most
1303        /// tests can use the same lightly populated touchpad event.
1304        pub(super) fn make_unhandled_touchpad_event() -> input_device::InputEvent {
1305            input_device::InputEvent {
1306                device_event: input_device::InputDeviceEvent::Touchpad(
1307                    touch_binding::TouchpadEvent {
1308                        injector_contacts: vec![],
1309                        pressed_buttons: hashset! {},
1310                    },
1311                ),
1312                device_descriptor: make_touchpad_descriptor(),
1313                event_time: zx::MonotonicInstant::ZERO,
1314                trace_id: None,
1315                handled: input_device::Handled::No,
1316            }
1317        }
1318
1319        /// The gesture arena is mostly agnostic to the event details. Consequently, most
1320        /// tests can use the same lightly populated mouse event.
1321        pub(super) fn make_unhandled_mouse_event() -> input_device::InputEvent {
1322            input_device::InputEvent {
1323                device_event: input_device::InputDeviceEvent::Mouse(mouse_binding::MouseEvent {
1324                    location: mouse_binding::MouseLocation::Relative(
1325                        mouse_binding::RelativeLocation { millimeters: Position::zero() },
1326                    ),
1327                    wheel_delta_h: None,
1328                    wheel_delta_v: None,
1329                    phase: mouse_binding::MousePhase::Move,
1330                    affected_buttons: hashset! {},
1331                    pressed_buttons: hashset! {},
1332                    is_precision_scroll: None,
1333                    wake_lease: None.into(),
1334                }),
1335                device_descriptor: make_mouse_descriptor(),
1336                event_time: zx::MonotonicInstant::ZERO,
1337                trace_id: None,
1338                handled: input_device::Handled::No,
1339            }
1340        }
1341
1342        /// The gesture arena is mostly agnostic to the event details. Consequently, most
1343        /// tests can use the same lightly populated keyboard event.
1344        pub(super) fn make_unhandled_keyboard_event() -> input_device::InputEvent {
1345            input_device::InputEvent {
1346                device_event: input_device::InputDeviceEvent::Keyboard(
1347                    keyboard_binding::KeyboardEvent::new(
1348                        fidl_fuchsia_input::Key::A,
1349                        fidl_fuchsia_ui_input3::KeyEventType::Pressed,
1350                    ),
1351                ),
1352                device_descriptor: make_keyboard_descriptor(),
1353                event_time: zx::MonotonicInstant::ZERO,
1354                trace_id: None,
1355                handled: input_device::Handled::No,
1356            }
1357        }
1358
1359        pub(super) fn make_touchpad_descriptor() -> input_device::InputDeviceDescriptor {
1360            input_device::InputDeviceDescriptor::Touchpad(touch_binding::TouchpadDeviceDescriptor {
1361                device_id: 1,
1362                contacts: vec![touch_binding::ContactDeviceDescriptor {
1363                    x_range: fidl_input_report::Range { min: 0, max: 10_000 },
1364                    y_range: fidl_input_report::Range { min: 0, max: 10_000 },
1365                    x_unit: fidl_input_report::Unit {
1366                        type_: fidl_input_report::UnitType::Meters,
1367                        exponent: -6,
1368                    },
1369                    y_unit: fidl_input_report::Unit {
1370                        type_: fidl_input_report::UnitType::Meters,
1371                        exponent: -6,
1372                    },
1373                    pressure_range: None,
1374                    width_range: Some(fidl_input_report::Range { min: 0, max: 10_000 }),
1375                    height_range: Some(fidl_input_report::Range { min: 0, max: 10_000 }),
1376                }],
1377            })
1378        }
1379
1380        pub(super) fn make_mouse_descriptor() -> input_device::InputDeviceDescriptor {
1381            input_device::InputDeviceDescriptor::Mouse(mouse_binding::MouseDeviceDescriptor {
1382                device_id: 2,
1383                absolute_x_range: Some(fidl_input_report::Range { min: -127, max: 127 }),
1384                absolute_y_range: Some(fidl_input_report::Range { min: -127, max: 127 }),
1385                wheel_v_range: None,
1386                wheel_h_range: None,
1387                buttons: Some(vec![PRIMARY_BUTTON]),
1388                counts_per_mm: COUNTS_PER_MM,
1389            })
1390        }
1391
1392        fn make_keyboard_descriptor() -> input_device::InputDeviceDescriptor {
1393            input_device::InputDeviceDescriptor::Keyboard(
1394                keyboard_binding::KeyboardDeviceDescriptor {
1395                    device_id: 3,
1396                    device_information: fidl_fuchsia_input_report::DeviceInformation {
1397                        vendor_id: Some(0),
1398                        product_id: Some(0),
1399                        version: Some(0),
1400                        polling_rate: Some(0),
1401                        ..Default::default()
1402                    },
1403                    keys: vec![fidl_fuchsia_input::Key::A],
1404                },
1405            )
1406        }
1407
1408        #[derive(Clone, Debug)]
1409        /// Provides the ability to
1410        ///
1411        /// 1. Plumb a fake `Contender` into a `GestureArena`
1412        /// 2. Fake interactions between the two
1413        /// 3. Inspect interactions between the two
1414        ///
1415        /// To plumb the fake, pass a `Clone` of the `StubContender` to
1416        /// `ContenderFactoryOnceOrPanic`. To fake or inspect interactions, call the
1417        /// inherent methods on the struct.
1418        pub(super) struct StubContender {
1419            inner: Rc<RefCell<StubContenderInner>>,
1420            start_from_idle: bool,
1421        }
1422
1423        impl StubContender {
1424            pub(super) fn new() -> Self {
1425                Self {
1426                    inner: Rc::new(RefCell::new(StubContenderInner {
1427                        next_result: None,
1428                        calls_received: 0,
1429                        last_touchpad_event: None,
1430                    })),
1431                    start_from_idle: false,
1432                }
1433            }
1434
1435            pub(super) fn new_start_from_idle() -> Self {
1436                Self {
1437                    inner: Rc::new(RefCell::new(StubContenderInner {
1438                        next_result: None,
1439                        calls_received: 0,
1440                        last_touchpad_event: None,
1441                    })),
1442                    start_from_idle: true,
1443                }
1444            }
1445
1446            /// Set the value to be returned on the next call to `examine_event()`.
1447            /// Aborts if a value is already set, since that suggests that a previously
1448            /// expected call was never made.
1449            pub(super) fn set_next_result(&self, next_result: ExamineEventResult) {
1450                self.assert_next_result_is_none();
1451                self.inner.borrow_mut().next_result = Some(next_result);
1452            }
1453
1454            pub(super) fn assert_next_result_is_none(&self) {
1455                assert_matches!(self.inner.borrow().next_result, None);
1456            }
1457
1458            pub(super) fn calls_received(&self) -> usize {
1459                self.inner.borrow().calls_received
1460            }
1461
1462            pub(super) fn ref_count(&self) -> usize {
1463                Rc::strong_count(&self.inner)
1464            }
1465
1466            pub(super) fn get_last_touchpad_event(&self) -> Option<TouchpadEvent> {
1467                self.inner.borrow_mut().last_touchpad_event.take()
1468            }
1469        }
1470
1471        #[derive(Debug)]
1472        struct StubContenderInner {
1473            next_result: Option<ExamineEventResult>,
1474            calls_received: usize,
1475            last_touchpad_event: Option<TouchpadEvent>,
1476        }
1477
1478        /// A factory that returns `Vec<Box<dyn Contender>>` from `contenders` on the
1479        /// first call, and `panic()`-s on the second call.
1480        ///
1481        /// Useful because
1482        /// a) `GestureArena` requires that the factory be invocable multiple times, BUT
1483        /// b) most of the gesture arena tests don't expect that to happen, SO
1484        /// c) the tests don't have logic to handle that case.
1485        ///
1486        /// To use: pass a lambda which invokes `make_contenders()` to
1487        /// `GestureArena::new_for_test()`.
1488        pub(super) struct ContenderFactoryOnceOrPanic {
1489            contenders: Cell<Option<Vec<Box<dyn Contender>>>>,
1490        }
1491
1492        impl ContenderFactoryOnceOrPanic {
1493            pub(super) fn new(contenders: Vec<Box<dyn Contender>>) -> Self {
1494                Self { contenders: Cell::new(Some(contenders)) }
1495            }
1496
1497            /// `ContenderFactoryOnceOrPanic::new_panic_when_call` will panic when calls
1498            /// `make_contenders`.
1499            pub(super) fn new_panic_when_call() -> Self {
1500                Self { contenders: Cell::new(None) }
1501            }
1502        }
1503
1504        impl ContenderFactory for ContenderFactoryOnceOrPanic {
1505            fn make_contenders(&self) -> Vec<Box<dyn Contender>> {
1506                self.contenders
1507                    .take()
1508                    .expect("`contenders` has been consumed")
1509                    .into_iter()
1510                    .collect()
1511            }
1512        }
1513
1514        impl Contender for StubContender {
1515            fn examine_event(self: Box<Self>, event: &TouchpadEvent) -> ExamineEventResult {
1516                let mut inner = self.inner.borrow_mut();
1517                inner.calls_received += 1;
1518                inner.last_touchpad_event = Some(event.clone());
1519                inner.next_result.take().unwrap_or_else(|| {
1520                    panic!("missing `next_result` on call {}", inner.calls_received)
1521                })
1522            }
1523
1524            fn start_from_idle(&self) -> bool {
1525                self.start_from_idle
1526            }
1527        }
1528
1529        #[derive(Clone)]
1530        pub(super) struct ContenderFactoryCalled {
1531            pub called: Rc<RefCell<bool>>,
1532        }
1533
1534        impl ContenderFactoryCalled {
1535            pub(super) fn new() -> Self {
1536                Self { called: Rc::new(RefCell::new(false)) }
1537            }
1538
1539            pub(super) fn was_called(&self) -> bool {
1540                *self.called.borrow()
1541            }
1542        }
1543
1544        impl ContenderFactory for ContenderFactoryCalled {
1545            fn make_contenders(&self) -> Vec<Box<dyn Contender>> {
1546                self.called.replace(true);
1547                vec![]
1548            }
1549        }
1550
1551        // A fake contender that always returns itself. Useful for keeping the
1552        // gesture arena from exiting the matching state. Keeping the arena in
1553        // the matching state eliminates the need for some tests to provide
1554        // return values for `process_buffered_events()`.
1555        #[derive(Debug)]
1556        pub(super) struct ContenderForever {}
1557
1558        impl Contender for ContenderForever {
1559            fn examine_event(self: Box<Self>, _event: &TouchpadEvent) -> ExamineEventResult {
1560                ExamineEventResult::Contender(self)
1561            }
1562        }
1563
1564        #[derive(Clone, Debug)]
1565        pub(super) struct StubMatchedContender {
1566            inner: Rc<RefCell<StubMatchedContenderInner>>,
1567        }
1568
1569        impl StubMatchedContender {
1570            pub(super) fn new() -> Self {
1571                Self {
1572                    inner: Rc::new(RefCell::new(StubMatchedContenderInner {
1573                        next_verify_event_result: None,
1574                        next_process_buffered_events_result: None,
1575                        verify_event_calls_received: 0,
1576                        process_buffered_events_calls_received: 0,
1577                        last_process_buffered_events_args: None,
1578                    })),
1579                }
1580            }
1581
1582            /// Set the value to be returned on the next call to `verify_event()`.
1583            /// Aborts if a value is already set, since that suggests that a previously
1584            /// expected call was never made.
1585            pub(super) fn set_next_verify_event_result(&self, next_result: VerifyEventResult) {
1586                self.assert_next_verify_event_result_is_none();
1587                self.inner.borrow_mut().next_verify_event_result = Some(next_result);
1588            }
1589
1590            fn assert_next_verify_event_result_is_none(&self) {
1591                assert_matches!(self.inner.borrow().next_verify_event_result, None);
1592            }
1593
1594            pub(super) fn verify_event_calls_received(&self) -> usize {
1595                self.inner.borrow().verify_event_calls_received
1596            }
1597
1598            /// Set the value to be returned on the next call to `process_buffered_events()`.
1599            /// Aborts if a value is already set, since that suggests that a previously
1600            /// expected call was never made.
1601            pub(super) fn set_next_process_buffered_events_result(
1602                &self,
1603                next_result: ProcessBufferedEventsResult,
1604            ) {
1605                self.assert_next_process_buffered_events_result_is_none();
1606                self.inner.borrow_mut().next_process_buffered_events_result = Some(next_result);
1607            }
1608
1609            pub(super) fn get_last_processed_buffered_events_args(
1610                &self,
1611            ) -> Option<Vec<TouchpadEvent>> {
1612                self.inner.borrow_mut().last_process_buffered_events_args.take()
1613            }
1614
1615            fn assert_next_process_buffered_events_result_is_none(&self) {
1616                assert_matches!(self.inner.borrow().next_process_buffered_events_result, None);
1617            }
1618
1619            pub(super) fn ref_count(&self) -> usize {
1620                Rc::strong_count(&self.inner)
1621            }
1622        }
1623
1624        #[derive(Debug)]
1625        struct StubMatchedContenderInner {
1626            next_verify_event_result: Option<VerifyEventResult>,
1627            next_process_buffered_events_result: Option<ProcessBufferedEventsResult>,
1628            verify_event_calls_received: usize,
1629            process_buffered_events_calls_received: usize,
1630            last_process_buffered_events_args: Option<Vec<TouchpadEvent>>,
1631        }
1632
1633        impl MatchedContender for StubMatchedContender {
1634            fn verify_event(self: Box<Self>, _event: &TouchpadEvent) -> VerifyEventResult {
1635                let mut inner = self.inner.borrow_mut();
1636                inner.verify_event_calls_received += 1;
1637                inner.next_verify_event_result.take().unwrap_or_else(|| {
1638                    panic!(
1639                        "missing `next_verify_event_result` on call {}",
1640                        inner.verify_event_calls_received
1641                    )
1642                })
1643            }
1644
1645            fn process_buffered_events(
1646                self: Box<Self>,
1647                events: Vec<TouchpadEvent>,
1648            ) -> ProcessBufferedEventsResult {
1649                let mut inner = self.inner.borrow_mut();
1650                inner.last_process_buffered_events_args = Some(events);
1651                inner.process_buffered_events_calls_received += 1;
1652                inner.next_process_buffered_events_result.take().unwrap_or_else(|| {
1653                    panic!(
1654                        "missing `next_process_buffered_events_result` on call {}",
1655                        inner.process_buffered_events_calls_received
1656                    )
1657                })
1658            }
1659        }
1660
1661        #[derive(Clone, Debug)]
1662        pub(super) struct StubWinner {
1663            inner: Rc<RefCell<StubWinnerInner>>,
1664        }
1665
1666        impl StubWinner {
1667            pub(super) fn new() -> Self {
1668                Self {
1669                    inner: Rc::new(RefCell::new(StubWinnerInner {
1670                        next_result: None,
1671                        calls_received: 0,
1672                    })),
1673                }
1674            }
1675
1676            /// Set the value to be returned on the next call to `examine_event()`.
1677            pub(super) fn set_next_result(&self, next_result: ProcessNewEventResult) {
1678                self.inner.borrow_mut().next_result = Some(next_result);
1679            }
1680
1681            pub(super) fn calls_received(&self) -> usize {
1682                self.inner.borrow().calls_received
1683            }
1684        }
1685
1686        #[derive(Debug)]
1687        struct StubWinnerInner {
1688            next_result: Option<ProcessNewEventResult>,
1689            calls_received: usize,
1690        }
1691
1692        impl Winner for StubWinner {
1693            fn process_new_event(self: Box<Self>, _event: TouchpadEvent) -> ProcessNewEventResult {
1694                let mut inner = self.inner.borrow_mut();
1695                inner.calls_received += 1;
1696                inner.next_result.take().unwrap_or_else(|| {
1697                    panic!("missing `next_result` on call {}", inner.calls_received)
1698                })
1699            }
1700        }
1701
1702        impl From<StubContender> for Box<dyn Contender> {
1703            fn from(stub_contender: StubContender) -> Box<dyn Contender> {
1704                Box::new(stub_contender)
1705            }
1706        }
1707
1708        impl From<ContenderForever> for Box<dyn Contender> {
1709            fn from(contender_forever: ContenderForever) -> Box<dyn Contender> {
1710                Box::new(contender_forever)
1711            }
1712        }
1713
1714        impl From<StubMatchedContender> for Box<dyn MatchedContender> {
1715            fn from(stub_matched_contender: StubMatchedContender) -> Box<dyn MatchedContender> {
1716                Box::new(stub_matched_contender)
1717            }
1718        }
1719
1720        impl From<StubWinner> for Box<dyn Winner> {
1721            fn from(stub_winner: StubWinner) -> Box<dyn Winner> {
1722                Box::new(stub_winner)
1723            }
1724        }
1725
1726        pub(super) const TOUCH_CONTACT_INDEX_FINGER: touch_binding::TouchContact =
1727            touch_binding::TouchContact {
1728                id: 0,
1729                position: Position { x: 0.0, y: 0.0 },
1730                pressure: None,
1731                contact_size: Some(Size {
1732                    width: args::MIN_PALM_SIZE_MM / 2.0,
1733                    height: args::MIN_PALM_SIZE_MM / 2.0,
1734                }),
1735            };
1736    }
1737
1738    mod idle_chain_states {
1739        use super::super::{
1740            ExamineEventResult, GestureArena, InputHandler, MutableState,
1741            ProcessBufferedEventsResult, Reason, RecognizedGesture, TouchpadEvent, args,
1742        };
1743        use super::utils::{
1744            ContenderFactoryCalled, ContenderFactoryOnceOrPanic, StubContender,
1745            StubMatchedContender, StubWinner, TOUCH_CONTACT_INDEX_FINGER, make_touchpad_descriptor,
1746            make_unhandled_keyboard_event, make_unhandled_mouse_event,
1747            make_unhandled_touchpad_event,
1748        };
1749        use crate::input_handler::InputHandlerStatus;
1750        use crate::utils::Size;
1751        use crate::{Position, input_device, touch_binding};
1752        use assert_matches::assert_matches;
1753
1754        use maplit::hashset;
1755        use std::cell::RefCell;
1756        use std::rc::Rc;
1757        use test_case::test_case;
1758
1759        fn make_gesture_arena_with_state(
1760            contender_factory: ContenderFactoryOnceOrPanic,
1761            state: MutableState,
1762        ) -> Rc<GestureArena> {
1763            Rc::new(GestureArena {
1764                contender_factory: Box::new(contender_factory),
1765                mutable_state: RefCell::new(state),
1766                inspect_log: RefCell::new(fuchsia_inspect_contrib::nodes::BoundedListNode::new(
1767                    fuchsia_inspect::Inspector::default().root().create_child("some_key"),
1768                    1,
1769                )),
1770                inspect_status: InputHandlerStatus::default(),
1771            })
1772        }
1773
1774        #[test_case(MutableState::Idle; "idle")]
1775        #[test_case(MutableState::Chain; "chain")]
1776        #[fuchsia::test(allow_stalls = false)]
1777        async fn invokes_contender_factory_on_touchpad_event(state: MutableState) {
1778            let contender_factory = Box::new(ContenderFactoryCalled::new());
1779            let arena = Rc::new(GestureArena {
1780                contender_factory: contender_factory.clone(),
1781                mutable_state: RefCell::new(state),
1782                inspect_log: RefCell::new(fuchsia_inspect_contrib::nodes::BoundedListNode::new(
1783                    fuchsia_inspect::Inspector::default().root().create_child("some_key"),
1784                    1,
1785                )),
1786                inspect_status: InputHandlerStatus::default(),
1787            });
1788            arena.handle_input_event(make_unhandled_touchpad_event()).await;
1789            assert!(contender_factory.was_called());
1790        }
1791
1792        #[test_case(MutableState::Idle; "idle")]
1793        #[test_case(MutableState::Chain; "chain")]
1794        #[fuchsia::test(allow_stalls = false)]
1795        async fn does_not_invoke_contender_factory_on_mouse_event(state: MutableState) {
1796            let contender_factory = Box::new(ContenderFactoryCalled::new());
1797            let arena = Rc::new(GestureArena {
1798                contender_factory: contender_factory.clone(),
1799                mutable_state: RefCell::new(state),
1800                inspect_log: RefCell::new(fuchsia_inspect_contrib::nodes::BoundedListNode::new(
1801                    fuchsia_inspect::Inspector::default().root().create_child("some_key"),
1802                    1,
1803                )),
1804                inspect_status: InputHandlerStatus::default(),
1805            });
1806            arena.handle_input_event(make_unhandled_mouse_event()).await;
1807            assert!(!contender_factory.was_called());
1808        }
1809
1810        #[test_case(MutableState::Idle; "idle")]
1811        #[test_case(MutableState::Chain; "chain")]
1812        #[fuchsia::test(allow_stalls = false)]
1813        async fn does_not_invoke_contender_factory_on_keyboard_event(state: MutableState) {
1814            let contender_factory = Box::new(ContenderFactoryCalled::new());
1815
1816            let arena = Rc::new(GestureArena {
1817                contender_factory: contender_factory.clone(),
1818                mutable_state: RefCell::new(state),
1819                inspect_log: RefCell::new(fuchsia_inspect_contrib::nodes::BoundedListNode::new(
1820                    fuchsia_inspect::Inspector::default().root().create_child("some_key"),
1821                    1,
1822                )),
1823                inspect_status: InputHandlerStatus::default(),
1824            });
1825            arena.handle_input_event(make_unhandled_keyboard_event()).await;
1826            assert!(!contender_factory.was_called());
1827        }
1828
1829        #[test_case(MutableState::Idle; "idle")]
1830        #[test_case(MutableState::Chain; "chain")]
1831        #[fuchsia::test(allow_stalls = false)]
1832        async fn calls_examine_event_on_contender(state: MutableState) {
1833            let contender = Box::new(StubContender::new());
1834            let contender_factory = ContenderFactoryOnceOrPanic::new(vec![contender.clone()]);
1835            let arena = make_gesture_arena_with_state(contender_factory, state);
1836            contender.set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
1837            arena.handle_input_event(make_unhandled_touchpad_event()).await;
1838            pretty_assertions::assert_eq!(contender.calls_received(), 1);
1839        }
1840
1841        #[fuchsia::test(allow_stalls = false)]
1842        async fn calls_examine_event_on_idle_only_contender_while_idle() {
1843            let contender = Box::new(StubContender::new_start_from_idle());
1844            let contender_factory = ContenderFactoryOnceOrPanic::new(vec![contender.clone()]);
1845            let arena = make_gesture_arena_with_state(contender_factory, MutableState::Idle);
1846            contender.set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
1847            arena.handle_input_event(make_unhandled_touchpad_event()).await;
1848            pretty_assertions::assert_eq!(contender.calls_received(), 1);
1849        }
1850
1851        #[fuchsia::test(allow_stalls = false)]
1852        async fn does_not_calls_examine_event_on_idle_only_contender_while_chain() {
1853            let contender = Box::new(StubContender::new_start_from_idle());
1854            let contender_factory = ContenderFactoryOnceOrPanic::new(vec![contender.clone()]);
1855            let arena = make_gesture_arena_with_state(contender_factory, MutableState::Chain);
1856            contender.set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
1857            arena.handle_input_event(make_unhandled_touchpad_event()).await;
1858            pretty_assertions::assert_eq!(contender.calls_received(), 0);
1859        }
1860
1861        #[test_case(MutableState::Idle; "idle")]
1862        #[test_case(MutableState::Chain; "chain")]
1863        #[fuchsia::test(allow_stalls = false)]
1864        async fn calls_examine_event_on_all_contenders_even_if_first_matches(state: MutableState) {
1865            let first_contender = Box::new(StubContender::new());
1866            let second_contender = Box::new(StubContender::new());
1867            let contender_factory = ContenderFactoryOnceOrPanic::new(vec![
1868                first_contender.clone(),
1869                second_contender.clone(),
1870            ]);
1871            let arena = make_gesture_arena_with_state(contender_factory, state);
1872            first_contender.set_next_result(ExamineEventResult::MatchedContender(
1873                StubMatchedContender::new().into(),
1874            ));
1875            // Second contender keeps gesture arena in Matching state.
1876            second_contender
1877                .set_next_result(ExamineEventResult::Contender(StubContender::new().into()));
1878            arena.handle_input_event(make_unhandled_touchpad_event()).await;
1879            pretty_assertions::assert_eq!(first_contender.calls_received(), 1);
1880            pretty_assertions::assert_eq!(second_contender.calls_received(), 1);
1881        }
1882
1883        #[test_case(MutableState::Idle; "idle")]
1884        #[test_case(MutableState::Chain; "chain")]
1885        #[fuchsia::test(allow_stalls = false)]
1886        async fn retains_reference_to_replacement_contender(state: MutableState) {
1887            // Create a gesture arena which will instantiate a `StubContender`.
1888            let initial_contender = Box::new(StubContender::new());
1889            let contender_factory =
1890                ContenderFactoryOnceOrPanic::new(vec![initial_contender.clone()]);
1891            let arena = make_gesture_arena_with_state(contender_factory, state);
1892
1893            // Configure `initial_contender` to return a new `StubContender` when
1894            // `examine_event()` is called.
1895            let replacement_contender = StubContender::new();
1896            initial_contender.set_next_result(ExamineEventResult::Contender(
1897                replacement_contender.clone().into(),
1898            ));
1899
1900            // Process a touchpad event. This should cause `arena` to consume the
1901            // `ExamineEventResult` set above.
1902            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
1903
1904            // Verify that the `ExamineEventResult` was, in fact, consumed.
1905            initial_contender.assert_next_result_is_none();
1906
1907            // Finally, verify that `replacement_contender` has two references.
1908            //
1909            // Note that:
1910            // * One of the references is from `replacement_contender`.
1911            // * The second one cannot be from the `ExamineEventResult` above,
1912            //   because the `ExamineEventResult` was consumed.
1913            //
1914            // Hence: the second reference (if it exists) must be in the gesture arena.
1915            pretty_assertions::assert_eq!(replacement_contender.ref_count(), 2);
1916        }
1917
1918        #[test_case(MutableState::Idle; "idle")]
1919        #[test_case(MutableState::Chain; "chain")]
1920        #[fuchsia::test(allow_stalls = false)]
1921        async fn retains_reference_to_matched_contender(state: MutableState) {
1922            // Create a gesture arena which will instantiate a `StubContender`.
1923            let initial_contender = Box::new(StubContender::new());
1924            // Second contender keeps gesture arena in Matching state.
1925            let second_contender = Box::new(StubContender::new());
1926            let contender_factory = ContenderFactoryOnceOrPanic::new(vec![
1927                initial_contender.clone(),
1928                second_contender.clone(),
1929            ]);
1930            let arena = make_gesture_arena_with_state(contender_factory, state);
1931            // Configure `initial_contender` to return a `StubMatchedContender` when
1932            // `examine_event()` is called.
1933            let replacement_contender = StubMatchedContender::new();
1934            initial_contender.set_next_result(ExamineEventResult::MatchedContender(
1935                replacement_contender.clone().into(),
1936            ));
1937            second_contender
1938                .set_next_result(ExamineEventResult::Contender(StubContender::new().into()));
1939
1940            // Process a touchpad event. This should cause `arena` to consume the
1941            // `ExamineEventResult` set above.
1942            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
1943
1944            // Verify that the `ExamineEventResult` was, in fact, consumed.
1945            initial_contender.assert_next_result_is_none();
1946
1947            // Finally, verify that `replacement_contender` has two references.
1948            //
1949            // Note that:
1950            // * One of the references is from `replacement_contender`.
1951            // * The second one cannot be from the `ExamineEventResult` above,
1952            //   because the `ExamineEventResult` was consumed.
1953            //
1954            // Hence: the second reference (if it exists) must be in the gesture arena.
1955            pretty_assertions::assert_eq!(replacement_contender.ref_count(), 2);
1956        }
1957
1958        #[test_case(MutableState::Idle; "idle")]
1959        #[test_case(MutableState::Chain; "chain")]
1960        #[fuchsia::test(allow_stalls = false)]
1961        async fn retains_touchpad_event_when_entering_matching(state: MutableState) {
1962            // Create a gesture arena which will instantiate a `StubContender`.
1963            let initial_contender = Box::new(StubContender::new());
1964            // Second contender keeps gesture arena in Matching state.
1965            let second_contender = Box::new(StubContender::new());
1966            let contender_factory = ContenderFactoryOnceOrPanic::new(vec![
1967                initial_contender.clone(),
1968                second_contender.clone(),
1969            ]);
1970            let arena = make_gesture_arena_with_state(contender_factory, state);
1971            // Create the event which will be sent to the arena.
1972            let touchpad_event = input_device::InputEvent {
1973                event_time: zx::MonotonicInstant::from_nanos(123456),
1974                device_event: input_device::InputDeviceEvent::Touchpad(
1975                    touch_binding::TouchpadEvent {
1976                        injector_contacts: vec![TOUCH_CONTACT_INDEX_FINGER],
1977                        pressed_buttons: hashset! {0},
1978                    },
1979                ),
1980                device_descriptor: make_touchpad_descriptor(),
1981                trace_id: None,
1982                handled: input_device::Handled::No,
1983            };
1984
1985            // Configure `initial_contender` to return a `StubMatchedContender` when
1986            // `examine_event()` is called.
1987            initial_contender.set_next_result(ExamineEventResult::MatchedContender(
1988                StubMatchedContender::new().into(),
1989            ));
1990            second_contender
1991                .set_next_result(ExamineEventResult::Contender(StubContender::new().into()));
1992
1993            // Process `touchpad_event`. Because `initial_contender` returns
1994            // `ExamineEventResult::MatchedContender`, the gesture arena will enter
1995            // the `Matching` state.
1996            arena.clone().handle_input_event(touchpad_event).await;
1997
1998            // Verify that `arena` retained the details of `touchpad_event`.
1999            assert_matches!(
2000                &*arena.mutable_state.borrow(),
2001                MutableState::Matching {
2002                    contenders: _,
2003                    matched_contenders: _,
2004                    buffered_events,
2005                    first_event_timestamp: _,
2006                } => pretty_assertions::assert_eq!(
2007                    buffered_events.as_slice(),
2008                    [TouchpadEvent {
2009                        timestamp: zx::MonotonicInstant::from_nanos(123456),
2010                        pressed_buttons: vec![0],
2011                        contacts: vec![
2012                            touch_binding::TouchContact {
2013                                contact_size: Some(Size{ width: args::MIN_PALM_SIZE_MM / 2000.0, height: args::MIN_PALM_SIZE_MM / 2000.0 }),
2014                                ..TOUCH_CONTACT_INDEX_FINGER
2015                            },
2016                        ],
2017                        filtered_palm_contacts: vec![],
2018                    }]
2019                )
2020            );
2021        }
2022
2023        #[test_case(MutableState::Idle; "idle")]
2024        #[test_case(MutableState::Chain; "chain")]
2025        #[fuchsia::test(allow_stalls = false)]
2026        async fn generates_no_events_on_mismatch_entering_chain(state: MutableState) {
2027            let contender = Box::new(StubContender::new());
2028            let contender_factory = ContenderFactoryOnceOrPanic::new(vec![contender.clone()]);
2029            let arena = make_gesture_arena_with_state(contender_factory, state);
2030            contender.set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
2031
2032            let touchpad_event = input_device::InputEvent {
2033                event_time: zx::MonotonicInstant::from_nanos(123456),
2034                device_event: input_device::InputDeviceEvent::Touchpad(
2035                    touch_binding::TouchpadEvent {
2036                        injector_contacts: vec![touch_binding::TouchContact {
2037                            id: 1,
2038                            position: Position { x: 0.0, y: 0.0 },
2039                            ..TOUCH_CONTACT_INDEX_FINGER
2040                        }],
2041                        pressed_buttons: hashset! {},
2042                    },
2043                ),
2044                device_descriptor: make_touchpad_descriptor(),
2045                trace_id: None,
2046                handled: input_device::Handled::No,
2047            };
2048
2049            pretty_assertions::assert_eq!(
2050                arena.clone().handle_input_event(touchpad_event).await,
2051                vec![]
2052            );
2053
2054            assert_matches!(*arena.mutable_state.borrow(), MutableState::Chain);
2055        }
2056
2057        #[test_case(MutableState::Idle; "idle")]
2058        #[test_case(MutableState::Chain; "chain")]
2059        #[fuchsia::test(allow_stalls = false)]
2060        async fn generates_no_events_on_mismatch_entering_idle(state: MutableState) {
2061            let contender = Box::new(StubContender::new());
2062            let contender_factory = ContenderFactoryOnceOrPanic::new(vec![contender.clone()]);
2063            let arena = make_gesture_arena_with_state(contender_factory, state);
2064            contender.set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
2065            pretty_assertions::assert_eq!(
2066                arena.clone().handle_input_event(make_unhandled_touchpad_event()).await,
2067                vec![]
2068            );
2069
2070            assert_matches!(*arena.mutable_state.borrow(), MutableState::Idle);
2071        }
2072
2073        #[test_case(MutableState::Idle; "idle")]
2074        #[test_case(MutableState::Chain; "chain")]
2075        #[fuchsia::test(allow_stalls = false)]
2076        async fn generates_no_events_when_entering_matching(state: MutableState) {
2077            let contender = Box::new(StubContender::new());
2078            let contender_factory = ContenderFactoryOnceOrPanic::new(vec![contender.clone()]);
2079            let arena = make_gesture_arena_with_state(contender_factory, state);
2080            contender.set_next_result(ExamineEventResult::Contender(StubContender::new().into()));
2081            pretty_assertions::assert_eq!(
2082                arena.handle_input_event(make_unhandled_touchpad_event()).await,
2083                vec![]
2084            );
2085        }
2086
2087        #[test_case(MutableState::Idle; "idle")]
2088        #[test_case(MutableState::Chain; "chain")]
2089        #[fuchsia::test(allow_stalls = false)]
2090        async fn enters_idle_on_mismatch(state: MutableState) {
2091            let contender = Box::new(StubContender::new());
2092            let contender_factory = ContenderFactoryOnceOrPanic::new(vec![contender.clone()]);
2093            let arena = make_gesture_arena_with_state(contender_factory, state);
2094            contender.set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
2095            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
2096            assert_matches!(*arena.mutable_state.borrow(), MutableState::Idle);
2097        }
2098
2099        #[test_case(MutableState::Idle; "idle")]
2100        #[test_case(MutableState::Chain; "chain")]
2101        #[fuchsia::test(allow_stalls = false)]
2102        async fn enters_matching_on_contender_result(state: MutableState) {
2103            let contender = Box::new(StubContender::new());
2104            let contender_factory = ContenderFactoryOnceOrPanic::new(vec![contender.clone()]);
2105            let arena = make_gesture_arena_with_state(contender_factory, state);
2106            contender.set_next_result(ExamineEventResult::Contender(StubContender::new().into()));
2107            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
2108            assert_matches!(*arena.mutable_state.borrow(), MutableState::Matching { .. });
2109        }
2110
2111        #[test_case(MutableState::Idle; "idle")]
2112        #[test_case(MutableState::Chain; "chain")]
2113        #[fuchsia::test(allow_stalls = false)]
2114        async fn enters_matching_on_matched_contender_result(state: MutableState) {
2115            let first_contender = Box::new(StubContender::new());
2116            // Second contender keeps gesture arena in Matching state.
2117            let second_contender = Box::new(StubContender::new());
2118            let contender_factory = ContenderFactoryOnceOrPanic::new(vec![
2119                first_contender.clone(),
2120                second_contender.clone(),
2121            ]);
2122            let arena = make_gesture_arena_with_state(contender_factory, state);
2123
2124            first_contender.set_next_result(ExamineEventResult::MatchedContender(
2125                StubMatchedContender::new().into(),
2126            ));
2127            second_contender
2128                .set_next_result(ExamineEventResult::Contender(StubContender::new().into()));
2129            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
2130            assert_matches!(*arena.mutable_state.borrow(), MutableState::Matching { .. });
2131        }
2132
2133        #[test_case(MutableState::Idle; "idle")]
2134        #[test_case(MutableState::Chain; "chain")]
2135        #[fuchsia::test(allow_stalls = false)]
2136        async fn enters_forward_on_only_one_matched_contender_result(state: MutableState) {
2137            let contender = Box::new(StubContender::new());
2138            let contender_factory = ContenderFactoryOnceOrPanic::new(vec![contender.clone()]);
2139            let arena = make_gesture_arena_with_state(contender_factory, state);
2140
2141            let matched_contender = StubMatchedContender::new();
2142            matched_contender.set_next_process_buffered_events_result(
2143                ProcessBufferedEventsResult {
2144                    generated_events: vec![],
2145                    winner: Some(Box::new(StubWinner::new())),
2146                    recognized_gesture: RecognizedGesture::Motion,
2147                },
2148            );
2149            contender
2150                .set_next_result(ExamineEventResult::MatchedContender(matched_contender.into()));
2151            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
2152            assert_matches!(*arena.mutable_state.borrow(), MutableState::Forwarding { .. });
2153        }
2154    }
2155
2156    mod matching_state {
2157        use super::super::{
2158            Contender, ContenderFactory, ExamineEventResult, GestureArena, InputHandler,
2159            MouseEvent, MutableState, PRIMARY_BUTTON, ProcessBufferedEventsResult, Reason,
2160            RecognizedGesture, TouchpadEvent, VerifyEventResult,
2161        };
2162        use super::utils::{
2163            ContenderForever, StubContender, StubMatchedContender, StubWinner,
2164            TOUCH_CONTACT_INDEX_FINGER, make_touchpad_descriptor, make_unhandled_keyboard_event,
2165            make_unhandled_mouse_event, make_unhandled_touchpad_event,
2166        };
2167        use crate::input_handler::InputHandlerStatus;
2168        use crate::{Position, input_device, mouse_binding, touch_binding};
2169        use assert_matches::assert_matches;
2170
2171        use maplit::hashset;
2172        use pretty_assertions::assert_eq;
2173        use std::cell::RefCell;
2174        use std::rc::Rc;
2175        use test_case::test_case;
2176
2177        struct ContenderFactoryWarn {}
2178
2179        impl ContenderFactory for ContenderFactoryWarn {
2180            fn make_contenders(&self) -> Vec<Box<dyn Contender>> {
2181                // Note: printing instead of panic()-ing here yields better
2182                // failure messages from the tests.
2183                eprintln!("factory invoked in matching state");
2184                vec![]
2185            }
2186        }
2187
2188        fn make_matching_arena(
2189            contenders: Vec<StubContender>,
2190            matched_contenders: Vec<StubMatchedContender>,
2191            buffered_events: Vec<TouchpadEvent>,
2192            contender_forever: Option<ContenderForever>,
2193        ) -> Rc<GestureArena> {
2194            Rc::new(GestureArena {
2195                contender_factory: Box::new(ContenderFactoryWarn {}),
2196                mutable_state: RefCell::new(MutableState::Matching {
2197                    contenders: {
2198                        contenders
2199                            .into_iter()
2200                            .map(std::convert::From::<StubContender>::from)
2201                            .chain(
2202                                contender_forever
2203                                    .into_iter()
2204                                    .map(std::convert::From::<ContenderForever>::from),
2205                            )
2206                            .collect()
2207                    },
2208                    matched_contenders: {
2209                        matched_contenders
2210                            .into_iter()
2211                            .map(std::convert::From::<StubMatchedContender>::from)
2212                            .collect()
2213                    },
2214                    first_event_timestamp: zx::MonotonicInstant::ZERO,
2215                    buffered_events,
2216                }),
2217                inspect_log: RefCell::new(fuchsia_inspect_contrib::nodes::BoundedListNode::new(
2218                    fuchsia_inspect::Inspector::default().root().create_child("some_key"),
2219                    1,
2220                )),
2221                inspect_status: InputHandlerStatus::default(),
2222            })
2223        }
2224
2225        #[fuchsia::test(allow_stalls = false)]
2226        async fn invokes_examine_and_verify_event_on_touchpad_event() {
2227            let contender = StubContender::new();
2228            let matched_contender = StubMatchedContender::new();
2229            let arena = make_matching_arena(
2230                vec![contender.clone()],
2231                vec![matched_contender.clone()],
2232                vec![],
2233                None,
2234            );
2235            contender.set_next_result(ExamineEventResult::Contender(contender.clone().into()));
2236            matched_contender.set_next_verify_event_result(VerifyEventResult::Mismatch(
2237                Reason::Basic("some reason"),
2238            ));
2239            arena.handle_input_event(make_unhandled_touchpad_event()).await;
2240            assert_eq!(contender.calls_received(), 1);
2241            assert_eq!(matched_contender.verify_event_calls_received(), 1);
2242        }
2243
2244        #[fuchsia::test(allow_stalls = false)]
2245        async fn does_not_invoke_examine_or_verify_event_on_mouse_event() {
2246            let contender = StubContender::new();
2247            let matched_contender = StubMatchedContender::new();
2248            let arena = make_matching_arena(
2249                vec![contender.clone()],
2250                vec![matched_contender.clone()],
2251                vec![],
2252                None,
2253            );
2254            contender.set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
2255            matched_contender.set_next_verify_event_result(VerifyEventResult::Mismatch(
2256                Reason::Basic("some reason"),
2257            ));
2258            arena.handle_input_event(make_unhandled_mouse_event()).await;
2259            assert_eq!(contender.calls_received(), 0);
2260            assert_eq!(matched_contender.verify_event_calls_received(), 0);
2261        }
2262
2263        #[fuchsia::test(allow_stalls = false)]
2264        async fn does_not_invoke_examine_or_verify_event_on_keyboard_event() {
2265            let contender = StubContender::new();
2266            let matched_contender = StubMatchedContender::new();
2267            let arena = make_matching_arena(
2268                vec![contender.clone()],
2269                vec![matched_contender.clone()],
2270                vec![],
2271                None,
2272            );
2273            contender.set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
2274            matched_contender.set_next_verify_event_result(VerifyEventResult::Mismatch(
2275                Reason::Basic("some reason"),
2276            ));
2277            arena.handle_input_event(make_unhandled_keyboard_event()).await;
2278            assert_eq!(contender.calls_received(), 0);
2279            assert_eq!(matched_contender.verify_event_calls_received(), 0);
2280        }
2281
2282        #[fuchsia::test(allow_stalls = false)]
2283        async fn does_not_repeat_event_to_matched_contender_returned_by_examine_event() {
2284            let contender = StubContender::new();
2285            let arena = make_matching_arena(
2286                vec![contender.clone()],
2287                vec![],
2288                vec![],
2289                // Make sure that `arena` does not progress to the forwarding state,
2290                // even if the logic _does_ repeat the event.
2291                Some(ContenderForever {}),
2292            );
2293
2294            // Configure `contender` to give a `MatchContender` to `arena`.
2295            let matched_contender = StubMatchedContender::new();
2296            contender.set_next_result(ExamineEventResult::MatchedContender(
2297                matched_contender.clone().into(),
2298            ));
2299
2300            // Set the return value for `matched_contender`. If the implementation
2301            // is buggy, and `verify_event()` is called, having a return value for
2302            // that call makes this test fail at the final assertion, which is easier
2303            // to understand.
2304            matched_contender.set_next_verify_event_result(VerifyEventResult::Mismatch(
2305                Reason::Basic("some reason"),
2306            ));
2307
2308            // Send the touchpad event, and validate that the arena did not call
2309            // `verify_event()` on the newly returned `MatchedContender`.
2310            arena.handle_input_event(make_unhandled_touchpad_event()).await;
2311            assert_eq!(matched_contender.verify_event_calls_received(), 0);
2312        }
2313
2314        #[fuchsia::test(allow_stalls = false)]
2315        async fn invokes_examine_event_for_new_event_with_contender_replaced_by_contender() {
2316            // Set up an arena with a `StubContender` that will continue the
2317            // matching process with a new `StubContender`.
2318            let initial_contender = StubContender::new();
2319            let arena = make_matching_arena(vec![initial_contender.clone()], vec![], vec![], None);
2320            let replacement_contender = StubContender::new();
2321            initial_contender.set_next_result(ExamineEventResult::Contender(
2322                replacement_contender.clone().into(),
2323            ));
2324
2325            // Process a touchpad event. This should cause `arena` to replace
2326            // `initial_contender` with `replacement_contender`.
2327            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
2328
2329            // Process another touchpad event. This should cause `arena` to invoke
2330            // `examine_event()` on `replacement_contender`.
2331            replacement_contender
2332                .set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
2333            arena.handle_input_event(make_unhandled_touchpad_event()).await;
2334
2335            // Verify that `replacement_contender` was called.
2336            assert_eq!(replacement_contender.calls_received(), 1);
2337        }
2338
2339        #[fuchsia::test(allow_stalls = false)]
2340        async fn invokes_verify_event_for_new_event_with_contender_replaced_by_matched_contender() {
2341            // Set up an arena with
2342            // * a `StubContender` that will continue the matching process with
2343            //   a new `StubMatchedContender`
2344            // * a `ContenderForever`, which will keep the arena in the matching
2345            //   state
2346            let initial_contender = StubContender::new();
2347            let arena = make_matching_arena(
2348                vec![initial_contender.clone()],
2349                vec![],
2350                vec![],
2351                Some(ContenderForever {}),
2352            );
2353            let replacement_contender = StubMatchedContender::new();
2354            initial_contender.set_next_result(ExamineEventResult::MatchedContender(
2355                replacement_contender.clone().into(),
2356            ));
2357
2358            // Process a touchpad event. This should cause `arena` to replace
2359            // `initial_contender` with `replacement_contender`.
2360            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
2361
2362            // Process another touchpad event. This should cause `arena` to invoke
2363            // `examine_event()` on `replacement_contender`.
2364            replacement_contender.set_next_verify_event_result(VerifyEventResult::Mismatch(
2365                Reason::Basic("some reason"),
2366            ));
2367            arena.handle_input_event(make_unhandled_touchpad_event()).await;
2368
2369            // Verify that `replacement_contender` was called.
2370            assert_eq!(replacement_contender.verify_event_calls_received(), 1);
2371        }
2372
2373        #[fuchsia::test(allow_stalls = false)]
2374        async fn invokes_verify_event_for_new_event_with_matched_contender_replaced_by_matched_contender()
2375         {
2376            let matched_contender = StubMatchedContender::new();
2377            let arena = make_matching_arena(
2378                vec![],
2379                vec![matched_contender.clone()],
2380                vec![],
2381                // Ensure that `arena` remains in the matching state. This simplifies
2382                // the test by eliminating the need to provide a response to a
2383                // potential `process_buffered_events()` call.
2384                Some(ContenderForever {}),
2385            );
2386
2387            // Configure `matched_contender` to replace itself with
2388            // `replacement_matched_contender`.
2389            let replacement_matched_contender = StubMatchedContender::new();
2390            matched_contender.set_next_verify_event_result(VerifyEventResult::MatchedContender(
2391                replacement_matched_contender.clone().into(),
2392            ));
2393
2394            // Process a touchpad event. This should cause `arena` to retain
2395            // replace `contender` with `replacement_contender`.
2396            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
2397
2398            // Set a return value for the expected call on `replacement_contender`.
2399            replacement_matched_contender.set_next_verify_event_result(
2400                VerifyEventResult::Mismatch(Reason::Basic("some reason")),
2401            );
2402
2403            // Process another touchpad event, and verify that `replacement_contender`
2404            // is called.
2405            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
2406            assert_eq!(replacement_matched_contender.verify_event_calls_received(), 1);
2407        }
2408
2409        #[fuchsia::test(allow_stalls = false)]
2410        async fn generates_no_events_on_mismatch_entering_idle() {
2411            let contender = StubContender::new();
2412            let arena = make_matching_arena(vec![contender.clone()], vec![], vec![], None);
2413            contender.set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
2414            assert_eq!(
2415                arena.clone().handle_input_event(make_unhandled_touchpad_event()).await,
2416                vec![]
2417            );
2418            assert_matches!(*arena.mutable_state.borrow(), MutableState::Idle);
2419        }
2420
2421        #[fuchsia::test(allow_stalls = false)]
2422        async fn generates_no_events_on_mismatch_entering_chain() {
2423            let contender = StubContender::new();
2424            let arena = make_matching_arena(vec![contender.clone()], vec![], vec![], None);
2425            contender.set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
2426
2427            let touchpad_event = input_device::InputEvent {
2428                event_time: zx::MonotonicInstant::from_nanos(123456),
2429                device_event: input_device::InputDeviceEvent::Touchpad(
2430                    touch_binding::TouchpadEvent {
2431                        injector_contacts: vec![touch_binding::TouchContact {
2432                            id: 1,
2433                            position: Position { x: 0.0, y: 0.0 },
2434                            ..TOUCH_CONTACT_INDEX_FINGER
2435                        }],
2436                        pressed_buttons: hashset! {},
2437                    },
2438                ),
2439                device_descriptor: make_touchpad_descriptor(),
2440                trace_id: None,
2441                handled: input_device::Handled::No,
2442            };
2443
2444            pretty_assertions::assert_eq!(
2445                arena.clone().handle_input_event(touchpad_event).await,
2446                vec![]
2447            );
2448
2449            assert_matches!(*arena.mutable_state.borrow(), MutableState::Chain);
2450        }
2451
2452        #[fuchsia::test(allow_stalls = false)]
2453        async fn generates_no_events_on_contender() {
2454            let contender = StubContender::new();
2455            let arena = make_matching_arena(vec![contender.clone()], vec![], vec![], None);
2456            contender.set_next_result(ExamineEventResult::Contender(contender.clone().into()));
2457            assert_eq!(arena.handle_input_event(make_unhandled_touchpad_event()).await, vec![]);
2458        }
2459
2460        #[fuchsia::test(allow_stalls = false)]
2461        async fn generates_no_events_on_multiple_matched_contenders() {
2462            let first_matched_contender = StubMatchedContender::new();
2463            let second_matched_contender = StubMatchedContender::new();
2464            let arena = make_matching_arena(
2465                vec![],
2466                vec![first_matched_contender.clone(), second_matched_contender.clone()],
2467                vec![],
2468                None,
2469            );
2470            first_matched_contender.set_next_verify_event_result(
2471                VerifyEventResult::MatchedContender(first_matched_contender.clone().into()),
2472            );
2473            second_matched_contender.set_next_verify_event_result(
2474                VerifyEventResult::MatchedContender(second_matched_contender.clone().into()),
2475            );
2476            assert_eq!(arena.handle_input_event(make_unhandled_touchpad_event()).await, vec![]);
2477        }
2478
2479        #[test_case(Some(StubWinner::new()); "with_winner")]
2480        #[test_case(None; "without_winner")]
2481        #[fuchsia::test(allow_stalls = false)]
2482        async fn generates_events_from_process_buffered_events_on_single_matched_contender(
2483            winner: Option<StubWinner>,
2484        ) {
2485            let matched_contender = StubMatchedContender::new();
2486            let arena = make_matching_arena(vec![], vec![matched_contender.clone()], vec![], None);
2487            matched_contender.set_next_verify_event_result(VerifyEventResult::MatchedContender(
2488                matched_contender.clone().into(),
2489            ));
2490            matched_contender.set_next_process_buffered_events_result(
2491                ProcessBufferedEventsResult {
2492                    generated_events: vec![
2493                        MouseEvent {
2494                            timestamp: zx::MonotonicInstant::from_nanos(123),
2495                            mouse_data: mouse_binding::MouseEvent {
2496                                location: mouse_binding::MouseLocation::Relative(
2497                                    mouse_binding::RelativeLocation {
2498                                        millimeters: Position::zero(),
2499                                    },
2500                                ),
2501                                wheel_delta_v: None,
2502                                wheel_delta_h: None,
2503                                phase: mouse_binding::MousePhase::Down,
2504                                affected_buttons: hashset! { PRIMARY_BUTTON },
2505                                pressed_buttons: hashset! { PRIMARY_BUTTON },
2506                                is_precision_scroll: None,
2507                                wake_lease: None.into(),
2508                            },
2509                        },
2510                        MouseEvent {
2511                            timestamp: zx::MonotonicInstant::from_nanos(456),
2512                            mouse_data: mouse_binding::MouseEvent {
2513                                location: mouse_binding::MouseLocation::Relative(
2514                                    mouse_binding::RelativeLocation {
2515                                        millimeters: Position::zero(),
2516                                    },
2517                                ),
2518                                wheel_delta_v: None,
2519                                wheel_delta_h: None,
2520                                phase: mouse_binding::MousePhase::Up,
2521                                affected_buttons: hashset! { PRIMARY_BUTTON },
2522                                pressed_buttons: hashset! {},
2523                                is_precision_scroll: None,
2524                                wake_lease: None.into(),
2525                            },
2526                        },
2527                    ],
2528                    winner: winner.map(std::convert::From::<StubWinner>::from),
2529                    recognized_gesture: RecognizedGesture::Motion,
2530                },
2531            );
2532            assert_matches!(
2533                arena
2534                    .handle_input_event(make_unhandled_touchpad_event())
2535                    .await
2536                    .as_slice(),
2537                [
2538                    input_device::InputEvent {
2539                        handled: input_device::Handled::No,
2540                        device_event: input_device::InputDeviceEvent::Mouse(
2541                            mouse_binding::MouseEvent {
2542                                pressed_buttons: first_pressed_buttons, ..
2543                            }
2544                        ),
2545                        ..
2546                    },
2547                    input_device::InputEvent {
2548                        handled: input_device::Handled::No,
2549                        device_event: input_device::InputDeviceEvent::Mouse(
2550                            mouse_binding::MouseEvent {
2551                                pressed_buttons: second_pressed_buttons, ..
2552                            }
2553                        ),
2554                        ..
2555                    },
2556                ] => {
2557                    pretty_assertions::assert_eq!(*first_pressed_buttons, hashset! { PRIMARY_BUTTON });
2558                    pretty_assertions::assert_eq!(*second_pressed_buttons, hashset! {});
2559                }
2560            );
2561        }
2562
2563        #[fuchsia::test(allow_stalls = false)]
2564        async fn passes_all_buffered_events_to_process_buffered_events() {
2565            // Create an arena, and seed it a buffered event (emulating what happens
2566            // as the arena transitions from Idle to Matching).
2567            let contender = StubContender::new();
2568            let matched_contender = StubMatchedContender::new();
2569            let arena = make_matching_arena(
2570                vec![contender.clone()],
2571                vec![matched_contender.clone()],
2572                vec![TouchpadEvent {
2573                    timestamp: zx::MonotonicInstant::from_nanos(123),
2574                    contacts: vec![],
2575                    pressed_buttons: vec![],
2576                    filtered_palm_contacts: vec![],
2577                }],
2578                None,
2579            );
2580
2581            // Send another event to the arena, to exercise the case of an event
2582            // being buffered because the contest continued.
2583            contender.set_next_result(ExamineEventResult::Contender(contender.clone().into()));
2584            matched_contender.set_next_verify_event_result(VerifyEventResult::MatchedContender(
2585                matched_contender.clone().into(),
2586            ));
2587            arena
2588                .clone()
2589                .handle_input_event(input_device::InputEvent {
2590                    event_time: zx::MonotonicInstant::from_nanos(456),
2591                    device_event: input_device::InputDeviceEvent::Touchpad(
2592                        touch_binding::TouchpadEvent {
2593                            injector_contacts: vec![],
2594                            pressed_buttons: hashset! {},
2595                        },
2596                    ),
2597                    device_descriptor: make_touchpad_descriptor(),
2598                    trace_id: None,
2599                    handled: input_device::Handled::No,
2600                })
2601                .await;
2602
2603            // Send the event that concludes the contest.
2604            contender.set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
2605            matched_contender.set_next_verify_event_result(VerifyEventResult::MatchedContender(
2606                matched_contender.clone().into(),
2607            ));
2608            matched_contender.set_next_process_buffered_events_result(
2609                ProcessBufferedEventsResult {
2610                    generated_events: vec![],
2611                    winner: None,
2612                    recognized_gesture: RecognizedGesture::Motion,
2613                },
2614            );
2615            arena
2616                .handle_input_event(input_device::InputEvent {
2617                    event_time: zx::MonotonicInstant::from_nanos(789),
2618                    device_event: input_device::InputDeviceEvent::Touchpad(
2619                        touch_binding::TouchpadEvent {
2620                            injector_contacts: vec![],
2621                            pressed_buttons: hashset! {},
2622                        },
2623                    ),
2624                    device_descriptor: make_touchpad_descriptor(),
2625                    trace_id: None,
2626                    handled: input_device::Handled::No,
2627                })
2628                .await;
2629
2630            // Verify that the contender received all three events.
2631            assert_eq!(
2632                matched_contender
2633                    .get_last_processed_buffered_events_args()
2634                    .map(|vec| vec
2635                        .into_iter()
2636                        .map(|event| event.timestamp.into_nanos())
2637                        .collect::<Vec<_>>())
2638                    .as_deref(),
2639                Some([123, 456, 789].as_slice())
2640            );
2641        }
2642
2643        #[fuchsia::test(allow_stalls = false)]
2644        async fn transitions_to_idle_when_sole_contender_does_not_match() {
2645            let contender = StubContender::new();
2646            let arena = make_matching_arena(vec![contender.clone()], vec![], vec![], None);
2647            contender.set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
2648            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
2649            assert_matches!(*arena.mutable_state.borrow(), MutableState::Idle);
2650        }
2651
2652        #[fuchsia::test(allow_stalls = false)]
2653        async fn transitions_to_idle_when_sole_matched_contender_does_not_match() {
2654            let matched_contender = StubMatchedContender::new();
2655            let arena = make_matching_arena(vec![], vec![matched_contender.clone()], vec![], None);
2656            matched_contender.set_next_verify_event_result(VerifyEventResult::Mismatch(
2657                Reason::Basic("some reason"),
2658            ));
2659            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
2660            assert_matches!(*arena.mutable_state.borrow(), MutableState::Idle);
2661        }
2662
2663        #[fuchsia::test(allow_stalls = false)]
2664        async fn remains_in_matching_when_a_contender_remains() {
2665            let contender = StubContender::new();
2666            let arena = make_matching_arena(vec![contender.clone()], vec![], vec![], None);
2667            contender.set_next_result(ExamineEventResult::Contender(contender.clone().into()));
2668            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
2669            assert_matches!(*arena.mutable_state.borrow(), MutableState::Matching { .. });
2670        }
2671
2672        #[fuchsia::test(allow_stalls = false)]
2673        async fn remains_in_matching_when_multiple_matched_contenders_remain() {
2674            let matched_contender_a = StubMatchedContender::new();
2675            let matched_contender_b = StubMatchedContender::new();
2676            let arena = make_matching_arena(
2677                vec![],
2678                vec![matched_contender_a.clone(), matched_contender_b.clone()],
2679                vec![],
2680                None,
2681            );
2682            matched_contender_a.set_next_verify_event_result(VerifyEventResult::MatchedContender(
2683                matched_contender_a.clone().into(),
2684            ));
2685            matched_contender_b.set_next_verify_event_result(VerifyEventResult::MatchedContender(
2686                matched_contender_b.clone().into(),
2687            ));
2688            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
2689            assert_matches!(*arena.mutable_state.borrow(), MutableState::Matching { .. });
2690        }
2691
2692        #[fuchsia::test(allow_stalls = false)]
2693        async fn transitions_to_idle_when_sole_matched_contender_returns_no_winner() {
2694            let matched_contender = StubMatchedContender::new();
2695            let arena = make_matching_arena(vec![], vec![matched_contender.clone()], vec![], None);
2696            matched_contender.set_next_verify_event_result(VerifyEventResult::MatchedContender(
2697                matched_contender.clone().into(),
2698            ));
2699            matched_contender.set_next_process_buffered_events_result(
2700                ProcessBufferedEventsResult {
2701                    generated_events: vec![],
2702                    winner: None,
2703                    recognized_gesture: RecognizedGesture::Motion,
2704                },
2705            );
2706            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
2707            assert_matches!(*arena.mutable_state.borrow(), MutableState::Idle);
2708        }
2709
2710        #[fuchsia::test(allow_stalls = false)]
2711        async fn transitions_to_forwarding_when_sole_matched_contender_returns_a_winner() {
2712            let matched_contender = StubMatchedContender::new();
2713            let arena = make_matching_arena(vec![], vec![matched_contender.clone()], vec![], None);
2714            matched_contender.set_next_verify_event_result(VerifyEventResult::MatchedContender(
2715                matched_contender.clone().into(),
2716            ));
2717            matched_contender.set_next_process_buffered_events_result(
2718                ProcessBufferedEventsResult {
2719                    generated_events: vec![],
2720                    winner: Some(StubWinner::new().into()),
2721                    recognized_gesture: RecognizedGesture::Motion,
2722                },
2723            );
2724            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
2725            assert_matches!(*arena.mutable_state.borrow(), MutableState::Forwarding { .. });
2726        }
2727    }
2728
2729    mod forwarding_state {
2730        use super::super::{
2731            Contender, EndGestureEvent, ExamineEventResult, GestureArena, InputHandler, MouseEvent,
2732            MutableState, ProcessNewEventResult, Reason, RecognizedGesture, TouchpadEvent,
2733        };
2734        use super::utils::{
2735            ContenderFactoryOnceOrPanic, ContenderForever, StubContender, StubWinner,
2736            TOUCH_CONTACT_INDEX_FINGER, make_touchpad_descriptor, make_unhandled_keyboard_event,
2737            make_unhandled_mouse_event, make_unhandled_touchpad_event,
2738        };
2739        use crate::input_handler::InputHandlerStatus;
2740        use crate::{Position, input_device, mouse_binding, touch_binding};
2741        use assert_matches::assert_matches;
2742
2743        use maplit::hashset;
2744        use pretty_assertions::assert_eq;
2745        use std::cell::RefCell;
2746        use std::rc::Rc;
2747        use test_case::test_case;
2748
2749        /// Creates an arena in the forwarding state, with
2750        /// a) the given `winner`, and
2751        /// b) an appropriate contender factory
2752        ///
2753        /// If `contender` is `None`, the contender factory will abort on the first
2754        /// call.
2755        ///
2756        /// If `contender` is `Some`, the contender factory will return the content
2757        /// of the `Option` on the first call, and abort on the second call.
2758        ///
2759        /// The former is the common case for the tests. The latter is useful for
2760        /// tests that exercise the arena's handling of an `EndGesture(Some)`.
2761        fn make_forwarding_arena(
2762            winner: StubWinner,
2763            contender: Option<Box<dyn Contender>>,
2764        ) -> Rc<GestureArena> {
2765            let contender_factory = match contender {
2766                Some(c) => Box::new(ContenderFactoryOnceOrPanic::new(vec![c])),
2767                None => Box::new(ContenderFactoryOnceOrPanic::new_panic_when_call()),
2768            };
2769
2770            Rc::new(GestureArena {
2771                contender_factory,
2772                mutable_state: RefCell::new(MutableState::Forwarding {
2773                    winner: winner.into(),
2774                    current_gesture: RecognizedGesture::Motion,
2775                    // Tests that care about `gesture_start_timestamp` should take care
2776                    // to set that value themselves. Default to a value that should cause
2777                    // any test that relies on a good value to fail.
2778                    gesture_start_timestamp: zx::MonotonicInstant::INFINITE_PAST,
2779                    num_events: 0,
2780                }),
2781                inspect_log: RefCell::new(fuchsia_inspect_contrib::nodes::BoundedListNode::new(
2782                    fuchsia_inspect::Inspector::default().root().create_child("some_key"),
2783                    1,
2784                )),
2785                inspect_status: InputHandlerStatus::default(),
2786            })
2787        }
2788
2789        #[fuchsia::test(allow_stalls = false)]
2790        async fn invokes_process_new_event_on_touchpad_event() {
2791            let winner = StubWinner::new();
2792            let arena = make_forwarding_arena(winner.clone(), None);
2793            winner.set_next_result(ProcessNewEventResult::EndGesture(
2794                EndGestureEvent::NoEvent,
2795                Reason::Basic("some reason"),
2796            ));
2797            arena.handle_input_event(make_unhandled_touchpad_event()).await;
2798            assert_eq!(winner.calls_received(), 1);
2799        }
2800
2801        #[fuchsia::test(allow_stalls = false)]
2802        async fn does_not_invoke_process_new_event_on_mouse_event() {
2803            let winner = StubWinner::new();
2804            let arena = make_forwarding_arena(winner.clone(), None);
2805            winner.set_next_result(ProcessNewEventResult::EndGesture(
2806                EndGestureEvent::NoEvent,
2807                Reason::Basic("some reason"),
2808            ));
2809            arena.handle_input_event(make_unhandled_mouse_event()).await;
2810            assert_eq!(winner.calls_received(), 0);
2811        }
2812
2813        #[fuchsia::test(allow_stalls = false)]
2814        async fn does_not_invoke_process_new_event_on_keyboard_event() {
2815            let winner = StubWinner::new();
2816            let arena = make_forwarding_arena(winner.clone(), None);
2817            winner.set_next_result(ProcessNewEventResult::EndGesture(
2818                EndGestureEvent::NoEvent,
2819                Reason::Basic("some reason"),
2820            ));
2821            arena.handle_input_event(make_unhandled_keyboard_event()).await;
2822            assert_eq!(winner.calls_received(), 0);
2823        }
2824
2825        #[fuchsia::test(allow_stalls = false)]
2826        async fn invokes_process_new_event_for_multiple_new_events() {
2827            // Create `arena` with `winner` and a `ContenderForever`. The latter
2828            // makes the test fail in a more useful way if the `GestureArena`
2829            // is buggy.
2830            let winner = StubWinner::new();
2831            let arena = make_forwarding_arena(winner.clone(), Some(ContenderForever {}.into()));
2832
2833            // Send two events to `arena`.
2834            winner.set_next_result(ProcessNewEventResult::ContinueGesture(
2835                None,
2836                winner.clone().into(),
2837            ));
2838            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
2839            winner.set_next_result(ProcessNewEventResult::ContinueGesture(
2840                None,
2841                winner.clone().into(),
2842            ));
2843            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
2844
2845            // Verify `winner` was called as expected.
2846            assert_eq!(winner.calls_received(), 2);
2847        }
2848
2849        #[fuchsia::test(allow_stalls = false)]
2850        async fn generates_event_on_continue_gesture_with_mouse_event() {
2851            let winner = StubWinner::new();
2852            let arena = make_forwarding_arena(winner.clone(), None);
2853            winner.set_next_result(ProcessNewEventResult::ContinueGesture(
2854                Some(MouseEvent {
2855                    timestamp: zx::MonotonicInstant::from_nanos(123),
2856                    mouse_data: mouse_binding::MouseEvent {
2857                        location: mouse_binding::MouseLocation::Relative(
2858                            mouse_binding::RelativeLocation { millimeters: Position::zero() },
2859                        ),
2860                        wheel_delta_v: None,
2861                        wheel_delta_h: None,
2862                        phase: mouse_binding::MousePhase::Move,
2863                        affected_buttons: hashset! {},
2864                        pressed_buttons: hashset! {},
2865                        is_precision_scroll: None,
2866                        wake_lease: None.into(),
2867                    },
2868                }),
2869                winner.clone().into(),
2870            ));
2871            assert_matches!(
2872                arena.handle_input_event(make_unhandled_touchpad_event()).await.as_slice(),
2873                [
2874                    input_device::InputEvent {
2875                        event_time,
2876                        handled: input_device::Handled::No,
2877                        device_event: input_device::InputDeviceEvent::Mouse(_),
2878                        ..
2879                    },
2880                ] => pretty_assertions::assert_eq!(*event_time, zx::MonotonicInstant::from_nanos(123))
2881            );
2882        }
2883
2884        #[fuchsia::test(allow_stalls = false)]
2885        async fn generates_no_events_on_continue_gesture_without_mouse_event() {
2886            let winner = StubWinner::new();
2887            let arena = make_forwarding_arena(winner.clone(), None);
2888            winner.set_next_result(ProcessNewEventResult::ContinueGesture(
2889                None,
2890                winner.clone().into(),
2891            ));
2892            pretty_assertions::assert_eq!(
2893                arena.handle_input_event(make_unhandled_touchpad_event()).await.as_slice(),
2894                vec![]
2895            );
2896        }
2897
2898        #[fuchsia::test(allow_stalls = false)]
2899        async fn generates_no_events_on_end_gesture_without_touchpad_event() {
2900            let winner = StubWinner::new();
2901            let arena = make_forwarding_arena(winner.clone(), None);
2902            winner.set_next_result(ProcessNewEventResult::EndGesture(
2903                EndGestureEvent::NoEvent,
2904                Reason::Basic("some reason"),
2905            ));
2906            pretty_assertions::assert_eq!(
2907                arena.handle_input_event(make_unhandled_touchpad_event()).await.as_slice(),
2908                vec![]
2909            );
2910        }
2911
2912        #[fuchsia::test(allow_stalls = false)]
2913        async fn generates_no_events_on_end_gesture_with_unconsumed_touchpad_event_entering_idle() {
2914            // Create `arena` with a `StubContender` for processing the unconsumed
2915            // `TouchpadEvent`.
2916            let winner = StubWinner::new();
2917            let contender = StubContender::new();
2918            let arena = make_forwarding_arena(winner.clone(), Some(contender.clone().into()));
2919            winner.set_next_result(ProcessNewEventResult::EndGesture(
2920                EndGestureEvent::UnconsumedEvent(TouchpadEvent {
2921                    contacts: vec![],
2922                    pressed_buttons: vec![],
2923                    timestamp: zx::MonotonicInstant::ZERO,
2924                    filtered_palm_contacts: vec![],
2925                }),
2926                Reason::Basic("some reason"),
2927            ));
2928
2929            // Set a return value for the `examine_event()` call.
2930            contender.set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
2931
2932            // Verify no events were generated.
2933            pretty_assertions::assert_eq!(
2934                arena.clone().handle_input_event(make_unhandled_touchpad_event()).await.as_slice(),
2935                vec![]
2936            );
2937
2938            // Unconsumed event without contact will invoked in handle_event_while_idle,
2939            // but no contender want this event so stay in Idle state.
2940            assert_matches!(*arena.mutable_state.borrow(), MutableState::Idle { .. });
2941        }
2942
2943        #[fuchsia::test(allow_stalls = false)]
2944        async fn generates_no_events_on_end_gesture_with_unconsumed_touchpad_event_entering_chain()
2945        {
2946            // Create `arena` with a `StubContender` for processing the unconsumed
2947            // `TouchpadEvent`.
2948            let winner = StubWinner::new();
2949            let contender = StubContender::new();
2950            let arena = make_forwarding_arena(winner.clone(), Some(contender.clone().into()));
2951            winner.set_next_result(ProcessNewEventResult::EndGesture(
2952                EndGestureEvent::UnconsumedEvent(TouchpadEvent {
2953                    contacts: vec![touch_binding::TouchContact {
2954                        id: 1,
2955                        position: Position { x: 0.0, y: 0.0 },
2956                        pressure: None,
2957                        contact_size: None,
2958                    }],
2959                    pressed_buttons: vec![],
2960                    timestamp: zx::MonotonicInstant::from_nanos(123456),
2961                    filtered_palm_contacts: vec![],
2962                }),
2963                Reason::Basic("some reason"),
2964            ));
2965
2966            // Set a return value for the `examine_event()` call.
2967            contender.set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
2968
2969            let touchpad_event = input_device::InputEvent {
2970                event_time: zx::MonotonicInstant::from_nanos(123456),
2971                device_event: input_device::InputDeviceEvent::Touchpad(
2972                    touch_binding::TouchpadEvent {
2973                        injector_contacts: vec![touch_binding::TouchContact {
2974                            id: 1,
2975                            position: Position { x: 0.0, y: 0.0 },
2976                            ..TOUCH_CONTACT_INDEX_FINGER
2977                        }],
2978                        pressed_buttons: hashset! {},
2979                    },
2980                ),
2981                device_descriptor: make_touchpad_descriptor(),
2982                trace_id: None,
2983                handled: input_device::Handled::No,
2984            };
2985
2986            // Verify no events were generated.
2987            pretty_assertions::assert_eq!(
2988                arena.clone().handle_input_event(touchpad_event).await.as_slice(),
2989                vec![]
2990            );
2991
2992            // Unconsumed event with contact will invoked in handle_event_while_chain,
2993            // but no contender want this event so stay in Chain state.
2994            assert_matches!(*arena.mutable_state.borrow(), MutableState::Chain { .. });
2995        }
2996
2997        #[fuchsia::test(allow_stalls = false)]
2998        async fn generates_event_on_end_gesture_with_touchpad_event() {
2999            // Create `arena` with a `StubContender` for processing the unconsumed
3000            // `TouchpadEvent`.
3001            let winner = StubWinner::new();
3002            let arena = make_forwarding_arena(winner.clone(), None);
3003            let mouse_event = MouseEvent {
3004                timestamp: zx::MonotonicInstant::from_nanos(123),
3005                mouse_data: mouse_binding::MouseEvent {
3006                    location: mouse_binding::MouseLocation::Relative(
3007                        mouse_binding::RelativeLocation { millimeters: Position::zero() },
3008                    ),
3009                    wheel_delta_v: None,
3010                    wheel_delta_h: None,
3011                    phase: mouse_binding::MousePhase::Move,
3012                    affected_buttons: hashset! {},
3013                    pressed_buttons: hashset! {},
3014                    is_precision_scroll: None,
3015                    wake_lease: None.into(),
3016                },
3017            };
3018            winner.set_next_result(ProcessNewEventResult::EndGesture(
3019                EndGestureEvent::GeneratedEvent(mouse_event),
3020                Reason::Basic("some reason"),
3021            ));
3022
3023            // Verify events were generated.
3024            assert_matches!(
3025                arena.handle_input_event(make_unhandled_touchpad_event()).await.as_slice(),
3026                [
3027                    input_device::InputEvent {
3028                        event_time,
3029                        handled: input_device::Handled::No,
3030                        device_event: input_device::InputDeviceEvent::Mouse(_),
3031                        ..
3032                    },
3033                ] => pretty_assertions::assert_eq!(*event_time, zx::MonotonicInstant::from_nanos(123))
3034            );
3035        }
3036
3037        #[test_case(Some(MouseEvent{
3038            timestamp: zx::MonotonicInstant::from_nanos(123),
3039            mouse_data: mouse_binding::MouseEvent {
3040                location: mouse_binding::MouseLocation::Relative(
3041                    mouse_binding::RelativeLocation {
3042                        millimeters: Position::zero(),
3043                    },
3044                ),
3045                wheel_delta_v: None,
3046                wheel_delta_h: None,
3047                phase: mouse_binding::MousePhase::Move,
3048                affected_buttons: hashset! {},
3049                pressed_buttons: hashset! {},
3050                is_precision_scroll: None,
3051                    wake_lease: None.into(),
3052
3053            },
3054        }); "with_mouse_event")]
3055        #[test_case(None; "without_mouse_event")]
3056        #[fuchsia::test(allow_stalls = false)]
3057        async fn remains_in_forwarding_on_continue_gesture(mouse_event: Option<MouseEvent>) {
3058            let winner = StubWinner::new();
3059            let arena = make_forwarding_arena(winner.clone(), None);
3060            winner.set_next_result(ProcessNewEventResult::ContinueGesture(
3061                mouse_event,
3062                winner.clone().into(),
3063            ));
3064            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
3065            assert_matches!(*arena.mutable_state.borrow(), MutableState::Forwarding { .. });
3066        }
3067
3068        #[fuchsia::test(allow_stalls = false)]
3069        async fn transitions_to_idle_on_end_gesture_with_touchpad_event() {
3070            let winner = StubWinner::new();
3071            let arena = make_forwarding_arena(winner.clone(), None);
3072            let mouse_event = MouseEvent {
3073                timestamp: zx::MonotonicInstant::from_nanos(123),
3074                mouse_data: mouse_binding::MouseEvent {
3075                    location: mouse_binding::MouseLocation::Relative(
3076                        mouse_binding::RelativeLocation { millimeters: Position::zero() },
3077                    ),
3078                    wheel_delta_v: None,
3079                    wheel_delta_h: None,
3080                    phase: mouse_binding::MousePhase::Move,
3081                    affected_buttons: hashset! {},
3082                    pressed_buttons: hashset! {},
3083                    is_precision_scroll: None,
3084                    wake_lease: None.into(),
3085                },
3086            };
3087            winner.set_next_result(ProcessNewEventResult::EndGesture(
3088                EndGestureEvent::GeneratedEvent(mouse_event),
3089                Reason::Basic("some reason"),
3090            ));
3091            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
3092            assert_matches!(*arena.mutable_state.borrow(), MutableState::Idle { .. });
3093        }
3094
3095        #[fuchsia::test(allow_stalls = false)]
3096        async fn transitions_to_chain_on_end_gesture_with_touchpad_event() {
3097            let winner = StubWinner::new();
3098            let arena = make_forwarding_arena(winner.clone(), None);
3099            let mouse_event = MouseEvent {
3100                timestamp: zx::MonotonicInstant::from_nanos(123),
3101                mouse_data: mouse_binding::MouseEvent {
3102                    location: mouse_binding::MouseLocation::Relative(
3103                        mouse_binding::RelativeLocation { millimeters: Position::zero() },
3104                    ),
3105                    wheel_delta_v: None,
3106                    wheel_delta_h: None,
3107                    phase: mouse_binding::MousePhase::Move,
3108                    affected_buttons: hashset! {},
3109                    pressed_buttons: hashset! {},
3110                    is_precision_scroll: None,
3111                    wake_lease: None.into(),
3112                },
3113            };
3114            winner.set_next_result(ProcessNewEventResult::EndGesture(
3115                EndGestureEvent::GeneratedEvent(mouse_event),
3116                Reason::Basic("some reason"),
3117            ));
3118            let touchpad_event = input_device::InputEvent {
3119                event_time: zx::MonotonicInstant::from_nanos(123456),
3120                device_event: input_device::InputDeviceEvent::Touchpad(
3121                    touch_binding::TouchpadEvent {
3122                        injector_contacts: vec![touch_binding::TouchContact {
3123                            id: 1,
3124                            position: Position { x: 0.0, y: 0.0 },
3125                            ..TOUCH_CONTACT_INDEX_FINGER
3126                        }],
3127                        pressed_buttons: hashset! {},
3128                    },
3129                ),
3130                device_descriptor: make_touchpad_descriptor(),
3131                trace_id: None,
3132                handled: input_device::Handled::No,
3133            };
3134            arena.clone().handle_input_event(touchpad_event).await;
3135            assert_matches!(*arena.mutable_state.borrow(), MutableState::Chain);
3136        }
3137
3138        #[fuchsia::test(allow_stalls = false)]
3139        async fn transitions_to_idle_on_end_gesture_without_touchpad_event() {
3140            let winner = StubWinner::new();
3141            let arena = make_forwarding_arena(winner.clone(), None);
3142            winner.set_next_result(ProcessNewEventResult::EndGesture(
3143                EndGestureEvent::NoEvent,
3144                Reason::Basic("reason"),
3145            ));
3146            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
3147            assert_matches!(*arena.mutable_state.borrow(), MutableState::Idle);
3148        }
3149
3150        #[fuchsia::test(allow_stalls = false)]
3151        async fn starts_new_contest_on_end_gesture_with_touchpad_event() {
3152            // Set up an arena in the forwarding state with `winner`, and
3153            // with a factory that will return `contender`. The latter should
3154            // be called at the start of the new contest.
3155            let winner = StubWinner::new();
3156            let contender = StubContender::new();
3157            let arena = make_forwarding_arena(winner.clone(), Some(contender.clone().into()));
3158
3159            // Set up `winner` to end the gesture and return an unconsumed event.
3160            winner.set_next_result(ProcessNewEventResult::EndGesture(
3161                EndGestureEvent::UnconsumedEvent(TouchpadEvent {
3162                    timestamp: zx::MonotonicInstant::ZERO,
3163                    contacts: vec![],
3164                    pressed_buttons: vec![],
3165                    filtered_palm_contacts: vec![],
3166                }),
3167                Reason::Basic("reason"),
3168            ));
3169
3170            // Set up `contender` to reply to the `examine_event()` call.
3171            contender.set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
3172
3173            // Send an event into the arena.
3174            arena.handle_input_event(make_unhandled_touchpad_event()).await;
3175
3176            // Verify that the arena started a new contest.
3177            assert_eq!(contender.calls_received(), 1);
3178        }
3179    }
3180
3181    mod touchpad_event_payload {
3182        use super::super::{ExamineEventResult, GestureArena, InputHandler, Reason, args};
3183        use super::utils::{
3184            ContenderFactoryOnceOrPanic, StubContender, TOUCH_CONTACT_INDEX_FINGER,
3185        };
3186        use crate::utils::Size;
3187        use crate::{Position, input_device, touch_binding};
3188        use assert_matches::assert_matches;
3189        use fidl_fuchsia_input_report::{self as fidl_input_report, UnitType};
3190
3191        use maplit::hashset;
3192        use std::rc::Rc;
3193        use test_case::test_case;
3194        use test_util::assert_near;
3195
3196        fn make_touchpad_descriptor(
3197            units: Vec<(fidl_input_report::Unit, fidl_input_report::Unit)>,
3198        ) -> input_device::InputDeviceDescriptor {
3199            let contacts: Vec<_> = units
3200                .into_iter()
3201                .map(|(x_unit, y_unit)| touch_binding::ContactDeviceDescriptor {
3202                    x_range: fidl_input_report::Range { min: 0, max: 1_000_000 },
3203                    y_range: fidl_input_report::Range { min: 0, max: 1_000_000 },
3204                    x_unit,
3205                    y_unit,
3206                    pressure_range: None,
3207                    width_range: Some(fidl_input_report::Range { min: 0, max: 10_000 }),
3208                    height_range: Some(fidl_input_report::Range { min: 0, max: 10_000 }),
3209                })
3210                .collect();
3211            input_device::InputDeviceDescriptor::Touchpad(touch_binding::TouchpadDeviceDescriptor {
3212                device_id: 1,
3213                contacts,
3214            })
3215        }
3216
3217        fn make_unhandled_touchpad_event_with_contacts(
3218            contact_position_units: Vec<(fidl_input_report::Unit, fidl_input_report::Unit)>,
3219            injector_contacts: Vec<touch_binding::TouchContact>,
3220        ) -> input_device::InputEvent {
3221            input_device::InputEvent {
3222                device_event: input_device::InputDeviceEvent::Touchpad(
3223                    touch_binding::TouchpadEvent {
3224                        injector_contacts,
3225                        pressed_buttons: hashset! {},
3226                    },
3227                ),
3228                device_descriptor: make_touchpad_descriptor(contact_position_units),
3229                event_time: zx::MonotonicInstant::ZERO,
3230                trace_id: None,
3231                handled: input_device::Handled::No,
3232            }
3233        }
3234
3235        fn make_unhandled_touchpad_event_with_positions(
3236            contact_position_units: Vec<(fidl_input_report::Unit, fidl_input_report::Unit)>,
3237            positions: Vec<Position>,
3238        ) -> input_device::InputEvent {
3239            let injector_contacts: Vec<_> = positions
3240                .into_iter()
3241                .enumerate()
3242                .map(|(i, position)| touch_binding::TouchContact {
3243                    id: u32::try_from(i).unwrap(),
3244                    position,
3245                    contact_size: None,
3246                    pressure: None,
3247                })
3248                .collect();
3249            make_unhandled_touchpad_event_with_contacts(contact_position_units, injector_contacts)
3250        }
3251
3252        #[test_case(
3253            vec![(
3254                fidl_input_report::Unit{ type_: UnitType::Meters, exponent: -6 },
3255                fidl_input_report::Unit{ type_: UnitType::Meters, exponent: -6 },
3256            )],
3257            vec![
3258                touch_binding::TouchContact{
3259                    id: 1,
3260                    position: Position { x: 200000.0, y: 100000.0 },
3261                    contact_size: Some(Size {
3262                        width: 2500.0,
3263                        height: 1500.0,
3264                    }),
3265                    pressure: None,
3266                }
3267            ]; "from_micrometers")]
3268        #[test_case(
3269            vec![(
3270                fidl_input_report::Unit{ type_: UnitType::Meters, exponent: -2 },
3271                fidl_input_report::Unit{ type_: UnitType::Meters, exponent: -2 },
3272            )],
3273            vec![
3274                touch_binding::TouchContact{
3275                    id: 1,
3276                    position: Position { x: 20.0, y: 10.0 },
3277                    contact_size: Some(Size {
3278                        width: 0.25,
3279                        height: 0.15,
3280                    }),
3281                    pressure: None,
3282                }
3283            ]; "from_centimeters")]
3284        #[fuchsia::test(allow_stalls = false)]
3285        async fn provides_recognizer_position_size_in_millimeters(
3286            contact_position_units: Vec<(fidl_input_report::Unit, fidl_input_report::Unit)>,
3287            contacts: Vec<touch_binding::TouchContact>,
3288        ) {
3289            let contender = Box::new(StubContender::new());
3290            let contender_factory = ContenderFactoryOnceOrPanic::new(vec![contender.clone()]);
3291            let arena = Rc::new(GestureArena::new_for_test(
3292                Box::new(contender_factory),
3293                &fuchsia_inspect::Inspector::default(),
3294                1,
3295            ));
3296            contender.set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
3297            arena
3298                .handle_input_event(make_unhandled_touchpad_event_with_contacts(
3299                    contact_position_units,
3300                    contacts,
3301                ))
3302                .await;
3303            assert_matches!(
3304                contender.get_last_touchpad_event().unwrap().contacts.as_slice(),
3305                [touch_binding::TouchContact { position, contact_size: Some(size), .. }] => {
3306                    assert_near!(position.x, 200.0, 1.0);
3307                    assert_near!(position.y, 100.0, 1.0);
3308                    assert_near!(size.width, 2.5, 0.1);
3309                    assert_near!(size.height, 1.5, 0.1);
3310                }
3311            );
3312        }
3313
3314        #[test_case(
3315            touch_binding::TouchpadEvent {
3316                injector_contacts: vec![
3317                    touch_binding::TouchContact{
3318                        id: 1,
3319                        position: Position { x: 0.0, y: 0.0 },
3320                        contact_size: Some(Size {
3321                            width: args::MIN_PALM_SIZE_MM,
3322                            height: 0.15,
3323                        }),
3324                        pressure: None,
3325                    }
3326                ],
3327                pressed_buttons: hashset! {},
3328            }; "only palm contact"
3329        )]
3330        #[fuchsia::test(allow_stalls = false)]
3331        async fn ignore_palm_contact(event: touch_binding::TouchpadEvent) {
3332            let contender = Box::new(StubContender::new());
3333            let contender_factory = ContenderFactoryOnceOrPanic::new(vec![contender.clone()]);
3334            let arena = Rc::new(GestureArena::new_for_test(
3335                Box::new(contender_factory),
3336                &fuchsia_inspect::Inspector::default(),
3337                1,
3338            ));
3339            contender.set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
3340
3341            let input_event = input_device::InputEvent {
3342                device_event: input_device::InputDeviceEvent::Touchpad(event),
3343                device_descriptor: make_touchpad_descriptor(vec![(
3344                    fidl_input_report::Unit { type_: UnitType::Meters, exponent: -3 },
3345                    fidl_input_report::Unit { type_: UnitType::Meters, exponent: -3 },
3346                )]),
3347                event_time: zx::MonotonicInstant::ZERO,
3348                trace_id: None,
3349                handled: input_device::Handled::No,
3350            };
3351
3352            arena.handle_input_event(input_event).await;
3353            assert_matches!(contender.get_last_touchpad_event().unwrap().contacts.as_slice(), []);
3354        }
3355
3356        #[test_case(
3357            touch_binding::TouchpadEvent {
3358                injector_contacts: vec![
3359                    TOUCH_CONTACT_INDEX_FINGER,
3360                    touch_binding::TouchContact{
3361                        id: 1,
3362                        position: Position { x: 0.0, y: 0.0 },
3363                        contact_size: Some(Size {
3364                            width: args::MIN_PALM_SIZE_MM,
3365                            height: 0.15,
3366                        }),
3367                        pressure: None,
3368                    }
3369                ],
3370                pressed_buttons: hashset! {},
3371            }, vec![]; "palm contact and finger"
3372        )]
3373        #[fuchsia::test(allow_stalls = false)]
3374        async fn ignore_palm_contact_keep_finger(
3375            event: touch_binding::TouchpadEvent,
3376            expect_buttons: Vec<u8>,
3377        ) {
3378            let contender = Box::new(StubContender::new());
3379            let contender_factory = ContenderFactoryOnceOrPanic::new(vec![contender.clone()]);
3380            let arena = Rc::new(GestureArena::new_for_test(
3381                Box::new(contender_factory),
3382                &fuchsia_inspect::Inspector::default(),
3383                1,
3384            ));
3385            contender.set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
3386
3387            let input_event = input_device::InputEvent {
3388                device_event: input_device::InputDeviceEvent::Touchpad(event),
3389                device_descriptor: make_touchpad_descriptor(vec![(
3390                    fidl_input_report::Unit { type_: UnitType::Meters, exponent: -3 },
3391                    fidl_input_report::Unit { type_: UnitType::Meters, exponent: -3 },
3392                )]),
3393                event_time: zx::MonotonicInstant::ZERO,
3394                trace_id: None,
3395                handled: input_device::Handled::No,
3396            };
3397
3398            arena.handle_input_event(input_event).await;
3399            let got = contender.get_last_touchpad_event().unwrap();
3400            assert_eq!(got.contacts.as_slice(), [TOUCH_CONTACT_INDEX_FINGER]);
3401            assert_eq!(got.pressed_buttons, expect_buttons);
3402        }
3403
3404        #[test_case(
3405            touch_binding::TouchpadEvent {
3406                injector_contacts: vec![
3407                    touch_binding::TouchContact{
3408                        id: 1,
3409                        position: Position { x: 0.0, y: 0.0 },
3410                        contact_size: Some(Size {
3411                            width: args::MIN_PALM_SIZE_MM,
3412                            height: 0.15,
3413                        }),
3414                        pressure: None,
3415                    }
3416                ],
3417                pressed_buttons: hashset! {1},
3418            }; "palm contact"
3419        )]
3420        #[test_case(
3421            touch_binding::TouchpadEvent {
3422                injector_contacts: vec![
3423                    touch_binding::TouchContact{
3424                        id: 1,
3425                        position: Position { x: 0.0, y: 0.0 },
3426                        contact_size: Some(Size {
3427                            width: args::MIN_PALM_SIZE_MM,
3428                            height: 0.15,
3429                        }),
3430                        pressure: None,
3431                    },
3432                    touch_binding::TouchContact{
3433                        id: 2,
3434                        position: Position { x: 5.0, y: 5.0 },
3435                        contact_size: Some(Size {
3436                            width: args::MIN_PALM_SIZE_MM / 2.0,
3437                            height: 0.15,
3438                        }),
3439                        pressure: None,
3440                    },
3441                ],
3442                pressed_buttons: hashset! {1},
3443            }; "palm and finger contact"
3444        )]
3445        #[fuchsia::test(allow_stalls = false)]
3446        async fn skip_palm_detection_when_button_down(event: touch_binding::TouchpadEvent) {
3447            let contender = Box::new(StubContender::new());
3448            let contender_factory = ContenderFactoryOnceOrPanic::new(vec![contender.clone()]);
3449            let arena = Rc::new(GestureArena::new_for_test(
3450                Box::new(contender_factory),
3451                &fuchsia_inspect::Inspector::default(),
3452                1,
3453            ));
3454            contender.set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
3455
3456            let count_of_contact = event.injector_contacts.len();
3457            let input_event = input_device::InputEvent {
3458                device_event: input_device::InputDeviceEvent::Touchpad(event),
3459                device_descriptor: make_touchpad_descriptor(vec![(
3460                    fidl_input_report::Unit { type_: UnitType::Meters, exponent: -3 },
3461                    fidl_input_report::Unit { type_: UnitType::Meters, exponent: -3 },
3462                )]),
3463                event_time: zx::MonotonicInstant::ZERO,
3464                trace_id: None,
3465                handled: input_device::Handled::No,
3466            };
3467
3468            arena.handle_input_event(input_event).await;
3469            assert_eq!(
3470                contender.get_last_touchpad_event().unwrap().contacts.len(),
3471                count_of_contact
3472            );
3473        }
3474
3475        #[test_case(
3476            vec![(
3477                fidl_input_report::Unit{ type_: UnitType::None, exponent: -6 },
3478                fidl_input_report::Unit{ type_: UnitType::None, exponent: -6 },
3479            )],
3480            vec![];
3481            "both units unspecified")]
3482        #[test_case(
3483            vec![(
3484                fidl_input_report::Unit{ type_: UnitType::None, exponent: -6 },
3485                fidl_input_report::Unit{ type_: UnitType::Meters, exponent: -6 },
3486            )],
3487            vec![];
3488            "x unit unspecified")]
3489        #[test_case(
3490            vec![(
3491                fidl_input_report::Unit{ type_: UnitType::Meters, exponent: -6 },
3492                fidl_input_report::Unit{ type_: UnitType::None, exponent: -6 },
3493            )],
3494            vec![];
3495            "y unit unspecified")]
3496        #[test_case(
3497            vec![(
3498                fidl_input_report::Unit{ type_: UnitType::Meters, exponent: -3 },
3499                fidl_input_report::Unit{ type_: UnitType::Meters, exponent: -6 },
3500            )],
3501            vec![];
3502            "mismatched exponents")]
3503        #[test_case(
3504            vec![
3505                (
3506                    fidl_input_report::Unit{ type_: UnitType::Meters, exponent: -3 },
3507                    fidl_input_report::Unit{ type_: UnitType::Meters, exponent: -3 },
3508                ),
3509                (
3510                    fidl_input_report::Unit{ type_: UnitType::Meters, exponent: -6 },
3511                    fidl_input_report::Unit{ type_: UnitType::Meters, exponent: -6 },
3512                ),
3513            ],
3514            vec![];
3515            "unequal divisors")]
3516        #[fuchsia::test(allow_stalls = false)]
3517        async fn skips_contender_on_bad_descriptor(
3518            contact_position_units: Vec<(fidl_input_report::Unit, fidl_input_report::Unit)>,
3519            positions: Vec<Position>,
3520        ) {
3521            let contender = Box::new(StubContender::new());
3522            let contender_factory = ContenderFactoryOnceOrPanic::new(vec![contender.clone()]);
3523            let arena = Rc::new(GestureArena::new_for_test(
3524                Box::new(contender_factory),
3525                &fuchsia_inspect::Inspector::default(),
3526                1,
3527            ));
3528            contender.set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
3529            arena
3530                .handle_input_event(make_unhandled_touchpad_event_with_positions(
3531                    contact_position_units,
3532                    positions,
3533                ))
3534                .await;
3535            assert_eq!(contender.calls_received(), 0);
3536        }
3537    }
3538
3539    mod inspect {
3540        use super::super::{
3541            Contender, ContenderFactory, DetailedReasonFloat, DetailedReasonInt,
3542            DetailedReasonUint, EndGestureEvent, ExamineEventResult, GestureArena, InputHandler,
3543            MouseEvent, ProcessBufferedEventsResult, ProcessNewEventResult, Reason,
3544            RecognizedGesture, TouchpadEvent, args,
3545        };
3546        use super::utils::{
3547            ContenderFactoryOnceOrPanic, StubContender, StubMatchedContender, StubWinner,
3548            make_touchpad_descriptor, make_unhandled_keyboard_event, make_unhandled_mouse_event,
3549            make_unhandled_touchpad_event,
3550        };
3551        use crate::{Position, Size, input_device, keyboard_binding, mouse_binding, touch_binding};
3552        use assert_matches::assert_matches;
3553        use maplit::hashset;
3554        use std::rc::Rc;
3555        use test_case::test_case;
3556        use {fidl_fuchsia_input_report as fidl_input_report, fuchsia_async as fasync};
3557
3558        struct EmptyContenderFactory {}
3559
3560        impl ContenderFactory for EmptyContenderFactory {
3561            fn make_contenders(&self) -> Vec<Box<dyn crate::gestures::gesture_arena::Contender>> {
3562                vec![]
3563            }
3564        }
3565
3566        #[fuchsia::test]
3567        async fn gesture_arena_initialized_with_inspect_node() {
3568            let inspector = fuchsia_inspect::Inspector::default();
3569            let fake_handlers_node = inspector.root().create_child("input_handlers_node");
3570            let _handler = GestureArena::new_internal(
3571                Box::new(EmptyContenderFactory {}),
3572                &inspector.root(),
3573                2,
3574                &fake_handlers_node,
3575            );
3576            diagnostics_assertions::assert_data_tree!(inspector, root: {
3577                gestures_event_log: {},
3578                input_handlers_node: {
3579                    gesture_arena: {
3580                        events_received_count: 0u64,
3581                        events_handled_count: 0u64,
3582                        last_received_timestamp_ns: 0u64,
3583                        "fuchsia.inspect.Health": {
3584                            status: "STARTING_UP",
3585                            // Timestamp value is unpredictable and not relevant in this context,
3586                            // so we only assert that the property is present.
3587                            start_timestamp_nanos: diagnostics_assertions::AnyProperty
3588                        },
3589                    }
3590                }
3591            });
3592        }
3593
3594        #[fasync::run_singlethreaded(test)]
3595        async fn gesture_arena_inspect_counts_events() {
3596            let inspector = fuchsia_inspect::Inspector::default();
3597            let fake_handlers_node = inspector.root().create_child("input_handlers_node");
3598            let arena = Rc::new(GestureArena::new_internal(
3599                Box::new(EmptyContenderFactory {}),
3600                &inspector.root(),
3601                1,
3602                &fake_handlers_node,
3603            ));
3604
3605            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
3606            arena.clone().handle_input_event(make_unhandled_mouse_event()).await;
3607            arena.clone().handle_input_event(make_unhandled_keyboard_event()).await;
3608            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
3609
3610            diagnostics_assertions::assert_data_tree!(inspector, root: contains {
3611                input_handlers_node: {
3612                    gesture_arena: {
3613                        events_received_count: 2u64,
3614                        events_handled_count: 0u64,
3615                        last_received_timestamp_ns: 0u64,
3616                        "fuchsia.inspect.Health": {
3617                            status: "STARTING_UP",
3618                            // Timestamp value is unpredictable and not relevant in this context,
3619                            // so we only assert that the property is present.
3620                            start_timestamp_nanos: diagnostics_assertions::AnyProperty
3621                        },
3622                    }
3623                }
3624            });
3625        }
3626
3627        #[fuchsia::test]
3628        fn logs_to_inspect() {
3629            let mut executor = fasync::TestExecutor::new_with_fake_time();
3630            let basic_mismatch_contender = Box::new(StubContender::new());
3631            let detailed_uint_mismatch_contender = Box::new(StubContender::new());
3632            let detailed_float_mismatch_contender = Box::new(StubContender::new());
3633            let detailed_int_mismatch_contender = Box::new(StubContender::new());
3634            let gesture_matching_contender = Box::new(StubContender::new());
3635            basic_mismatch_contender
3636                .set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
3637            detailed_uint_mismatch_contender.set_next_result(ExamineEventResult::Mismatch(
3638                Reason::DetailedUint(DetailedReasonUint {
3639                    criterion: "num_goats_teleported",
3640                    min: Some(10),
3641                    max: Some(30),
3642                    actual: 42,
3643                }),
3644            ));
3645            detailed_float_mismatch_contender.set_next_result(ExamineEventResult::Mismatch(
3646                Reason::DetailedFloat(DetailedReasonFloat {
3647                    criterion: "teleportation_distance_kilometers",
3648                    min: Some(10.125),
3649                    max: Some(30.5),
3650                    actual: 42.0,
3651                }),
3652            ));
3653            detailed_int_mismatch_contender.set_next_result(ExamineEventResult::Mismatch(
3654                Reason::DetailedInt(DetailedReasonInt {
3655                    criterion: "budget_surplus_trillions",
3656                    min: Some(-10),
3657                    max: Some(1),
3658                    actual: -42,
3659                }),
3660            ));
3661
3662            let inspector = fuchsia_inspect::Inspector::default();
3663            let contender_factory = Box::new(ContenderFactoryOnceOrPanic::new(vec![
3664                basic_mismatch_contender,
3665                detailed_uint_mismatch_contender,
3666                detailed_float_mismatch_contender,
3667                detailed_int_mismatch_contender,
3668                gesture_matching_contender.clone(),
3669            ]));
3670            let arena = Rc::new(GestureArena::new_for_test(contender_factory, &inspector, 100));
3671            let touchpad_descriptor = input_device::InputDeviceDescriptor::Touchpad(
3672                touch_binding::TouchpadDeviceDescriptor {
3673                    device_id: 1,
3674                    contacts: vec![touch_binding::ContactDeviceDescriptor {
3675                        x_range: fidl_input_report::Range { min: 0, max: 10_000 },
3676                        y_range: fidl_input_report::Range { min: 0, max: 10_000 },
3677                        x_unit: fidl_input_report::Unit {
3678                            // Use millimeters to avoid floating-point rounding.
3679                            type_: fidl_input_report::UnitType::Meters,
3680                            exponent: -3,
3681                        },
3682                        y_unit: fidl_input_report::Unit {
3683                            // Use millimeters to avoid floating-point rounding.
3684                            type_: fidl_input_report::UnitType::Meters,
3685                            exponent: -3,
3686                        },
3687                        pressure_range: None,
3688                        width_range: Some(fidl_input_report::Range { min: 0, max: 10_000 }),
3689                        height_range: Some(fidl_input_report::Range { min: 0, max: 10_000 }),
3690                    }],
3691                },
3692            );
3693            let keyboard_descriptor = input_device::InputDeviceDescriptor::Keyboard(
3694                keyboard_binding::KeyboardDeviceDescriptor {
3695                    device_id: 2,
3696                    device_information: fidl_fuchsia_input_report::DeviceInformation {
3697                        vendor_id: Some(0),
3698                        product_id: Some(0),
3699                        version: Some(0),
3700                        polling_rate: Some(0),
3701                        ..Default::default()
3702                    },
3703                    keys: vec![fidl_fuchsia_input::Key::A, fidl_fuchsia_input::Key::B],
3704                },
3705            );
3706
3707            // Process a touchpad event without width/height.
3708            let mut handle_event_fut = arena.clone().handle_input_event(input_device::InputEvent {
3709                device_event: input_device::InputDeviceEvent::Touchpad(
3710                    touch_binding::TouchpadEvent {
3711                        injector_contacts: vec![
3712                            touch_binding::TouchContact {
3713                                id: 1u32,
3714                                position: Position { x: 2.0, y: 3.0 },
3715                                contact_size: None,
3716                                pressure: None,
3717                            },
3718                            touch_binding::TouchContact {
3719                                id: 2u32,
3720                                position: Position { x: 40.0, y: 50.0 },
3721                                contact_size: None,
3722                                pressure: None,
3723                            },
3724                        ],
3725                        pressed_buttons: hashset! {1},
3726                    },
3727                ),
3728                device_descriptor: touchpad_descriptor.clone(),
3729                event_time: zx::MonotonicInstant::from_nanos(12_300),
3730                trace_id: None,
3731                handled: input_device::Handled::No,
3732            });
3733            executor.set_fake_time(fasync::MonotonicInstant::from_nanos(10_000_000));
3734            gesture_matching_contender
3735                .set_next_result(ExamineEventResult::Contender(gesture_matching_contender.clone()));
3736            assert_matches!(
3737                executor.run_until_stalled(&mut handle_event_fut),
3738                std::task::Poll::Ready(_)
3739            );
3740
3741            // Process a handled key event.
3742            let mut handle_event_fut = arena.clone().handle_input_event(input_device::InputEvent {
3743                device_event: input_device::InputDeviceEvent::Keyboard(
3744                    keyboard_binding::KeyboardEvent::new(
3745                        fidl_fuchsia_input::Key::A,
3746                        fidl_fuchsia_ui_input3::KeyEventType::Pressed,
3747                    ),
3748                ),
3749                device_descriptor: keyboard_descriptor.clone(),
3750                event_time: zx::MonotonicInstant::from_nanos(11_000_000),
3751                trace_id: None,
3752                handled: input_device::Handled::Yes,
3753            });
3754            executor.set_fake_time(fasync::MonotonicInstant::from_nanos(12_000_000));
3755            assert_matches!(
3756                executor.run_until_stalled(&mut handle_event_fut),
3757                std::task::Poll::Ready(_)
3758            );
3759
3760            // Process an unhandled key event.
3761            let mut handle_event_fut = arena.clone().handle_input_event(input_device::InputEvent {
3762                device_event: input_device::InputDeviceEvent::Keyboard(
3763                    keyboard_binding::KeyboardEvent::new(
3764                        fidl_fuchsia_input::Key::B,
3765                        fidl_fuchsia_ui_input3::KeyEventType::Pressed,
3766                    ),
3767                ),
3768                device_descriptor: keyboard_descriptor,
3769                event_time: zx::MonotonicInstant::from_nanos(13_000_000),
3770                trace_id: None,
3771                handled: input_device::Handled::No,
3772            });
3773            executor.set_fake_time(fasync::MonotonicInstant::from_nanos(14_000_000));
3774            assert_matches!(
3775                executor.run_until_stalled(&mut handle_event_fut),
3776                std::task::Poll::Ready(_)
3777            );
3778
3779            // Process a touchpad event with width/height, and end the contest with a match.
3780            let mut handle_event_fut = arena.clone().handle_input_event(input_device::InputEvent {
3781                device_event: input_device::InputDeviceEvent::Touchpad(
3782                    touch_binding::TouchpadEvent {
3783                        injector_contacts: vec![touch_binding::TouchContact {
3784                            id: 1u32,
3785                            position: Position { x: 2.0, y: 3.0 },
3786                            contact_size: Some(Size { width: 3.0, height: 4.0 }),
3787                            pressure: None,
3788                        }],
3789                        pressed_buttons: hashset! {},
3790                    },
3791                ),
3792                device_descriptor: touchpad_descriptor.clone(),
3793                event_time: zx::MonotonicInstant::from_nanos(18_000_000),
3794                trace_id: None,
3795                handled: input_device::Handled::No,
3796            });
3797            let matched_contender = Box::new(StubMatchedContender::new());
3798            matched_contender.set_next_process_buffered_events_result(
3799                ProcessBufferedEventsResult {
3800                    generated_events: vec![],
3801                    winner: None,
3802                    recognized_gesture: RecognizedGesture::Motion,
3803                },
3804            );
3805            gesture_matching_contender
3806                .set_next_result(ExamineEventResult::MatchedContender(matched_contender));
3807            executor.set_fake_time(fasync::MonotonicInstant::from_nanos(19_000_000));
3808            assert_matches!(
3809                executor.run_until_stalled(&mut handle_event_fut),
3810                std::task::Poll::Ready(_)
3811            );
3812
3813            // Uncomment this block to generate a new example for the
3814            // documentation found at the bottom of this file.
3815            /*
3816            {
3817                use fuchsia_inspect::hierarchy::testing::JsonGetter;
3818                println!("{}", inspector.get_pretty_json());
3819            }
3820            */
3821
3822            diagnostics_assertions::assert_data_tree!(@executor executor, inspector, root: {
3823                gestures_event_log: {
3824                    "0": {
3825                        touchpad_event: {
3826                            driver_monotonic_nanos: 12_300i64,
3827                            entry_latency_micros: 9987i64,  // 10_000_000 - 12_300 = 9_987_700 nsec
3828                            pressed_buttons: vec![ 1u64 ],
3829                            contacts: {
3830                                "1": {
3831                                    pos_x_mm: 2.0,
3832                                    pos_y_mm: 3.0,
3833                                },
3834                                "2": {
3835                                    pos_x_mm: 40.0,
3836                                    pos_y_mm: 50.0,
3837                                },
3838                            },
3839                            filtered_palm_contacts: {},
3840                        }
3841                    },
3842                    "1": {
3843                        mismatch_event: {
3844                            contender: "utils::StubContender",
3845                            reason: "some reason",
3846                        }
3847                    },
3848                    "2": {
3849                        mismatch_event: {
3850                            contender: "utils::StubContender",
3851                            criterion: "num_goats_teleported",
3852                            min_allowed: 10u64,
3853                            max_allowed: 30u64,
3854                            actual: 42u64,
3855                        }
3856                    },
3857                    "3": {
3858                        mismatch_event: {
3859                            contender: "utils::StubContender",
3860                            criterion: "teleportation_distance_kilometers",
3861                            min_allowed: 10.125,
3862                            max_allowed: 30.5,
3863                            actual: 42.0,
3864                        }
3865                    },
3866                    "4": {
3867                        mismatch_event: {
3868                            contender: "utils::StubContender",
3869                            criterion: "budget_surplus_trillions",
3870                            min_allowed: -10i64,
3871                            max_allowed: 1i64,
3872                            actual: -42i64,
3873                        }
3874                    },
3875                    "5": {
3876                        key_event: {
3877                            driver_monotonic_nanos: 11_000_000i64,
3878                            entry_latency_micros: 1_000i64,  // 12_000_000 - 11_000_000 = 1_000_00 nsec
3879                        }
3880                    },
3881                    "6": {
3882                        key_event: {
3883                            driver_monotonic_nanos: 13_000_000i64,
3884                            entry_latency_micros: 1_000i64,  // 14_000_000 - 13_000_000 = 1_000_00 nsec
3885                        }
3886                    },
3887                    "7": {
3888                        touchpad_event: {
3889                            driver_monotonic_nanos: 18_000_000i64,
3890                            entry_latency_micros: 1_000i64,  // 19_000_000 - 18_000_000 = 1_000_00 nsec
3891                            pressed_buttons: Vec::<u64>::new(),
3892                            contacts: {
3893                                "1": {
3894                                    pos_x_mm: 2.0,
3895                                    pos_y_mm: 3.0,
3896                                    width_mm: 3.0,
3897                                    height_mm: 4.0,
3898                                },
3899                            },
3900                            filtered_palm_contacts: {},
3901                        }
3902                    },
3903                    "8": {
3904                        gesture_start: {
3905                          gesture_name: "motion",
3906                          latency_event_count: 1u64,
3907                          latency_micros: 17_987i64,  // 18_000_000 - 12_300 = 17_987_700
3908                        }
3909                    },
3910                    "9": {
3911                        gesture_end: {
3912                          gesture_name: "motion",
3913                          contender: "utils::StubMatchedContender",
3914                          event_count: 0u64,
3915                          duration_micros: 0i64,
3916                          reason: "discrete-recognizer",
3917                        }
3918                    }
3919                }
3920            });
3921        }
3922
3923        #[fuchsia::test(allow_stalls = false)]
3924        async fn negative_matching_latency_is_logged_correctly() {
3925            let inspector = fuchsia_inspect::Inspector::default();
3926            let gesture_matching_contender = Box::new(StubContender::new());
3927            let contender_factory = Box::new(ContenderFactoryOnceOrPanic::new(vec![
3928                gesture_matching_contender.clone(),
3929            ]));
3930            let arena = Rc::new(GestureArena::new_for_test(contender_factory, &inspector, 100));
3931
3932            gesture_matching_contender
3933                .set_next_result(ExamineEventResult::Contender(gesture_matching_contender.clone()));
3934            arena
3935                .clone()
3936                .handle_input_event(input_device::InputEvent {
3937                    event_time: zx::MonotonicInstant::from_nanos(15_000),
3938                    ..make_unhandled_touchpad_event()
3939                })
3940                .await;
3941
3942            let matched_contender = Box::new(StubMatchedContender::new());
3943            matched_contender.set_next_process_buffered_events_result(
3944                ProcessBufferedEventsResult {
3945                    generated_events: vec![],
3946                    winner: None,
3947                    recognized_gesture: RecognizedGesture::Motion,
3948                },
3949            );
3950            gesture_matching_contender
3951                .set_next_result(ExamineEventResult::MatchedContender(matched_contender));
3952            arena
3953                .clone()
3954                .handle_input_event(input_device::InputEvent {
3955                    event_time: zx::MonotonicInstant::from_nanos(6_000),
3956                    ..make_unhandled_touchpad_event()
3957                })
3958                .await;
3959
3960            diagnostics_assertions::assert_data_tree!(inspector, root: {
3961                gestures_event_log: {
3962                    "0": contains {},
3963                    "1": contains {},
3964                    "2": {
3965                        gesture_start: {
3966                          gesture_name: diagnostics_assertions::AnyProperty,
3967                          latency_event_count: 1u64,
3968                          latency_micros: -9i64,
3969                        }
3970                    },
3971                    "3": {
3972                        gesture_end: contains {}
3973                    },
3974                }
3975            })
3976        }
3977
3978        struct ContenderFactoryOnceThenEmpty {
3979            contenders: std::cell::Cell<Vec<Box<dyn Contender>>>,
3980        }
3981
3982        impl ContenderFactory for ContenderFactoryOnceThenEmpty {
3983            fn make_contenders(&self) -> Vec<Box<dyn Contender>> {
3984                self.contenders.take()
3985            }
3986        }
3987
3988        #[test_case(EndGestureEvent::NoEvent; "end_gesture_no_event")]
3989        #[test_case(EndGestureEvent::UnconsumedEvent(TouchpadEvent {
3990            timestamp: zx::MonotonicInstant::ZERO,
3991            pressed_buttons: vec![],
3992            contacts: vec![],
3993            filtered_palm_contacts: vec![],
3994        }); "end_gesture_unconsumed_event")]
3995        #[test_case(EndGestureEvent::GeneratedEvent(MouseEvent {
3996            timestamp: zx::MonotonicInstant::ZERO,
3997            mouse_data: mouse_binding::MouseEvent {
3998                location: mouse_binding::MouseLocation::Relative(
3999                    mouse_binding::RelativeLocation {
4000                        millimeters: Position::zero(),
4001                    },
4002                ),
4003                wheel_delta_v: None,
4004                wheel_delta_h: None,
4005                phase: mouse_binding::MousePhase::Move,
4006                affected_buttons: hashset! {},
4007                pressed_buttons: hashset! {},
4008                is_precision_scroll: None,
4009                    wake_lease: None.into(),
4010
4011            },
4012        }); "end_gesture_generated_event")]
4013        #[fuchsia::test(allow_stalls = false)]
4014        async fn multi_event_gesture_is_logged_correctly(end_gesture_event: EndGestureEvent) {
4015            // Set up the arena, and send the first touchpad event.
4016            let inspector = fuchsia_inspect::Inspector::default();
4017            let matching_contender = Box::new(StubContender::new());
4018            let arena = Rc::new(GestureArena::new_for_test(
4019                // In the `UnconsumedEvent` case, the gesture arena will try to start
4020                // a new contest. Hence, `ContenderFactoryOnceOrPanic` isn't appropriate
4021                // here. Nor is `ContenderFactoryOnceOrWarn`, since that would generate
4022                // a spurious warning, making this test harder to debug.
4023                Box::new(ContenderFactoryOnceThenEmpty {
4024                    contenders: std::cell::Cell::new(vec![matching_contender.clone()]),
4025                }),
4026                &inspector,
4027                100,
4028            ));
4029            matching_contender
4030                .set_next_result(ExamineEventResult::Contender(matching_contender.clone()));
4031            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
4032
4033            // Plumb things up for the next event to trigger the start of a multi-event gesture,
4034            // and send the next event.
4035            let matched_contender = Box::new(StubMatchedContender::new());
4036            let winner = Box::new(StubWinner::new());
4037            matching_contender
4038                .set_next_result(ExamineEventResult::MatchedContender(matched_contender.clone()));
4039            matched_contender.set_next_process_buffered_events_result(
4040                ProcessBufferedEventsResult {
4041                    generated_events: vec![],
4042                    winner: Some(winner.clone()),
4043                    recognized_gesture: RecognizedGesture::Motion,
4044                },
4045            );
4046            winner.set_next_result(ProcessNewEventResult::ContinueGesture(None, winner.clone()));
4047            arena
4048                .clone()
4049                .handle_input_event(input_device::InputEvent {
4050                    device_event: input_device::InputDeviceEvent::Touchpad(
4051                        touch_binding::TouchpadEvent {
4052                            injector_contacts: vec![],
4053                            pressed_buttons: hashset! {},
4054                        },
4055                    ),
4056                    device_descriptor: make_touchpad_descriptor(),
4057                    event_time: zx::MonotonicInstant::from_nanos(123_000),
4058                    trace_id: None,
4059                    handled: input_device::Handled::No,
4060                })
4061                .await;
4062
4063            // Send another event as part of the gesture.
4064            winner.set_next_result(ProcessNewEventResult::ContinueGesture(None, winner.clone()));
4065            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
4066
4067            // Plumb things to end the gesture on the next event, and send that event.
4068            winner.set_next_result(ProcessNewEventResult::EndGesture(
4069                end_gesture_event,
4070                Reason::DetailedUint(DetailedReasonUint {
4071                    criterion: "num_goats_teleported",
4072                    min: Some(10),
4073                    max: Some(30),
4074                    actual: 42,
4075                }),
4076            ));
4077            arena
4078                .clone()
4079                .handle_input_event(input_device::InputEvent {
4080                    device_event: input_device::InputDeviceEvent::Touchpad(
4081                        touch_binding::TouchpadEvent {
4082                            injector_contacts: vec![],
4083                            pressed_buttons: hashset! {},
4084                        },
4085                    ),
4086                    device_descriptor: make_touchpad_descriptor(),
4087                    event_time: zx::MonotonicInstant::from_nanos(456_000),
4088                    trace_id: None,
4089                    handled: input_device::Handled::No,
4090                })
4091                .await;
4092
4093            diagnostics_assertions::assert_data_tree!(inspector, root: {
4094                gestures_event_log: {
4095                    "0": { touchpad_event: contains {} },
4096                    "1": { touchpad_event: contains {} },
4097                    "2": { gesture_start: contains {} },
4098                    "3": { touchpad_event: contains {} },
4099                    "4": { touchpad_event: contains {} },
4100                    "5": {
4101                        gesture_end: contains {
4102                            gesture_name: "motion",
4103                            contender: "utils::StubWinner",
4104                            criterion: "num_goats_teleported",
4105                            min_allowed: 10u64,
4106                            max_allowed: 30u64,
4107                            actual: 42u64,
4108                            duration_micros: 333i64, // 456_000 - 123_000 = 333_000 nsec
4109                            event_count: 2u64,
4110                        },
4111                    },
4112                }
4113            })
4114        }
4115
4116        #[fuchsia::test(allow_stalls = false)]
4117        async fn retains_latest_events_up_to_cap() {
4118            let inspector = fuchsia_inspect::Inspector::default();
4119            let arena = Rc::new(GestureArena::new_for_test(
4120                Box::new(EmptyContenderFactory {}),
4121                &inspector,
4122                2,
4123            ));
4124            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await; // 0
4125            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await; // 1
4126            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await; // 2
4127            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await; // 3
4128            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await; // 4
4129            diagnostics_assertions::assert_data_tree!(inspector, root: {
4130                gestures_event_log: {
4131                    "3": contains {},
4132                    "4": contains {},
4133                }
4134            })
4135        }
4136
4137        #[fuchsia::test]
4138        fn retains_palm_contacts() {
4139            let mut executor = fasync::TestExecutor::new_with_fake_time();
4140            let inspector = fuchsia_inspect::Inspector::default();
4141            let arena = Rc::new(GestureArena::new_for_test(
4142                Box::new(EmptyContenderFactory {}),
4143                &inspector,
4144                2,
4145            ));
4146            let mut handle_event_fut = arena.clone().handle_input_event(input_device::InputEvent {
4147                device_event: input_device::InputDeviceEvent::Touchpad(
4148                    touch_binding::TouchpadEvent {
4149                        injector_contacts: vec![touch_binding::TouchContact {
4150                            id: 1u32,
4151                            position: Position { x: 2_000.0, y: 1_000.0 },
4152                            contact_size: Some(Size {
4153                                width: (args::MIN_PALM_SIZE_MM + 0.1) * 1_000.0,
4154                                height: 4_000.0,
4155                            }),
4156                            pressure: None,
4157                        }],
4158                        pressed_buttons: hashset! {},
4159                    },
4160                ),
4161                device_descriptor: make_touchpad_descriptor(),
4162                event_time: zx::MonotonicInstant::ZERO,
4163                trace_id: None,
4164                handled: input_device::Handled::No,
4165            });
4166            executor.set_fake_time(fasync::MonotonicInstant::from_nanos(1_000_000));
4167            assert_matches!(
4168                executor.run_until_stalled(&mut handle_event_fut),
4169                std::task::Poll::Ready(_)
4170            );
4171            diagnostics_assertions::assert_data_tree!(@executor executor, inspector, root: {
4172                gestures_event_log: {
4173                    "0": {
4174                        touchpad_event: {
4175                            driver_monotonic_nanos: 0i64,
4176                            entry_latency_micros: 1_000i64,
4177                            pressed_buttons: Vec::<u64>::new(),
4178                            contacts: {},
4179                            filtered_palm_contacts: {
4180                                "1": {
4181                                    pos_x_mm: 2.0,
4182                                    pos_y_mm: 1.0,
4183                                    width_mm: (args::MIN_PALM_SIZE_MM + 0.1) as f64,
4184                                    height_mm: 4.0,
4185                                },
4186                            },
4187                        },
4188                    },
4189                },
4190            });
4191        }
4192    }
4193}
4194
4195// Example JSON dump of inspect tree generated by `gesture_arena`, from a unit test:
4196//
4197// ```json
4198// {
4199//   "root": {
4200//     "gestures_event_log": {
4201//       "0": {
4202//         "touchpad_event": {
4203//           "driver_monotonic_nanos": 12300,
4204//           "entry_latency_micros": 9987,
4205//           "pressed_buttons": [
4206//             1
4207//           ],
4208//           "contacts": {
4209//             "1": {
4210//               "pos_x_mm": 2.0,
4211//               "pos_y_mm": 3.0
4212//             },
4213//             "2": {
4214//               "pos_x_mm": 40.0,
4215//               "pos_y_mm": 50.0
4216//             }
4217//           }
4218//         }
4219//       },
4220//       "1": {
4221//         "mismatch_event": {
4222//           "contender": "utils::StubContender",
4223//           "reason": "some reason"
4224//         }
4225//       },
4226//       "2": {
4227//         "mismatch_event": {
4228//           "actual": 42,
4229//           "contender": "utils::StubContender",
4230//           "criterion": "num_goats_teleported",
4231//           "max_allowed": 30,
4232//           "min_allowed": 10
4233//         }
4234//       },
4235//       "3": {
4236//         "mismatch_event": {
4237//           "actual": 42.0,
4238//           "contender": "utils::StubContender",
4239//           "criterion": "teleportation_distance_kilometers",
4240//           "max_allowed": 30.5,
4241//           "min_allowed": 10.125
4242//         }
4243//       },
4244//       "4": {
4245//         "mismatch_event": {
4246//           "actual": -42,
4247//           "contender": "utils::StubContender",
4248//           "criterion": "budget_surplus_trillions",
4249//           "max_allowed": 1,
4250//           "min_allowed": -10
4251//         }
4252//       },
4253//       "5": {
4254//         "key_event": {
4255//           "driver_monotonic_nanos": 11000000,
4256//           "entry_latency_micros": 1000
4257//         }
4258//       },
4259//       "6": {
4260//         "key_event": {
4261//           "driver_monotonic_nanos": 13000000,
4262//           "entry_latency_micros": 1000
4263//         }
4264//       },
4265//       "7": {
4266//         "touchpad_event": {
4267//           "driver_monotonic_nanos": 18000000,
4268//           "entry_latency_micros": 1000,
4269//           "pressed_buttons": [],
4270//           "contacts": {
4271//             "1": {
4272//               "height_mm": 4.0,
4273//               "pos_x_mm": 2.0,
4274//               "pos_y_mm": 3.0,
4275//               "width_mm": 3.0
4276//             }
4277//           }
4278//         }
4279//       },
4280//       "8": {
4281//         "gesture_start": {
4282//           "gesture_name": "click",
4283//           "latency_event_count": 1,
4284//           "latency_micros": 17987
4285//         }
4286//       },
4287//       "9": {
4288//         "gesture_end": {
4289//           "contender": "utils::StubMatchedContender",
4290//           "duration_micros": 0,
4291//           "event_count": 0,
4292//           "gesture_name": "click",
4293//           "reason": "discrete-recognizer"
4294//         }
4295//       }
4296//     }
4297//   }
4298// }
4299// ```
4300
4301// Example `iquery` excerpt from a live device:
4302//
4303// ```json5
4304// core/ui/scene_manager:
4305//   metadata:
4306//     filename = fuchsia.inspect.Tree
4307//     component_url = fuchsia-pkg://fuchsia.com/scene_manager#meta/scene_manager.cm
4308//     timestamp = 375999103371
4309//   payload:
4310//     root:
4311//       fuchsia.inspect.Stats:
4312//         allocated_blocks = 9998
4313//         current_size = 163840
4314//         deallocated_blocks = 0
4315//         failed_allocations = 0
4316//         maximum_size = 307200
4317//         total_dynamic_children = 1
4318//       input_pipeline:
4319//         gestures_event_log:
4320//           0:
4321//             key_event:
4322//               driver_monotonic_nanos = 297873226402
4323//               entry_latency_micros = 32908
4324//           1:
4325//             key_event:
4326//               driver_monotonic_nanos = 297955554861
4327//               entry_latency_micros = 1403
4328//           /* ...many entries omitted... */
4329//           150:
4330//             touchpad_event:
4331//               driver_monotonic_nanos = 361816423302
4332//               entry_latency_micros = 14432
4333//               pressed_buttons = []
4334//               contacts:
4335//                 0:
4336//                   height_mm = 2.5840000
4337//                   pos_x_mm = 26.528000
4338//                   pos_y_mm = 23.712999
4339//                   width_mm = 2.9530000
4340//           /* mismatches on u64 properties */
4341//           151:
4342//             mismatch_event:
4343//               actual = 0
4344//               contender = one_finger_drag::InitialContender
4345//               criterion = num_pressed_buttons
4346//               max_allowed = 1
4347//               min_allowed = 1
4348//           152:
4349//             mismatch_event:
4350//               actual = 1
4351//               contender = scroll::InitialContender
4352//               criterion = num_contacts
4353//               max_allowed = 2
4354//               min_allowed = 2
4355//           /* ... many entries omitted ... */
4356//           159:
4357//             touchpad_event:
4358//               driver_monotonic_nanos = 361871136901
4359//               entry_latency_micros = 4745
4360//               pressed_buttons = []
4361//               contacts:
4362//                 0:
4363//                   height_mm = 2.5840000
4364//                   pos_x_mm = 27.162001
4365//                   pos_y_mm = 24.061001
4366//                   width_mm = 2.9530000
4367//           /* mismatches on float properties */
4368//           160:
4369//             mismatch_event:
4370//               actual = 0.723230
4371//               contender = click::UnpressedContender
4372//               criterion = displacement_mm
4373//               max_allowed = 0.500000
4374//           161:
4375//             mismatch_event:
4376//               actual = 0.723230
4377//               contender = primary_tap::FingerContactContender
4378//               criterion = displacement_mm
4379//               max_allowed = 0.500000
4380//           162:
4381//             mismatch_event:
4382//               actual = 0.723230
4383//               contender = secondary_tap::OneFingerContactContender
4384//               criterion = displacement_mm
4385//               max_allowed = 0.500000
4386//           /* gesture start */
4387//           163:
4388//             gesture_start:
4389//               gesture_name = motion
4390//               latency_event_count = 7
4391//               latency_micros = 54713
4392//           /* ... many entries omitted ... */
4393//           295:
4394//             touchpad_event:
4395//               driver_monotonic_nanos = 362903529428
4396//               entry_latency_micros = 3603
4397//               pressed_buttons = []
4398//               contacts:
4399//           /* gesture end */
4400//           296:
4401//             gesture_end:
4402//               actual = 0
4403//               contender = motion::Winner
4404//               criterion = num_contacts
4405//               duration_micros = 1032392
4406//               event_count = 132
4407//               gesture_name = motion
4408//               max_allowed = 1
4409//               min_allowed = 1
4410//           /* ... many entries omitted ... */
4411//           596:
4412//             touchpad_event:
4413//               driver_monotonic_nanos = 370902306135
4414//               entry_latency_micros = 4630
4415//               pressed_buttons = []
4416//               contacts:
4417//                 0:
4418//                   height_mm = 2.5840000
4419//                   pos_x_mm = 76.887001
4420//                   pos_y_mm = 25.962999
4421//                   width_mm = 2.9530000
4422//           /* ... many entries omitted ... */
4423//           752:
4424//             touchpad_event:
4425//               driver_monotonic_nanos = 372106779670
4426//               entry_latency_micros = 4607
4427//               pressed_buttons = []
4428//               contacts:
4429//                 0:
4430//                   height_mm = 3.2300000
4431//                   pos_x_mm = 76.949997
4432//                   pos_y_mm = 26.184999
4433//                   width_mm = 2.9530000
4434//           /* mismatches on i64 properties */
4435//           753:
4436//             mismatch_event:
4437//               actual = 1204473
4438//               contender = primary_tap::FingerContactContender
4439//               criterion = elapsed_time_micros
4440//               max_allowed = 1200000
4441//           754:
4442//             mismatch_event:
4443//               actual = 1204473
4444//               contender = secondary_tap::OneFingerContactContender
4445//               criterion = elapsed_time_micros
4446//               max_allowed = 1200000
4447// ```