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