1use super::{
6    args, motion, one_finger_button, primary_tap, scroll, secondary_button, secondary_tap,
7};
8use crate::input_handler::{InputHandler, InputHandlerStatus};
9use crate::utils::Size;
10use crate::{input_device, mouse_binding, touch_binding};
11use anyhow::{Context, Error, format_err};
12use async_trait::async_trait;
13use core::cell::RefCell;
14use fidl_fuchsia_input_report as fidl_input_report;
15use fuchsia_inspect::health::Reporter;
16use fuchsia_inspect::{ArrayProperty, Node as InspectNode};
17use fuchsia_inspect_contrib::nodes::BoundedListNode;
18use std::any::Any;
19use std::fmt::Debug;
20
21struct GestureArenaInitialContenders {}
22
23impl ContenderFactory for GestureArenaInitialContenders {
24    fn make_contenders(&self) -> Vec<Box<dyn Contender>> {
25        vec![
26            Box::new(motion::InitialContender {
27                min_movement_in_mm: args::SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_MM,
28            }),
29            Box::new(primary_tap::InitialContender {
30                max_finger_displacement_in_mm: args::SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_MM,
31                max_time_elapsed: args::TAP_TIMEOUT,
32            }),
33            Box::new(secondary_tap::InitialContender {
34                max_finger_displacement_in_mm: args::SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_MM,
35                max_time_elapsed: args::TAP_TIMEOUT,
36            }),
37            Box::new(scroll::InitialContender {
38                motion_threshold_in_mm: args::SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_MM,
39                min_movement_in_mm: args::SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_MM,
40                max_movement_in_mm: args::MAX_SPURIOUS_TO_INTENTIONAL_SCROLL_THRESHOLD_MM,
41                limit_tangent_for_direction: args::MAX_SCROLL_DIRECTION_SKEW_DEGREES
42                    .to_radians()
43                    .tan(),
44            }),
45            Box::new(one_finger_button::InitialContender {
46                spurious_to_intentional_motion_threshold_mm:
47                    args::SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_MM,
48                spurious_to_intentional_motion_threshold_button_change_mm:
49                    args::SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_BUTTON_CHANGE_MM,
50                button_change_state_timeout: args::BUTTON_CHANGE_STATE_TIMEOUT,
51            }),
52            Box::new(secondary_button::InitialContender {
53                spurious_to_intentional_motion_threshold_mm:
54                    args::SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_MM,
55                spurious_to_intentional_motion_threshold_button_change_mm:
56                    args::SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_BUTTON_CHANGE_MM,
57                button_change_state_timeout: args::BUTTON_CHANGE_STATE_TIMEOUT,
58            }),
59        ]
60    }
61}
62
63pub fn make_input_handler(
64    inspect_node: &InspectNode,
65    input_handlers_node: &InspectNode,
66) -> std::rc::Rc<dyn crate::input_handler::InputHandler> {
67    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#[derive(Debug, Clone, PartialEq)]
83pub(super) struct TouchpadEvent {
84    pub(super) timestamp: zx::MonotonicInstant,
85    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    fn examine_event(self: Box<Self>, event: &TouchpadEvent) -> ExamineEventResult;
150
151    fn get_type_name(&self) -> &'static str {
154        std::any::type_name::<Self>()
155    }
156
157    fn start_from_idle(&self) -> bool {
159        false
160    }
161}
162
163pub trait AsAny {
164    #[allow(dead_code)] 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    _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, }
200
201pub(super) trait MatchedContender: std::fmt::Debug + AsAny {
202    fn verify_event(self: Box<Self>, event: &TouchpadEvent) -> VerifyEventResult;
211
212    fn process_buffered_events(
234        self: Box<Self>,
235        events: Vec<TouchpadEvent>,
236    ) -> ProcessBufferedEventsResult;
237
238    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    #[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    fn process_new_event(self: Box<Self>, event: TouchpadEvent) -> ProcessNewEventResult;
281
282    fn get_type_name(&self) -> &'static str {
285        std::any::type_name::<Self>()
286    }
287}
288
289const MAX_TOUCHPAD_EVENT_LOG_ENTRIES: usize = 1250; #[derive(Debug)]
292enum MutableState {
293    Idle,
295
296    Chain,
308
309    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    Forwarding {
319        winner: Box<dyn Winner>,
320        current_gesture: RecognizedGesture,
321        gesture_start_timestamp: zx::MonotonicInstant,
322        num_events: usize,
323    },
324
325    Invalid,
327}
328
329#[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    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            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        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        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        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        (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    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            device_event: input_device::InputDeviceEvent::Mouse(mouse_event.mouse_data),
540            device_descriptor: input_device::InputDeviceDescriptor::Mouse(
541                mouse_binding::MouseDeviceDescriptor {
542                    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                        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                    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    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        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        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            (0, 0) => {
797                if has_finger_contact {
798                    (MutableState::Chain, vec![])
803                } else {
804                    (MutableState::Idle, vec![])
805                }
806            }
807            (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            (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)] 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)] fn log_mutable_state(&self) {
1014        log::info!("{}", self.mutable_state_to_str());
1015    }
1016}
1017
1018#[async_trait(?Send)]
1019impl InputHandler for GestureArena {
1020    async fn handle_input_event(
1025        self: std::rc::Rc<Self>,
1026        input_event: input_device::InputEvent,
1027    ) -> Vec<input_device::InputEvent> {
1028        match input_event {
1029            input_device::InputEvent {
1030                device_event: input_device::InputDeviceEvent::Touchpad(ref touchpad_event),
1031                ref event_time,
1032                device_descriptor:
1033                    input_device::InputDeviceDescriptor::Touchpad(ref touchpad_descriptor),
1034                handled: input_device::Handled::No,
1035                ..
1036            } => {
1037                self.inspect_status.count_received_event(&event_time);
1038                match self.handle_touchpad_event(event_time, touchpad_event, touchpad_descriptor) {
1039                    Ok(r) => r,
1040                    Err(e) => {
1041                        log::error!("{}", e);
1042                        vec![input_event]
1043                    }
1044                }
1045            }
1046            input_device::InputEvent {
1047                device_event: input_device::InputDeviceEvent::Keyboard(_),
1048                event_time,
1049                ..
1050            } => {
1051                self.inspect_log.borrow_mut().add_entry(|node| {
1052                    log_keyboard_event_timestamp(node, event_time);
1053                });
1054                vec![input_event]
1055            }
1056            _ => {
1057                vec![input_event]
1058            }
1059        }
1060    }
1061
1062    fn set_handler_healthy(self: std::rc::Rc<Self>) {
1063        self.inspect_status.health_node.borrow_mut().set_ok();
1064    }
1065
1066    fn set_handler_unhealthy(self: std::rc::Rc<Self>, msg: &str) {
1067        self.inspect_status.health_node.borrow_mut().set_unhealthy(msg);
1068    }
1069}
1070
1071fn get_position_divisor_to_mm(
1078    touchpad_descriptor: &touch_binding::TouchpadDeviceDescriptor,
1079) -> Result<f32, Error> {
1080    const EXPONENT_MILLIS: i32 = -3;
1081    let divisors =
1082        touchpad_descriptor.contacts.iter().enumerate().map(|(i, contact_descriptor)| {
1083            match (contact_descriptor.x_unit, contact_descriptor.y_unit) {
1084                (
1085                    fidl_input_report::Unit {
1086                        type_: fidl_input_report::UnitType::Meters,
1087                        exponent: exponent_x,
1088                    },
1089                    fidl_input_report::Unit {
1090                        type_: fidl_input_report::UnitType::Meters,
1091                        exponent: exponent_y,
1092                    },
1093                ) => {
1094                    if exponent_x == exponent_y {
1095                        Ok(f32::powi(10.0, EXPONENT_MILLIS - exponent_x))
1096                    } else {
1097                        Err(format!(
1098                            "contact {}: mismatched exponents x={}, y={}",
1099                            i, exponent_x, exponent_y
1100                        ))
1101                    }
1102                }
1103                (
1104                    fidl_input_report::Unit { type_: x_unit_type, .. },
1105                    fidl_input_report::Unit { type_: fidl_input_report::UnitType::Meters, .. },
1106                ) => Err(format!(
1107                    "contact {}: expected x-unit-type of Meters but got {:?}",
1108                    i, x_unit_type
1109                )),
1110                (
1111                    fidl_input_report::Unit { type_: fidl_input_report::UnitType::Meters, .. },
1112                    fidl_input_report::Unit { type_: y_unit_type, .. },
1113                ) => Err(format!(
1114                    "contact {}: expected y-unit-type of Meters but got {:?}",
1115                    i, y_unit_type
1116                )),
1117                (
1118                    fidl_input_report::Unit { type_: x_unit_type, .. },
1119                    fidl_input_report::Unit { type_: y_unit_type, .. },
1120                ) => Err(format!(
1121                    "contact {}: expected x and y unit-types of Meters but got x={:?} and y={:?}",
1122                    i, x_unit_type, y_unit_type
1123                )),
1124            }
1125        });
1126
1127    let (divisors, errors): (Vec<_>, Vec<_>) =
1128        divisors.fold((vec![], vec![]), |(mut divisors, mut errors), divisor| {
1129            match divisor {
1130                Ok(d) => divisors.push(d),
1131                Err(e) => errors.push(e),
1132            };
1133            (divisors, errors)
1134        });
1135
1136    if !errors.is_empty() {
1137        return Err(format_err!(
1138            errors.into_iter().fold(String::new(), |prev_err_msgs, this_err_msg| prev_err_msgs
1139                + &this_err_msg
1140                + ", ")
1141        ));
1142    }
1143
1144    let first_divisor = match divisors.first() {
1145        Some(&divisor) => divisor,
1146        None => return Err(format_err!("no contact descriptors!")),
1147    };
1148
1149    if divisors.iter().any(|&divisor| divisor != first_divisor) {
1150        return Err(format_err!(
1151            divisors
1152                .iter()
1153                .enumerate()
1154                .filter(|(_i, &divisor)| divisor != first_divisor)
1155                .map(|(i, divisor)| format!(
1156                    "contact {} has a different divisor than the first contact ({:?} != {:?})",
1157                    i, divisor, first_divisor,
1158                ))
1159                .fold(String::new(), |prev_err_msgs, this_err_msg| prev_err_msgs
1160                    + &this_err_msg
1161                    + ", ")
1162        ));
1163    }
1164
1165    Ok(first_divisor)
1166}
1167
1168fn log_keyboard_event_timestamp(
1169    log_entry_node: &InspectNode,
1170    driver_timestamp: zx::MonotonicInstant,
1171) {
1172    log_entry_node.record_child("key_event", |key_event_node| {
1173        log_common(key_event_node, driver_timestamp);
1174    });
1175}
1176
1177fn log_basic_reason(reason_node: &InspectNode, text: &'static str) {
1178    reason_node.record_string("reason", text);
1179}
1180
1181fn log_detailed_reason_uint(reason_node: &InspectNode, reason_details: DetailedReasonUint) {
1182    reason_node.record_string("criterion", reason_details.criterion);
1183    reason_node.record_uint("actual", u64::try_from(reason_details.actual).unwrap_or(u64::MAX));
1184    reason_details.min.map(|min| reason_node.record_uint("min_allowed", min));
1185    reason_details.max.map(|max| reason_node.record_uint("max_allowed", max));
1186}
1187
1188fn log_detailed_reason_float(reason_node: &InspectNode, reason_details: DetailedReasonFloat) {
1189    reason_node.record_string("criterion", reason_details.criterion);
1190    reason_node.record_double("actual", f64::from(reason_details.actual));
1191    reason_details.min.map(|min| reason_node.record_double("min_allowed", f64::from(min)));
1192    reason_details.max.map(|max| reason_node.record_double("max_allowed", f64::from(max)));
1193}
1194
1195fn log_detailed_reason_int(reason_node: &InspectNode, reason_details: DetailedReasonInt) {
1196    reason_node.record_string("criterion", reason_details.criterion);
1197    reason_node.record_int("actual", reason_details.actual);
1198    reason_details.min.map(|min| reason_node.record_int("min_allowed", min));
1199    reason_details.max.map(|max| reason_node.record_int("max_allowed", max));
1200}
1201
1202fn log_reason(reason_node: &InspectNode, contender_name: &'static str, reason: Reason) {
1203    let contender_name = match contender_name.rmatch_indices("::").nth(1) {
1206        Some((i, _matched_substr)) => &contender_name[i + 2..],
1207        None => contender_name,
1208    };
1209    reason_node.record_string("contender", contender_name);
1210    match reason {
1211        Reason::Basic(text) => log_basic_reason(reason_node, text),
1212        Reason::DetailedUint(mismatch_details) => {
1213            log_detailed_reason_uint(reason_node, mismatch_details)
1214        }
1215        Reason::DetailedFloat(mismatch_details) => {
1216            log_detailed_reason_float(reason_node, mismatch_details)
1217        }
1218        Reason::DetailedInt(mismatch_details) => {
1219            log_detailed_reason_int(reason_node, mismatch_details)
1220        }
1221    }
1222}
1223
1224fn log_mismatch_reason(log_entry_node: &InspectNode, contender_name: &'static str, reason: Reason) {
1225    log::debug!("touchpad: {} mismatched: {:?}", contender_name, reason);
1226    log_entry_node.record_child("mismatch_event", |mismatch_event_node| {
1227        log_reason(mismatch_event_node, contender_name, reason);
1228    });
1229}
1230
1231fn log_gesture_start(
1232    log_entry_node: &InspectNode,
1233    recognized_gesture: RecognizedGesture,
1234    num_previous_events: usize,
1235    elapsed_from_first_event: zx::MonotonicDuration,
1236) {
1237    log::debug!("touchpad: recognized start {:?}", recognized_gesture);
1238    log_entry_node.record_child("gesture_start", |gesture_start_node| {
1239        gesture_start_node.record_string("gesture_name", recognized_gesture.to_str());
1240        gesture_start_node.record_int(
1241            "latency_micros",
1242            elapsed_from_first_event.into_micros(),
1244        );
1245        gesture_start_node.record_uint(
1246            "latency_event_count",
1247            u64::try_from(num_previous_events).unwrap_or(u64::MAX),
1248        );
1249    });
1250}
1251
1252fn log_gesture_end(
1253    log_entry_node: &InspectNode,
1254    contender_name: &'static str,
1255    current_gesture: RecognizedGesture,
1256    reason: Reason,
1257    num_events: usize,
1258    elapsed_from_gesture_start: zx::MonotonicDuration,
1259) {
1260    log::debug!("touchpad: recognized end {:?}", current_gesture);
1261    log_entry_node.record_child("gesture_end", |gesture_end_node| {
1262        gesture_end_node.record_string("gesture_name", current_gesture.to_str());
1263        gesture_end_node.record_int(
1264            "duration_micros",
1265            elapsed_from_gesture_start.into_micros(),
1267        );
1268        gesture_end_node.record_uint("event_count", u64::try_from(num_events).unwrap_or(u64::MAX));
1269        log_reason(gesture_end_node, contender_name, reason)
1270    });
1271}
1272
1273#[cfg(test)]
1274mod tests {
1275    mod utils {
1276        use super::super::{
1277            COUNTS_PER_MM, Contender, ContenderFactory, ExamineEventResult, MatchedContender,
1278            PRIMARY_BUTTON, ProcessBufferedEventsResult, ProcessNewEventResult, TouchpadEvent,
1279            VerifyEventResult, Winner, args,
1280        };
1281        use crate::utils::Size;
1282        use crate::{Position, input_device, keyboard_binding, mouse_binding, touch_binding};
1283        use assert_matches::assert_matches;
1284        use fidl_fuchsia_input_report as fidl_input_report;
1285        use maplit::hashset;
1286        use std::cell::{Cell, RefCell};
1287        use std::rc::Rc;
1288
1289        pub(super) fn make_unhandled_touchpad_event() -> input_device::InputEvent {
1292            input_device::InputEvent {
1293                device_event: input_device::InputDeviceEvent::Touchpad(
1294                    touch_binding::TouchpadEvent {
1295                        injector_contacts: vec![],
1296                        pressed_buttons: hashset! {},
1297                    },
1298                ),
1299                device_descriptor: make_touchpad_descriptor(),
1300                event_time: zx::MonotonicInstant::ZERO,
1301                trace_id: None,
1302                handled: input_device::Handled::No,
1303            }
1304        }
1305
1306        pub(super) fn make_unhandled_mouse_event() -> input_device::InputEvent {
1309            input_device::InputEvent {
1310                device_event: input_device::InputDeviceEvent::Mouse(mouse_binding::MouseEvent {
1311                    location: mouse_binding::MouseLocation::Relative(
1312                        mouse_binding::RelativeLocation { millimeters: Position::zero() },
1313                    ),
1314                    wheel_delta_h: None,
1315                    wheel_delta_v: None,
1316                    phase: mouse_binding::MousePhase::Move,
1317                    affected_buttons: hashset! {},
1318                    pressed_buttons: hashset! {},
1319                    is_precision_scroll: None,
1320                }),
1321                device_descriptor: make_mouse_descriptor(),
1322                event_time: zx::MonotonicInstant::ZERO,
1323                trace_id: None,
1324                handled: input_device::Handled::No,
1325            }
1326        }
1327
1328        pub(super) fn make_unhandled_keyboard_event() -> input_device::InputEvent {
1331            input_device::InputEvent {
1332                device_event: input_device::InputDeviceEvent::Keyboard(
1333                    keyboard_binding::KeyboardEvent::new(
1334                        fidl_fuchsia_input::Key::A,
1335                        fidl_fuchsia_ui_input3::KeyEventType::Pressed,
1336                    ),
1337                ),
1338                device_descriptor: make_keyboard_descriptor(),
1339                event_time: zx::MonotonicInstant::ZERO,
1340                trace_id: None,
1341                handled: input_device::Handled::No,
1342            }
1343        }
1344
1345        pub(super) fn make_touchpad_descriptor() -> input_device::InputDeviceDescriptor {
1346            input_device::InputDeviceDescriptor::Touchpad(touch_binding::TouchpadDeviceDescriptor {
1347                device_id: 1,
1348                contacts: vec![touch_binding::ContactDeviceDescriptor {
1349                    x_range: fidl_input_report::Range { min: 0, max: 10_000 },
1350                    y_range: fidl_input_report::Range { min: 0, max: 10_000 },
1351                    x_unit: fidl_input_report::Unit {
1352                        type_: fidl_input_report::UnitType::Meters,
1353                        exponent: -6,
1354                    },
1355                    y_unit: fidl_input_report::Unit {
1356                        type_: fidl_input_report::UnitType::Meters,
1357                        exponent: -6,
1358                    },
1359                    pressure_range: None,
1360                    width_range: Some(fidl_input_report::Range { min: 0, max: 10_000 }),
1361                    height_range: Some(fidl_input_report::Range { min: 0, max: 10_000 }),
1362                }],
1363            })
1364        }
1365
1366        pub(super) fn make_mouse_descriptor() -> input_device::InputDeviceDescriptor {
1367            input_device::InputDeviceDescriptor::Mouse(mouse_binding::MouseDeviceDescriptor {
1368                device_id: 2,
1369                absolute_x_range: Some(fidl_input_report::Range { min: -127, max: 127 }),
1370                absolute_y_range: Some(fidl_input_report::Range { min: -127, max: 127 }),
1371                wheel_v_range: None,
1372                wheel_h_range: None,
1373                buttons: Some(vec![PRIMARY_BUTTON]),
1374                counts_per_mm: COUNTS_PER_MM,
1375            })
1376        }
1377
1378        fn make_keyboard_descriptor() -> input_device::InputDeviceDescriptor {
1379            input_device::InputDeviceDescriptor::Keyboard(
1380                keyboard_binding::KeyboardDeviceDescriptor {
1381                    device_id: 3,
1382                    device_information: fidl_fuchsia_input_report::DeviceInformation {
1383                        vendor_id: Some(0),
1384                        product_id: Some(0),
1385                        version: Some(0),
1386                        polling_rate: Some(0),
1387                        ..Default::default()
1388                    },
1389                    keys: vec![fidl_fuchsia_input::Key::A],
1390                },
1391            )
1392        }
1393
1394        #[derive(Clone, Debug)]
1395        pub(super) struct StubContender {
1405            inner: Rc<RefCell<StubContenderInner>>,
1406            start_from_idle: bool,
1407        }
1408
1409        impl StubContender {
1410            pub(super) fn new() -> Self {
1411                Self {
1412                    inner: Rc::new(RefCell::new(StubContenderInner {
1413                        next_result: None,
1414                        calls_received: 0,
1415                        last_touchpad_event: None,
1416                    })),
1417                    start_from_idle: false,
1418                }
1419            }
1420
1421            pub(super) fn new_start_from_idle() -> Self {
1422                Self {
1423                    inner: Rc::new(RefCell::new(StubContenderInner {
1424                        next_result: None,
1425                        calls_received: 0,
1426                        last_touchpad_event: None,
1427                    })),
1428                    start_from_idle: true,
1429                }
1430            }
1431
1432            pub(super) fn set_next_result(&self, next_result: ExamineEventResult) {
1436                self.assert_next_result_is_none();
1437                self.inner.borrow_mut().next_result = Some(next_result);
1438            }
1439
1440            pub(super) fn assert_next_result_is_none(&self) {
1441                assert_matches!(self.inner.borrow().next_result, None);
1442            }
1443
1444            pub(super) fn calls_received(&self) -> usize {
1445                self.inner.borrow().calls_received
1446            }
1447
1448            pub(super) fn ref_count(&self) -> usize {
1449                Rc::strong_count(&self.inner)
1450            }
1451
1452            pub(super) fn get_last_touchpad_event(&self) -> Option<TouchpadEvent> {
1453                self.inner.borrow_mut().last_touchpad_event.take()
1454            }
1455        }
1456
1457        #[derive(Debug)]
1458        struct StubContenderInner {
1459            next_result: Option<ExamineEventResult>,
1460            calls_received: usize,
1461            last_touchpad_event: Option<TouchpadEvent>,
1462        }
1463
1464        pub(super) struct ContenderFactoryOnceOrPanic {
1475            contenders: Cell<Option<Vec<Box<dyn Contender>>>>,
1476        }
1477
1478        impl ContenderFactoryOnceOrPanic {
1479            pub(super) fn new(contenders: Vec<Box<dyn Contender>>) -> Self {
1480                Self { contenders: Cell::new(Some(contenders)) }
1481            }
1482
1483            pub(super) fn new_panic_when_call() -> Self {
1486                Self { contenders: Cell::new(None) }
1487            }
1488        }
1489
1490        impl ContenderFactory for ContenderFactoryOnceOrPanic {
1491            fn make_contenders(&self) -> Vec<Box<dyn Contender>> {
1492                self.contenders
1493                    .take()
1494                    .expect("`contenders` has been consumed")
1495                    .into_iter()
1496                    .collect()
1497            }
1498        }
1499
1500        impl Contender for StubContender {
1501            fn examine_event(self: Box<Self>, event: &TouchpadEvent) -> ExamineEventResult {
1502                let mut inner = self.inner.borrow_mut();
1503                inner.calls_received += 1;
1504                inner.last_touchpad_event = Some(event.clone());
1505                inner.next_result.take().unwrap_or_else(|| {
1506                    panic!("missing `next_result` on call {}", inner.calls_received)
1507                })
1508            }
1509
1510            fn start_from_idle(&self) -> bool {
1511                self.start_from_idle
1512            }
1513        }
1514
1515        #[derive(Clone)]
1516        pub(super) struct ContenderFactoryCalled {
1517            pub called: Rc<RefCell<bool>>,
1518        }
1519
1520        impl ContenderFactoryCalled {
1521            pub(super) fn new() -> Self {
1522                Self { called: Rc::new(RefCell::new(false)) }
1523            }
1524
1525            pub(super) fn was_called(&self) -> bool {
1526                *self.called.borrow()
1527            }
1528        }
1529
1530        impl ContenderFactory for ContenderFactoryCalled {
1531            fn make_contenders(&self) -> Vec<Box<dyn Contender>> {
1532                self.called.replace(true);
1533                vec![]
1534            }
1535        }
1536
1537        #[derive(Debug)]
1542        pub(super) struct ContenderForever {}
1543
1544        impl Contender for ContenderForever {
1545            fn examine_event(self: Box<Self>, _event: &TouchpadEvent) -> ExamineEventResult {
1546                ExamineEventResult::Contender(self)
1547            }
1548        }
1549
1550        #[derive(Clone, Debug)]
1551        pub(super) struct StubMatchedContender {
1552            inner: Rc<RefCell<StubMatchedContenderInner>>,
1553        }
1554
1555        impl StubMatchedContender {
1556            pub(super) fn new() -> Self {
1557                Self {
1558                    inner: Rc::new(RefCell::new(StubMatchedContenderInner {
1559                        next_verify_event_result: None,
1560                        next_process_buffered_events_result: None,
1561                        verify_event_calls_received: 0,
1562                        process_buffered_events_calls_received: 0,
1563                        last_process_buffered_events_args: None,
1564                    })),
1565                }
1566            }
1567
1568            pub(super) fn set_next_verify_event_result(&self, next_result: VerifyEventResult) {
1572                self.assert_next_verify_event_result_is_none();
1573                self.inner.borrow_mut().next_verify_event_result = Some(next_result);
1574            }
1575
1576            fn assert_next_verify_event_result_is_none(&self) {
1577                assert_matches!(self.inner.borrow().next_verify_event_result, None);
1578            }
1579
1580            pub(super) fn verify_event_calls_received(&self) -> usize {
1581                self.inner.borrow().verify_event_calls_received
1582            }
1583
1584            pub(super) fn set_next_process_buffered_events_result(
1588                &self,
1589                next_result: ProcessBufferedEventsResult,
1590            ) {
1591                self.assert_next_process_buffered_events_result_is_none();
1592                self.inner.borrow_mut().next_process_buffered_events_result = Some(next_result);
1593            }
1594
1595            pub(super) fn get_last_processed_buffered_events_args(
1596                &self,
1597            ) -> Option<Vec<TouchpadEvent>> {
1598                self.inner.borrow_mut().last_process_buffered_events_args.take()
1599            }
1600
1601            fn assert_next_process_buffered_events_result_is_none(&self) {
1602                assert_matches!(self.inner.borrow().next_process_buffered_events_result, None);
1603            }
1604
1605            pub(super) fn ref_count(&self) -> usize {
1606                Rc::strong_count(&self.inner)
1607            }
1608        }
1609
1610        #[derive(Debug)]
1611        struct StubMatchedContenderInner {
1612            next_verify_event_result: Option<VerifyEventResult>,
1613            next_process_buffered_events_result: Option<ProcessBufferedEventsResult>,
1614            verify_event_calls_received: usize,
1615            process_buffered_events_calls_received: usize,
1616            last_process_buffered_events_args: Option<Vec<TouchpadEvent>>,
1617        }
1618
1619        impl MatchedContender for StubMatchedContender {
1620            fn verify_event(self: Box<Self>, _event: &TouchpadEvent) -> VerifyEventResult {
1621                let mut inner = self.inner.borrow_mut();
1622                inner.verify_event_calls_received += 1;
1623                inner.next_verify_event_result.take().unwrap_or_else(|| {
1624                    panic!(
1625                        "missing `next_verify_event_result` on call {}",
1626                        inner.verify_event_calls_received
1627                    )
1628                })
1629            }
1630
1631            fn process_buffered_events(
1632                self: Box<Self>,
1633                events: Vec<TouchpadEvent>,
1634            ) -> ProcessBufferedEventsResult {
1635                let mut inner = self.inner.borrow_mut();
1636                inner.last_process_buffered_events_args = Some(events);
1637                inner.process_buffered_events_calls_received += 1;
1638                inner.next_process_buffered_events_result.take().unwrap_or_else(|| {
1639                    panic!(
1640                        "missing `next_process_buffered_events_result` on call {}",
1641                        inner.process_buffered_events_calls_received
1642                    )
1643                })
1644            }
1645        }
1646
1647        #[derive(Clone, Debug)]
1648        pub(super) struct StubWinner {
1649            inner: Rc<RefCell<StubWinnerInner>>,
1650        }
1651
1652        impl StubWinner {
1653            pub(super) fn new() -> Self {
1654                Self {
1655                    inner: Rc::new(RefCell::new(StubWinnerInner {
1656                        next_result: None,
1657                        calls_received: 0,
1658                    })),
1659                }
1660            }
1661
1662            pub(super) fn set_next_result(&self, next_result: ProcessNewEventResult) {
1664                self.inner.borrow_mut().next_result = Some(next_result);
1665            }
1666
1667            pub(super) fn calls_received(&self) -> usize {
1668                self.inner.borrow().calls_received
1669            }
1670        }
1671
1672        #[derive(Debug)]
1673        struct StubWinnerInner {
1674            next_result: Option<ProcessNewEventResult>,
1675            calls_received: usize,
1676        }
1677
1678        impl Winner for StubWinner {
1679            fn process_new_event(self: Box<Self>, _event: TouchpadEvent) -> ProcessNewEventResult {
1680                let mut inner = self.inner.borrow_mut();
1681                inner.calls_received += 1;
1682                inner.next_result.take().unwrap_or_else(|| {
1683                    panic!("missing `next_result` on call {}", inner.calls_received)
1684                })
1685            }
1686        }
1687
1688        impl From<StubContender> for Box<dyn Contender> {
1689            fn from(stub_contender: StubContender) -> Box<dyn Contender> {
1690                Box::new(stub_contender)
1691            }
1692        }
1693
1694        impl From<ContenderForever> for Box<dyn Contender> {
1695            fn from(contender_forever: ContenderForever) -> Box<dyn Contender> {
1696                Box::new(contender_forever)
1697            }
1698        }
1699
1700        impl From<StubMatchedContender> for Box<dyn MatchedContender> {
1701            fn from(stub_matched_contender: StubMatchedContender) -> Box<dyn MatchedContender> {
1702                Box::new(stub_matched_contender)
1703            }
1704        }
1705
1706        impl From<StubWinner> for Box<dyn Winner> {
1707            fn from(stub_winner: StubWinner) -> Box<dyn Winner> {
1708                Box::new(stub_winner)
1709            }
1710        }
1711
1712        pub(super) const TOUCH_CONTACT_INDEX_FINGER: touch_binding::TouchContact =
1713            touch_binding::TouchContact {
1714                id: 0,
1715                position: Position { x: 0.0, y: 0.0 },
1716                pressure: None,
1717                contact_size: Some(Size {
1718                    width: args::MIN_PALM_SIZE_MM / 2.0,
1719                    height: args::MIN_PALM_SIZE_MM / 2.0,
1720                }),
1721            };
1722    }
1723
1724    mod idle_chain_states {
1725        use super::super::{
1726            ExamineEventResult, GestureArena, InputHandler, MutableState,
1727            ProcessBufferedEventsResult, Reason, RecognizedGesture, TouchpadEvent, args,
1728        };
1729        use super::utils::{
1730            ContenderFactoryCalled, ContenderFactoryOnceOrPanic, StubContender,
1731            StubMatchedContender, StubWinner, TOUCH_CONTACT_INDEX_FINGER, make_touchpad_descriptor,
1732            make_unhandled_keyboard_event, make_unhandled_mouse_event,
1733            make_unhandled_touchpad_event,
1734        };
1735        use crate::input_handler::InputHandlerStatus;
1736        use crate::utils::Size;
1737        use crate::{Position, input_device, touch_binding};
1738        use assert_matches::assert_matches;
1739
1740        use maplit::hashset;
1741        use std::cell::RefCell;
1742        use std::rc::Rc;
1743        use test_case::test_case;
1744
1745        fn make_gesture_arena_with_state(
1746            contender_factory: ContenderFactoryOnceOrPanic,
1747            state: MutableState,
1748        ) -> Rc<GestureArena> {
1749            Rc::new(GestureArena {
1750                contender_factory: Box::new(contender_factory),
1751                mutable_state: RefCell::new(state),
1752                inspect_log: RefCell::new(fuchsia_inspect_contrib::nodes::BoundedListNode::new(
1753                    fuchsia_inspect::Inspector::default().root().create_child("some_key"),
1754                    1,
1755                )),
1756                inspect_status: InputHandlerStatus::default(),
1757            })
1758        }
1759
1760        #[test_case(MutableState::Idle; "idle")]
1761        #[test_case(MutableState::Chain; "chain")]
1762        #[fuchsia::test(allow_stalls = false)]
1763        async fn invokes_contender_factory_on_touchpad_event(state: MutableState) {
1764            let contender_factory = Box::new(ContenderFactoryCalled::new());
1765            let arena = Rc::new(GestureArena {
1766                contender_factory: contender_factory.clone(),
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            arena.handle_input_event(make_unhandled_touchpad_event()).await;
1775            assert!(contender_factory.was_called());
1776        }
1777
1778        #[test_case(MutableState::Idle; "idle")]
1779        #[test_case(MutableState::Chain; "chain")]
1780        #[fuchsia::test(allow_stalls = false)]
1781        async fn does_not_invoke_contender_factory_on_mouse_event(state: MutableState) {
1782            let contender_factory = Box::new(ContenderFactoryCalled::new());
1783            let arena = Rc::new(GestureArena {
1784                contender_factory: contender_factory.clone(),
1785                mutable_state: RefCell::new(state),
1786                inspect_log: RefCell::new(fuchsia_inspect_contrib::nodes::BoundedListNode::new(
1787                    fuchsia_inspect::Inspector::default().root().create_child("some_key"),
1788                    1,
1789                )),
1790                inspect_status: InputHandlerStatus::default(),
1791            });
1792            arena.handle_input_event(make_unhandled_mouse_event()).await;
1793            assert!(!contender_factory.was_called());
1794        }
1795
1796        #[test_case(MutableState::Idle; "idle")]
1797        #[test_case(MutableState::Chain; "chain")]
1798        #[fuchsia::test(allow_stalls = false)]
1799        async fn does_not_invoke_contender_factory_on_keyboard_event(state: MutableState) {
1800            let contender_factory = Box::new(ContenderFactoryCalled::new());
1801
1802            let arena = Rc::new(GestureArena {
1803                contender_factory: contender_factory.clone(),
1804                mutable_state: RefCell::new(state),
1805                inspect_log: RefCell::new(fuchsia_inspect_contrib::nodes::BoundedListNode::new(
1806                    fuchsia_inspect::Inspector::default().root().create_child("some_key"),
1807                    1,
1808                )),
1809                inspect_status: InputHandlerStatus::default(),
1810            });
1811            arena.handle_input_event(make_unhandled_keyboard_event()).await;
1812            assert!(!contender_factory.was_called());
1813        }
1814
1815        #[test_case(MutableState::Idle; "idle")]
1816        #[test_case(MutableState::Chain; "chain")]
1817        #[fuchsia::test(allow_stalls = false)]
1818        async fn calls_examine_event_on_contender(state: MutableState) {
1819            let contender = Box::new(StubContender::new());
1820            let contender_factory = ContenderFactoryOnceOrPanic::new(vec![contender.clone()]);
1821            let arena = make_gesture_arena_with_state(contender_factory, state);
1822            contender.set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
1823            arena.handle_input_event(make_unhandled_touchpad_event()).await;
1824            pretty_assertions::assert_eq!(contender.calls_received(), 1);
1825        }
1826
1827        #[fuchsia::test(allow_stalls = false)]
1828        async fn calls_examine_event_on_idle_only_contender_while_idle() {
1829            let contender = Box::new(StubContender::new_start_from_idle());
1830            let contender_factory = ContenderFactoryOnceOrPanic::new(vec![contender.clone()]);
1831            let arena = make_gesture_arena_with_state(contender_factory, MutableState::Idle);
1832            contender.set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
1833            arena.handle_input_event(make_unhandled_touchpad_event()).await;
1834            pretty_assertions::assert_eq!(contender.calls_received(), 1);
1835        }
1836
1837        #[fuchsia::test(allow_stalls = false)]
1838        async fn does_not_calls_examine_event_on_idle_only_contender_while_chain() {
1839            let contender = Box::new(StubContender::new_start_from_idle());
1840            let contender_factory = ContenderFactoryOnceOrPanic::new(vec![contender.clone()]);
1841            let arena = make_gesture_arena_with_state(contender_factory, MutableState::Chain);
1842            contender.set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
1843            arena.handle_input_event(make_unhandled_touchpad_event()).await;
1844            pretty_assertions::assert_eq!(contender.calls_received(), 0);
1845        }
1846
1847        #[test_case(MutableState::Idle; "idle")]
1848        #[test_case(MutableState::Chain; "chain")]
1849        #[fuchsia::test(allow_stalls = false)]
1850        async fn calls_examine_event_on_all_contenders_even_if_first_matches(state: MutableState) {
1851            let first_contender = Box::new(StubContender::new());
1852            let second_contender = Box::new(StubContender::new());
1853            let contender_factory = ContenderFactoryOnceOrPanic::new(vec![
1854                first_contender.clone(),
1855                second_contender.clone(),
1856            ]);
1857            let arena = make_gesture_arena_with_state(contender_factory, state);
1858            first_contender.set_next_result(ExamineEventResult::MatchedContender(
1859                StubMatchedContender::new().into(),
1860            ));
1861            second_contender
1863                .set_next_result(ExamineEventResult::Contender(StubContender::new().into()));
1864            arena.handle_input_event(make_unhandled_touchpad_event()).await;
1865            pretty_assertions::assert_eq!(first_contender.calls_received(), 1);
1866            pretty_assertions::assert_eq!(second_contender.calls_received(), 1);
1867        }
1868
1869        #[test_case(MutableState::Idle; "idle")]
1870        #[test_case(MutableState::Chain; "chain")]
1871        #[fuchsia::test(allow_stalls = false)]
1872        async fn retains_reference_to_replacement_contender(state: MutableState) {
1873            let initial_contender = Box::new(StubContender::new());
1875            let contender_factory =
1876                ContenderFactoryOnceOrPanic::new(vec![initial_contender.clone()]);
1877            let arena = make_gesture_arena_with_state(contender_factory, state);
1878
1879            let replacement_contender = StubContender::new();
1882            initial_contender.set_next_result(ExamineEventResult::Contender(
1883                replacement_contender.clone().into(),
1884            ));
1885
1886            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
1889
1890            initial_contender.assert_next_result_is_none();
1892
1893            pretty_assertions::assert_eq!(replacement_contender.ref_count(), 2);
1902        }
1903
1904        #[test_case(MutableState::Idle; "idle")]
1905        #[test_case(MutableState::Chain; "chain")]
1906        #[fuchsia::test(allow_stalls = false)]
1907        async fn retains_reference_to_matched_contender(state: MutableState) {
1908            let initial_contender = Box::new(StubContender::new());
1910            let second_contender = Box::new(StubContender::new());
1912            let contender_factory = ContenderFactoryOnceOrPanic::new(vec![
1913                initial_contender.clone(),
1914                second_contender.clone(),
1915            ]);
1916            let arena = make_gesture_arena_with_state(contender_factory, state);
1917            let replacement_contender = StubMatchedContender::new();
1920            initial_contender.set_next_result(ExamineEventResult::MatchedContender(
1921                replacement_contender.clone().into(),
1922            ));
1923            second_contender
1924                .set_next_result(ExamineEventResult::Contender(StubContender::new().into()));
1925
1926            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
1929
1930            initial_contender.assert_next_result_is_none();
1932
1933            pretty_assertions::assert_eq!(replacement_contender.ref_count(), 2);
1942        }
1943
1944        #[test_case(MutableState::Idle; "idle")]
1945        #[test_case(MutableState::Chain; "chain")]
1946        #[fuchsia::test(allow_stalls = false)]
1947        async fn retains_touchpad_event_when_entering_matching(state: MutableState) {
1948            let initial_contender = Box::new(StubContender::new());
1950            let second_contender = Box::new(StubContender::new());
1952            let contender_factory = ContenderFactoryOnceOrPanic::new(vec![
1953                initial_contender.clone(),
1954                second_contender.clone(),
1955            ]);
1956            let arena = make_gesture_arena_with_state(contender_factory, state);
1957            let touchpad_event = input_device::InputEvent {
1959                event_time: zx::MonotonicInstant::from_nanos(123456),
1960                device_event: input_device::InputDeviceEvent::Touchpad(
1961                    touch_binding::TouchpadEvent {
1962                        injector_contacts: vec![TOUCH_CONTACT_INDEX_FINGER],
1963                        pressed_buttons: hashset! {0},
1964                    },
1965                ),
1966                device_descriptor: make_touchpad_descriptor(),
1967                trace_id: None,
1968                handled: input_device::Handled::No,
1969            };
1970
1971            initial_contender.set_next_result(ExamineEventResult::MatchedContender(
1974                StubMatchedContender::new().into(),
1975            ));
1976            second_contender
1977                .set_next_result(ExamineEventResult::Contender(StubContender::new().into()));
1978
1979            arena.clone().handle_input_event(touchpad_event).await;
1983
1984            assert_matches!(
1986                &*arena.mutable_state.borrow(),
1987                MutableState::Matching {
1988                    contenders: _,
1989                    matched_contenders: _,
1990                    buffered_events,
1991                    first_event_timestamp: _,
1992                } => pretty_assertions::assert_eq!(
1993                    buffered_events.as_slice(),
1994                    [TouchpadEvent {
1995                        timestamp: zx::MonotonicInstant::from_nanos(123456),
1996                        pressed_buttons: vec![0],
1997                        contacts: vec![
1998                            touch_binding::TouchContact {
1999                                contact_size: Some(Size{ width: args::MIN_PALM_SIZE_MM / 2000.0, height: args::MIN_PALM_SIZE_MM / 2000.0 }),
2000                                ..TOUCH_CONTACT_INDEX_FINGER
2001                            },
2002                        ],
2003                        filtered_palm_contacts: vec![],
2004                    }]
2005                )
2006            );
2007        }
2008
2009        #[test_case(MutableState::Idle; "idle")]
2010        #[test_case(MutableState::Chain; "chain")]
2011        #[fuchsia::test(allow_stalls = false)]
2012        async fn generates_no_events_on_mismatch_entering_chain(state: MutableState) {
2013            let contender = Box::new(StubContender::new());
2014            let contender_factory = ContenderFactoryOnceOrPanic::new(vec![contender.clone()]);
2015            let arena = make_gesture_arena_with_state(contender_factory, state);
2016            contender.set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
2017
2018            let touchpad_event = input_device::InputEvent {
2019                event_time: zx::MonotonicInstant::from_nanos(123456),
2020                device_event: input_device::InputDeviceEvent::Touchpad(
2021                    touch_binding::TouchpadEvent {
2022                        injector_contacts: vec![touch_binding::TouchContact {
2023                            id: 1,
2024                            position: Position { x: 0.0, y: 0.0 },
2025                            ..TOUCH_CONTACT_INDEX_FINGER
2026                        }],
2027                        pressed_buttons: hashset! {},
2028                    },
2029                ),
2030                device_descriptor: make_touchpad_descriptor(),
2031                trace_id: None,
2032                handled: input_device::Handled::No,
2033            };
2034
2035            pretty_assertions::assert_eq!(
2036                arena.clone().handle_input_event(touchpad_event).await,
2037                vec![]
2038            );
2039
2040            assert_matches!(*arena.mutable_state.borrow(), MutableState::Chain);
2041        }
2042
2043        #[test_case(MutableState::Idle; "idle")]
2044        #[test_case(MutableState::Chain; "chain")]
2045        #[fuchsia::test(allow_stalls = false)]
2046        async fn generates_no_events_on_mismatch_entering_idle(state: MutableState) {
2047            let contender = Box::new(StubContender::new());
2048            let contender_factory = ContenderFactoryOnceOrPanic::new(vec![contender.clone()]);
2049            let arena = make_gesture_arena_with_state(contender_factory, state);
2050            contender.set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
2051            pretty_assertions::assert_eq!(
2052                arena.clone().handle_input_event(make_unhandled_touchpad_event()).await,
2053                vec![]
2054            );
2055
2056            assert_matches!(*arena.mutable_state.borrow(), MutableState::Idle);
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_when_entering_matching(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::Contender(StubContender::new().into()));
2067            pretty_assertions::assert_eq!(
2068                arena.handle_input_event(make_unhandled_touchpad_event()).await,
2069                vec![]
2070            );
2071        }
2072
2073        #[test_case(MutableState::Idle; "idle")]
2074        #[test_case(MutableState::Chain; "chain")]
2075        #[fuchsia::test(allow_stalls = false)]
2076        async fn enters_idle_on_mismatch(state: MutableState) {
2077            let contender = Box::new(StubContender::new());
2078            let contender_factory = ContenderFactoryOnceOrPanic::new(vec![contender.clone()]);
2079            let arena = make_gesture_arena_with_state(contender_factory, state);
2080            contender.set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
2081            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
2082            assert_matches!(*arena.mutable_state.borrow(), MutableState::Idle);
2083        }
2084
2085        #[test_case(MutableState::Idle; "idle")]
2086        #[test_case(MutableState::Chain; "chain")]
2087        #[fuchsia::test(allow_stalls = false)]
2088        async fn enters_matching_on_contender_result(state: MutableState) {
2089            let contender = Box::new(StubContender::new());
2090            let contender_factory = ContenderFactoryOnceOrPanic::new(vec![contender.clone()]);
2091            let arena = make_gesture_arena_with_state(contender_factory, state);
2092            contender.set_next_result(ExamineEventResult::Contender(StubContender::new().into()));
2093            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
2094            assert_matches!(*arena.mutable_state.borrow(), MutableState::Matching { .. });
2095        }
2096
2097        #[test_case(MutableState::Idle; "idle")]
2098        #[test_case(MutableState::Chain; "chain")]
2099        #[fuchsia::test(allow_stalls = false)]
2100        async fn enters_matching_on_matched_contender_result(state: MutableState) {
2101            let first_contender = Box::new(StubContender::new());
2102            let second_contender = Box::new(StubContender::new());
2104            let contender_factory = ContenderFactoryOnceOrPanic::new(vec![
2105                first_contender.clone(),
2106                second_contender.clone(),
2107            ]);
2108            let arena = make_gesture_arena_with_state(contender_factory, state);
2109
2110            first_contender.set_next_result(ExamineEventResult::MatchedContender(
2111                StubMatchedContender::new().into(),
2112            ));
2113            second_contender
2114                .set_next_result(ExamineEventResult::Contender(StubContender::new().into()));
2115            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
2116            assert_matches!(*arena.mutable_state.borrow(), MutableState::Matching { .. });
2117        }
2118
2119        #[test_case(MutableState::Idle; "idle")]
2120        #[test_case(MutableState::Chain; "chain")]
2121        #[fuchsia::test(allow_stalls = false)]
2122        async fn enters_forward_on_only_one_matched_contender_result(state: MutableState) {
2123            let contender = Box::new(StubContender::new());
2124            let contender_factory = ContenderFactoryOnceOrPanic::new(vec![contender.clone()]);
2125            let arena = make_gesture_arena_with_state(contender_factory, state);
2126
2127            let matched_contender = StubMatchedContender::new();
2128            matched_contender.set_next_process_buffered_events_result(
2129                ProcessBufferedEventsResult {
2130                    generated_events: vec![],
2131                    winner: Some(Box::new(StubWinner::new())),
2132                    recognized_gesture: RecognizedGesture::Motion,
2133                },
2134            );
2135            contender
2136                .set_next_result(ExamineEventResult::MatchedContender(matched_contender.into()));
2137            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
2138            assert_matches!(*arena.mutable_state.borrow(), MutableState::Forwarding { .. });
2139        }
2140    }
2141
2142    mod matching_state {
2143        use super::super::{
2144            Contender, ContenderFactory, ExamineEventResult, GestureArena, InputHandler,
2145            MouseEvent, MutableState, PRIMARY_BUTTON, ProcessBufferedEventsResult, Reason,
2146            RecognizedGesture, TouchpadEvent, VerifyEventResult,
2147        };
2148        use super::utils::{
2149            ContenderForever, StubContender, StubMatchedContender, StubWinner,
2150            TOUCH_CONTACT_INDEX_FINGER, make_touchpad_descriptor, make_unhandled_keyboard_event,
2151            make_unhandled_mouse_event, make_unhandled_touchpad_event,
2152        };
2153        use crate::input_handler::InputHandlerStatus;
2154        use crate::{Position, input_device, mouse_binding, touch_binding};
2155        use assert_matches::assert_matches;
2156
2157        use maplit::hashset;
2158        use pretty_assertions::assert_eq;
2159        use std::cell::RefCell;
2160        use std::rc::Rc;
2161        use test_case::test_case;
2162
2163        struct ContenderFactoryWarn {}
2164
2165        impl ContenderFactory for ContenderFactoryWarn {
2166            fn make_contenders(&self) -> Vec<Box<dyn Contender>> {
2167                eprintln!("factory invoked in matching state");
2170                vec![]
2171            }
2172        }
2173
2174        fn make_matching_arena(
2175            contenders: Vec<StubContender>,
2176            matched_contenders: Vec<StubMatchedContender>,
2177            buffered_events: Vec<TouchpadEvent>,
2178            contender_forever: Option<ContenderForever>,
2179        ) -> Rc<GestureArena> {
2180            Rc::new(GestureArena {
2181                contender_factory: Box::new(ContenderFactoryWarn {}),
2182                mutable_state: RefCell::new(MutableState::Matching {
2183                    contenders: {
2184                        contenders
2185                            .into_iter()
2186                            .map(std::convert::From::<StubContender>::from)
2187                            .chain(
2188                                contender_forever
2189                                    .into_iter()
2190                                    .map(std::convert::From::<ContenderForever>::from),
2191                            )
2192                            .collect()
2193                    },
2194                    matched_contenders: {
2195                        matched_contenders
2196                            .into_iter()
2197                            .map(std::convert::From::<StubMatchedContender>::from)
2198                            .collect()
2199                    },
2200                    first_event_timestamp: zx::MonotonicInstant::ZERO,
2201                    buffered_events,
2202                }),
2203                inspect_log: RefCell::new(fuchsia_inspect_contrib::nodes::BoundedListNode::new(
2204                    fuchsia_inspect::Inspector::default().root().create_child("some_key"),
2205                    1,
2206                )),
2207                inspect_status: InputHandlerStatus::default(),
2208            })
2209        }
2210
2211        #[fuchsia::test(allow_stalls = false)]
2212        async fn invokes_examine_and_verify_event_on_touchpad_event() {
2213            let contender = StubContender::new();
2214            let matched_contender = StubMatchedContender::new();
2215            let arena = make_matching_arena(
2216                vec![contender.clone()],
2217                vec![matched_contender.clone()],
2218                vec![],
2219                None,
2220            );
2221            contender.set_next_result(ExamineEventResult::Contender(contender.clone().into()));
2222            matched_contender.set_next_verify_event_result(VerifyEventResult::Mismatch(
2223                Reason::Basic("some reason"),
2224            ));
2225            arena.handle_input_event(make_unhandled_touchpad_event()).await;
2226            assert_eq!(contender.calls_received(), 1);
2227            assert_eq!(matched_contender.verify_event_calls_received(), 1);
2228        }
2229
2230        #[fuchsia::test(allow_stalls = false)]
2231        async fn does_not_invoke_examine_or_verify_event_on_mouse_event() {
2232            let contender = StubContender::new();
2233            let matched_contender = StubMatchedContender::new();
2234            let arena = make_matching_arena(
2235                vec![contender.clone()],
2236                vec![matched_contender.clone()],
2237                vec![],
2238                None,
2239            );
2240            contender.set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
2241            matched_contender.set_next_verify_event_result(VerifyEventResult::Mismatch(
2242                Reason::Basic("some reason"),
2243            ));
2244            arena.handle_input_event(make_unhandled_mouse_event()).await;
2245            assert_eq!(contender.calls_received(), 0);
2246            assert_eq!(matched_contender.verify_event_calls_received(), 0);
2247        }
2248
2249        #[fuchsia::test(allow_stalls = false)]
2250        async fn does_not_invoke_examine_or_verify_event_on_keyboard_event() {
2251            let contender = StubContender::new();
2252            let matched_contender = StubMatchedContender::new();
2253            let arena = make_matching_arena(
2254                vec![contender.clone()],
2255                vec![matched_contender.clone()],
2256                vec![],
2257                None,
2258            );
2259            contender.set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
2260            matched_contender.set_next_verify_event_result(VerifyEventResult::Mismatch(
2261                Reason::Basic("some reason"),
2262            ));
2263            arena.handle_input_event(make_unhandled_keyboard_event()).await;
2264            assert_eq!(contender.calls_received(), 0);
2265            assert_eq!(matched_contender.verify_event_calls_received(), 0);
2266        }
2267
2268        #[fuchsia::test(allow_stalls = false)]
2269        async fn does_not_repeat_event_to_matched_contender_returned_by_examine_event() {
2270            let contender = StubContender::new();
2271            let arena = make_matching_arena(
2272                vec![contender.clone()],
2273                vec![],
2274                vec![],
2275                Some(ContenderForever {}),
2278            );
2279
2280            let matched_contender = StubMatchedContender::new();
2282            contender.set_next_result(ExamineEventResult::MatchedContender(
2283                matched_contender.clone().into(),
2284            ));
2285
2286            matched_contender.set_next_verify_event_result(VerifyEventResult::Mismatch(
2291                Reason::Basic("some reason"),
2292            ));
2293
2294            arena.handle_input_event(make_unhandled_touchpad_event()).await;
2297            assert_eq!(matched_contender.verify_event_calls_received(), 0);
2298        }
2299
2300        #[fuchsia::test(allow_stalls = false)]
2301        async fn invokes_examine_event_for_new_event_with_contender_replaced_by_contender() {
2302            let initial_contender = StubContender::new();
2305            let arena = make_matching_arena(vec![initial_contender.clone()], vec![], vec![], None);
2306            let replacement_contender = StubContender::new();
2307            initial_contender.set_next_result(ExamineEventResult::Contender(
2308                replacement_contender.clone().into(),
2309            ));
2310
2311            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
2314
2315            replacement_contender
2318                .set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
2319            arena.handle_input_event(make_unhandled_touchpad_event()).await;
2320
2321            assert_eq!(replacement_contender.calls_received(), 1);
2323        }
2324
2325        #[fuchsia::test(allow_stalls = false)]
2326        async fn invokes_verify_event_for_new_event_with_contender_replaced_by_matched_contender() {
2327            let initial_contender = StubContender::new();
2333            let arena = make_matching_arena(
2334                vec![initial_contender.clone()],
2335                vec![],
2336                vec![],
2337                Some(ContenderForever {}),
2338            );
2339            let replacement_contender = StubMatchedContender::new();
2340            initial_contender.set_next_result(ExamineEventResult::MatchedContender(
2341                replacement_contender.clone().into(),
2342            ));
2343
2344            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
2347
2348            replacement_contender.set_next_verify_event_result(VerifyEventResult::Mismatch(
2351                Reason::Basic("some reason"),
2352            ));
2353            arena.handle_input_event(make_unhandled_touchpad_event()).await;
2354
2355            assert_eq!(replacement_contender.verify_event_calls_received(), 1);
2357        }
2358
2359        #[fuchsia::test(allow_stalls = false)]
2360        async fn invokes_verify_event_for_new_event_with_matched_contender_replaced_by_matched_contender()
2361         {
2362            let matched_contender = StubMatchedContender::new();
2363            let arena = make_matching_arena(
2364                vec![],
2365                vec![matched_contender.clone()],
2366                vec![],
2367                Some(ContenderForever {}),
2371            );
2372
2373            let replacement_matched_contender = StubMatchedContender::new();
2376            matched_contender.set_next_verify_event_result(VerifyEventResult::MatchedContender(
2377                replacement_matched_contender.clone().into(),
2378            ));
2379
2380            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
2383
2384            replacement_matched_contender.set_next_verify_event_result(
2386                VerifyEventResult::Mismatch(Reason::Basic("some reason")),
2387            );
2388
2389            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
2392            assert_eq!(replacement_matched_contender.verify_event_calls_received(), 1);
2393        }
2394
2395        #[fuchsia::test(allow_stalls = false)]
2396        async fn generates_no_events_on_mismatch_entering_idle() {
2397            let contender = StubContender::new();
2398            let arena = make_matching_arena(vec![contender.clone()], vec![], vec![], None);
2399            contender.set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
2400            assert_eq!(
2401                arena.clone().handle_input_event(make_unhandled_touchpad_event()).await,
2402                vec![]
2403            );
2404            assert_matches!(*arena.mutable_state.borrow(), MutableState::Idle);
2405        }
2406
2407        #[fuchsia::test(allow_stalls = false)]
2408        async fn generates_no_events_on_mismatch_entering_chain() {
2409            let contender = StubContender::new();
2410            let arena = make_matching_arena(vec![contender.clone()], vec![], vec![], None);
2411            contender.set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
2412
2413            let touchpad_event = input_device::InputEvent {
2414                event_time: zx::MonotonicInstant::from_nanos(123456),
2415                device_event: input_device::InputDeviceEvent::Touchpad(
2416                    touch_binding::TouchpadEvent {
2417                        injector_contacts: vec![touch_binding::TouchContact {
2418                            id: 1,
2419                            position: Position { x: 0.0, y: 0.0 },
2420                            ..TOUCH_CONTACT_INDEX_FINGER
2421                        }],
2422                        pressed_buttons: hashset! {},
2423                    },
2424                ),
2425                device_descriptor: make_touchpad_descriptor(),
2426                trace_id: None,
2427                handled: input_device::Handled::No,
2428            };
2429
2430            pretty_assertions::assert_eq!(
2431                arena.clone().handle_input_event(touchpad_event).await,
2432                vec![]
2433            );
2434
2435            assert_matches!(*arena.mutable_state.borrow(), MutableState::Chain);
2436        }
2437
2438        #[fuchsia::test(allow_stalls = false)]
2439        async fn generates_no_events_on_contender() {
2440            let contender = StubContender::new();
2441            let arena = make_matching_arena(vec![contender.clone()], vec![], vec![], None);
2442            contender.set_next_result(ExamineEventResult::Contender(contender.clone().into()));
2443            assert_eq!(arena.handle_input_event(make_unhandled_touchpad_event()).await, vec![]);
2444        }
2445
2446        #[fuchsia::test(allow_stalls = false)]
2447        async fn generates_no_events_on_multiple_matched_contenders() {
2448            let first_matched_contender = StubMatchedContender::new();
2449            let second_matched_contender = StubMatchedContender::new();
2450            let arena = make_matching_arena(
2451                vec![],
2452                vec![first_matched_contender.clone(), second_matched_contender.clone()],
2453                vec![],
2454                None,
2455            );
2456            first_matched_contender.set_next_verify_event_result(
2457                VerifyEventResult::MatchedContender(first_matched_contender.clone().into()),
2458            );
2459            second_matched_contender.set_next_verify_event_result(
2460                VerifyEventResult::MatchedContender(second_matched_contender.clone().into()),
2461            );
2462            assert_eq!(arena.handle_input_event(make_unhandled_touchpad_event()).await, vec![]);
2463        }
2464
2465        #[test_case(Some(StubWinner::new()); "with_winner")]
2466        #[test_case(None; "without_winner")]
2467        #[fuchsia::test(allow_stalls = false)]
2468        async fn generates_events_from_process_buffered_events_on_single_matched_contender(
2469            winner: Option<StubWinner>,
2470        ) {
2471            let matched_contender = StubMatchedContender::new();
2472            let arena = make_matching_arena(vec![], vec![matched_contender.clone()], vec![], None);
2473            matched_contender.set_next_verify_event_result(VerifyEventResult::MatchedContender(
2474                matched_contender.clone().into(),
2475            ));
2476            matched_contender.set_next_process_buffered_events_result(
2477                ProcessBufferedEventsResult {
2478                    generated_events: vec![
2479                        MouseEvent {
2480                            timestamp: zx::MonotonicInstant::from_nanos(123),
2481                            mouse_data: mouse_binding::MouseEvent {
2482                                location: mouse_binding::MouseLocation::Relative(
2483                                    mouse_binding::RelativeLocation {
2484                                        millimeters: Position::zero(),
2485                                    },
2486                                ),
2487                                wheel_delta_v: None,
2488                                wheel_delta_h: None,
2489                                phase: mouse_binding::MousePhase::Down,
2490                                affected_buttons: hashset! { PRIMARY_BUTTON },
2491                                pressed_buttons: hashset! { PRIMARY_BUTTON },
2492                                is_precision_scroll: None,
2493                            },
2494                        },
2495                        MouseEvent {
2496                            timestamp: zx::MonotonicInstant::from_nanos(456),
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::Up,
2506                                affected_buttons: hashset! { PRIMARY_BUTTON },
2507                                pressed_buttons: hashset! {},
2508                                is_precision_scroll: None,
2509                            },
2510                        },
2511                    ],
2512                    winner: winner.map(std::convert::From::<StubWinner>::from),
2513                    recognized_gesture: RecognizedGesture::Motion,
2514                },
2515            );
2516            assert_matches!(
2517                arena
2518                    .handle_input_event(make_unhandled_touchpad_event())
2519                    .await
2520                    .as_slice(),
2521                [
2522                    input_device::InputEvent {
2523                        handled: input_device::Handled::No,
2524                        device_event: input_device::InputDeviceEvent::Mouse(
2525                            mouse_binding::MouseEvent {
2526                                pressed_buttons: first_pressed_buttons, ..
2527                            }
2528                        ),
2529                        ..
2530                    },
2531                    input_device::InputEvent {
2532                        handled: input_device::Handled::No,
2533                        device_event: input_device::InputDeviceEvent::Mouse(
2534                            mouse_binding::MouseEvent {
2535                                pressed_buttons: second_pressed_buttons, ..
2536                            }
2537                        ),
2538                        ..
2539                    },
2540                ] => {
2541                    pretty_assertions::assert_eq!(*first_pressed_buttons, hashset! { PRIMARY_BUTTON });
2542                    pretty_assertions::assert_eq!(*second_pressed_buttons, hashset! {});
2543                }
2544            );
2545        }
2546
2547        #[fuchsia::test(allow_stalls = false)]
2548        async fn passes_all_buffered_events_to_process_buffered_events() {
2549            let contender = StubContender::new();
2552            let matched_contender = StubMatchedContender::new();
2553            let arena = make_matching_arena(
2554                vec![contender.clone()],
2555                vec![matched_contender.clone()],
2556                vec![TouchpadEvent {
2557                    timestamp: zx::MonotonicInstant::from_nanos(123),
2558                    contacts: vec![],
2559                    pressed_buttons: vec![],
2560                    filtered_palm_contacts: vec![],
2561                }],
2562                None,
2563            );
2564
2565            contender.set_next_result(ExamineEventResult::Contender(contender.clone().into()));
2568            matched_contender.set_next_verify_event_result(VerifyEventResult::MatchedContender(
2569                matched_contender.clone().into(),
2570            ));
2571            arena
2572                .clone()
2573                .handle_input_event(input_device::InputEvent {
2574                    event_time: zx::MonotonicInstant::from_nanos(456),
2575                    device_event: input_device::InputDeviceEvent::Touchpad(
2576                        touch_binding::TouchpadEvent {
2577                            injector_contacts: vec![],
2578                            pressed_buttons: hashset! {},
2579                        },
2580                    ),
2581                    device_descriptor: make_touchpad_descriptor(),
2582                    trace_id: None,
2583                    handled: input_device::Handled::No,
2584                })
2585                .await;
2586
2587            contender.set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
2589            matched_contender.set_next_verify_event_result(VerifyEventResult::MatchedContender(
2590                matched_contender.clone().into(),
2591            ));
2592            matched_contender.set_next_process_buffered_events_result(
2593                ProcessBufferedEventsResult {
2594                    generated_events: vec![],
2595                    winner: None,
2596                    recognized_gesture: RecognizedGesture::Motion,
2597                },
2598            );
2599            arena
2600                .handle_input_event(input_device::InputEvent {
2601                    event_time: zx::MonotonicInstant::from_nanos(789),
2602                    device_event: input_device::InputDeviceEvent::Touchpad(
2603                        touch_binding::TouchpadEvent {
2604                            injector_contacts: vec![],
2605                            pressed_buttons: hashset! {},
2606                        },
2607                    ),
2608                    device_descriptor: make_touchpad_descriptor(),
2609                    trace_id: None,
2610                    handled: input_device::Handled::No,
2611                })
2612                .await;
2613
2614            assert_eq!(
2616                matched_contender
2617                    .get_last_processed_buffered_events_args()
2618                    .map(|vec| vec
2619                        .into_iter()
2620                        .map(|event| event.timestamp.into_nanos())
2621                        .collect::<Vec<_>>())
2622                    .as_deref(),
2623                Some([123, 456, 789].as_slice())
2624            );
2625        }
2626
2627        #[fuchsia::test(allow_stalls = false)]
2628        async fn transitions_to_idle_when_sole_contender_does_not_match() {
2629            let contender = StubContender::new();
2630            let arena = make_matching_arena(vec![contender.clone()], vec![], vec![], None);
2631            contender.set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
2632            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
2633            assert_matches!(*arena.mutable_state.borrow(), MutableState::Idle);
2634        }
2635
2636        #[fuchsia::test(allow_stalls = false)]
2637        async fn transitions_to_idle_when_sole_matched_contender_does_not_match() {
2638            let matched_contender = StubMatchedContender::new();
2639            let arena = make_matching_arena(vec![], vec![matched_contender.clone()], vec![], None);
2640            matched_contender.set_next_verify_event_result(VerifyEventResult::Mismatch(
2641                Reason::Basic("some reason"),
2642            ));
2643            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
2644            assert_matches!(*arena.mutable_state.borrow(), MutableState::Idle);
2645        }
2646
2647        #[fuchsia::test(allow_stalls = false)]
2648        async fn remains_in_matching_when_a_contender_remains() {
2649            let contender = StubContender::new();
2650            let arena = make_matching_arena(vec![contender.clone()], vec![], vec![], None);
2651            contender.set_next_result(ExamineEventResult::Contender(contender.clone().into()));
2652            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
2653            assert_matches!(*arena.mutable_state.borrow(), MutableState::Matching { .. });
2654        }
2655
2656        #[fuchsia::test(allow_stalls = false)]
2657        async fn remains_in_matching_when_multiple_matched_contenders_remain() {
2658            let matched_contender_a = StubMatchedContender::new();
2659            let matched_contender_b = StubMatchedContender::new();
2660            let arena = make_matching_arena(
2661                vec![],
2662                vec![matched_contender_a.clone(), matched_contender_b.clone()],
2663                vec![],
2664                None,
2665            );
2666            matched_contender_a.set_next_verify_event_result(VerifyEventResult::MatchedContender(
2667                matched_contender_a.clone().into(),
2668            ));
2669            matched_contender_b.set_next_verify_event_result(VerifyEventResult::MatchedContender(
2670                matched_contender_b.clone().into(),
2671            ));
2672            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
2673            assert_matches!(*arena.mutable_state.borrow(), MutableState::Matching { .. });
2674        }
2675
2676        #[fuchsia::test(allow_stalls = false)]
2677        async fn transitions_to_idle_when_sole_matched_contender_returns_no_winner() {
2678            let matched_contender = StubMatchedContender::new();
2679            let arena = make_matching_arena(vec![], vec![matched_contender.clone()], vec![], None);
2680            matched_contender.set_next_verify_event_result(VerifyEventResult::MatchedContender(
2681                matched_contender.clone().into(),
2682            ));
2683            matched_contender.set_next_process_buffered_events_result(
2684                ProcessBufferedEventsResult {
2685                    generated_events: vec![],
2686                    winner: None,
2687                    recognized_gesture: RecognizedGesture::Motion,
2688                },
2689            );
2690            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
2691            assert_matches!(*arena.mutable_state.borrow(), MutableState::Idle);
2692        }
2693
2694        #[fuchsia::test(allow_stalls = false)]
2695        async fn transitions_to_forwarding_when_sole_matched_contender_returns_a_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: Some(StubWinner::new().into()),
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::Forwarding { .. });
2710        }
2711    }
2712
2713    mod forwarding_state {
2714        use super::super::{
2715            Contender, EndGestureEvent, ExamineEventResult, GestureArena, InputHandler, MouseEvent,
2716            MutableState, ProcessNewEventResult, Reason, RecognizedGesture, TouchpadEvent,
2717        };
2718        use super::utils::{
2719            ContenderFactoryOnceOrPanic, ContenderForever, StubContender, StubWinner,
2720            TOUCH_CONTACT_INDEX_FINGER, make_touchpad_descriptor, make_unhandled_keyboard_event,
2721            make_unhandled_mouse_event, make_unhandled_touchpad_event,
2722        };
2723        use crate::input_handler::InputHandlerStatus;
2724        use crate::{Position, input_device, mouse_binding, touch_binding};
2725        use assert_matches::assert_matches;
2726
2727        use maplit::hashset;
2728        use pretty_assertions::assert_eq;
2729        use std::cell::RefCell;
2730        use std::rc::Rc;
2731        use test_case::test_case;
2732
2733        fn make_forwarding_arena(
2746            winner: StubWinner,
2747            contender: Option<Box<dyn Contender>>,
2748        ) -> Rc<GestureArena> {
2749            let contender_factory = match contender {
2750                Some(c) => Box::new(ContenderFactoryOnceOrPanic::new(vec![c])),
2751                None => Box::new(ContenderFactoryOnceOrPanic::new_panic_when_call()),
2752            };
2753
2754            Rc::new(GestureArena {
2755                contender_factory,
2756                mutable_state: RefCell::new(MutableState::Forwarding {
2757                    winner: winner.into(),
2758                    current_gesture: RecognizedGesture::Motion,
2759                    gesture_start_timestamp: zx::MonotonicInstant::INFINITE_PAST,
2763                    num_events: 0,
2764                }),
2765                inspect_log: RefCell::new(fuchsia_inspect_contrib::nodes::BoundedListNode::new(
2766                    fuchsia_inspect::Inspector::default().root().create_child("some_key"),
2767                    1,
2768                )),
2769                inspect_status: InputHandlerStatus::default(),
2770            })
2771        }
2772
2773        #[fuchsia::test(allow_stalls = false)]
2774        async fn invokes_process_new_event_on_touchpad_event() {
2775            let winner = StubWinner::new();
2776            let arena = make_forwarding_arena(winner.clone(), None);
2777            winner.set_next_result(ProcessNewEventResult::EndGesture(
2778                EndGestureEvent::NoEvent,
2779                Reason::Basic("some reason"),
2780            ));
2781            arena.handle_input_event(make_unhandled_touchpad_event()).await;
2782            assert_eq!(winner.calls_received(), 1);
2783        }
2784
2785        #[fuchsia::test(allow_stalls = false)]
2786        async fn does_not_invoke_process_new_event_on_mouse_event() {
2787            let winner = StubWinner::new();
2788            let arena = make_forwarding_arena(winner.clone(), None);
2789            winner.set_next_result(ProcessNewEventResult::EndGesture(
2790                EndGestureEvent::NoEvent,
2791                Reason::Basic("some reason"),
2792            ));
2793            arena.handle_input_event(make_unhandled_mouse_event()).await;
2794            assert_eq!(winner.calls_received(), 0);
2795        }
2796
2797        #[fuchsia::test(allow_stalls = false)]
2798        async fn does_not_invoke_process_new_event_on_keyboard_event() {
2799            let winner = StubWinner::new();
2800            let arena = make_forwarding_arena(winner.clone(), None);
2801            winner.set_next_result(ProcessNewEventResult::EndGesture(
2802                EndGestureEvent::NoEvent,
2803                Reason::Basic("some reason"),
2804            ));
2805            arena.handle_input_event(make_unhandled_keyboard_event()).await;
2806            assert_eq!(winner.calls_received(), 0);
2807        }
2808
2809        #[fuchsia::test(allow_stalls = false)]
2810        async fn invokes_process_new_event_for_multiple_new_events() {
2811            let winner = StubWinner::new();
2815            let arena = make_forwarding_arena(winner.clone(), Some(ContenderForever {}.into()));
2816
2817            winner.set_next_result(ProcessNewEventResult::ContinueGesture(
2819                None,
2820                winner.clone().into(),
2821            ));
2822            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
2823            winner.set_next_result(ProcessNewEventResult::ContinueGesture(
2824                None,
2825                winner.clone().into(),
2826            ));
2827            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
2828
2829            assert_eq!(winner.calls_received(), 2);
2831        }
2832
2833        #[fuchsia::test(allow_stalls = false)]
2834        async fn generates_event_on_continue_gesture_with_mouse_event() {
2835            let winner = StubWinner::new();
2836            let arena = make_forwarding_arena(winner.clone(), None);
2837            winner.set_next_result(ProcessNewEventResult::ContinueGesture(
2838                Some(MouseEvent {
2839                    timestamp: zx::MonotonicInstant::from_nanos(123),
2840                    mouse_data: mouse_binding::MouseEvent {
2841                        location: mouse_binding::MouseLocation::Relative(
2842                            mouse_binding::RelativeLocation { millimeters: Position::zero() },
2843                        ),
2844                        wheel_delta_v: None,
2845                        wheel_delta_h: None,
2846                        phase: mouse_binding::MousePhase::Move,
2847                        affected_buttons: hashset! {},
2848                        pressed_buttons: hashset! {},
2849                        is_precision_scroll: None,
2850                    },
2851                }),
2852                winner.clone().into(),
2853            ));
2854            assert_matches!(
2855                arena.handle_input_event(make_unhandled_touchpad_event()).await.as_slice(),
2856                [
2857                    input_device::InputEvent {
2858                        event_time,
2859                        handled: input_device::Handled::No,
2860                        device_event: input_device::InputDeviceEvent::Mouse(_),
2861                        ..
2862                    },
2863                ] => pretty_assertions::assert_eq!(*event_time, zx::MonotonicInstant::from_nanos(123))
2864            );
2865        }
2866
2867        #[fuchsia::test(allow_stalls = false)]
2868        async fn generates_no_events_on_continue_gesture_without_mouse_event() {
2869            let winner = StubWinner::new();
2870            let arena = make_forwarding_arena(winner.clone(), None);
2871            winner.set_next_result(ProcessNewEventResult::ContinueGesture(
2872                None,
2873                winner.clone().into(),
2874            ));
2875            pretty_assertions::assert_eq!(
2876                arena.handle_input_event(make_unhandled_touchpad_event()).await.as_slice(),
2877                vec![]
2878            );
2879        }
2880
2881        #[fuchsia::test(allow_stalls = false)]
2882        async fn generates_no_events_on_end_gesture_without_touchpad_event() {
2883            let winner = StubWinner::new();
2884            let arena = make_forwarding_arena(winner.clone(), None);
2885            winner.set_next_result(ProcessNewEventResult::EndGesture(
2886                EndGestureEvent::NoEvent,
2887                Reason::Basic("some reason"),
2888            ));
2889            pretty_assertions::assert_eq!(
2890                arena.handle_input_event(make_unhandled_touchpad_event()).await.as_slice(),
2891                vec![]
2892            );
2893        }
2894
2895        #[fuchsia::test(allow_stalls = false)]
2896        async fn generates_no_events_on_end_gesture_with_unconsumed_touchpad_event_entering_idle() {
2897            let winner = StubWinner::new();
2900            let contender = StubContender::new();
2901            let arena = make_forwarding_arena(winner.clone(), Some(contender.clone().into()));
2902            winner.set_next_result(ProcessNewEventResult::EndGesture(
2903                EndGestureEvent::UnconsumedEvent(TouchpadEvent {
2904                    contacts: vec![],
2905                    pressed_buttons: vec![],
2906                    timestamp: zx::MonotonicInstant::ZERO,
2907                    filtered_palm_contacts: vec![],
2908                }),
2909                Reason::Basic("some reason"),
2910            ));
2911
2912            contender.set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
2914
2915            pretty_assertions::assert_eq!(
2917                arena.clone().handle_input_event(make_unhandled_touchpad_event()).await.as_slice(),
2918                vec![]
2919            );
2920
2921            assert_matches!(*arena.mutable_state.borrow(), MutableState::Idle { .. });
2924        }
2925
2926        #[fuchsia::test(allow_stalls = false)]
2927        async fn generates_no_events_on_end_gesture_with_unconsumed_touchpad_event_entering_chain()
2928        {
2929            let winner = StubWinner::new();
2932            let contender = StubContender::new();
2933            let arena = make_forwarding_arena(winner.clone(), Some(contender.clone().into()));
2934            winner.set_next_result(ProcessNewEventResult::EndGesture(
2935                EndGestureEvent::UnconsumedEvent(TouchpadEvent {
2936                    contacts: vec![touch_binding::TouchContact {
2937                        id: 1,
2938                        position: Position { x: 0.0, y: 0.0 },
2939                        pressure: None,
2940                        contact_size: None,
2941                    }],
2942                    pressed_buttons: vec![],
2943                    timestamp: zx::MonotonicInstant::from_nanos(123456),
2944                    filtered_palm_contacts: vec![],
2945                }),
2946                Reason::Basic("some reason"),
2947            ));
2948
2949            contender.set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
2951
2952            let touchpad_event = input_device::InputEvent {
2953                event_time: zx::MonotonicInstant::from_nanos(123456),
2954                device_event: input_device::InputDeviceEvent::Touchpad(
2955                    touch_binding::TouchpadEvent {
2956                        injector_contacts: vec![touch_binding::TouchContact {
2957                            id: 1,
2958                            position: Position { x: 0.0, y: 0.0 },
2959                            ..TOUCH_CONTACT_INDEX_FINGER
2960                        }],
2961                        pressed_buttons: hashset! {},
2962                    },
2963                ),
2964                device_descriptor: make_touchpad_descriptor(),
2965                trace_id: None,
2966                handled: input_device::Handled::No,
2967            };
2968
2969            pretty_assertions::assert_eq!(
2971                arena.clone().handle_input_event(touchpad_event).await.as_slice(),
2972                vec![]
2973            );
2974
2975            assert_matches!(*arena.mutable_state.borrow(), MutableState::Chain { .. });
2978        }
2979
2980        #[fuchsia::test(allow_stalls = false)]
2981        async fn generates_event_on_end_gesture_with_touchpad_event() {
2982            let winner = StubWinner::new();
2985            let arena = make_forwarding_arena(winner.clone(), None);
2986            let mouse_event = MouseEvent {
2987                timestamp: zx::MonotonicInstant::from_nanos(123),
2988                mouse_data: mouse_binding::MouseEvent {
2989                    location: mouse_binding::MouseLocation::Relative(
2990                        mouse_binding::RelativeLocation { millimeters: Position::zero() },
2991                    ),
2992                    wheel_delta_v: None,
2993                    wheel_delta_h: None,
2994                    phase: mouse_binding::MousePhase::Move,
2995                    affected_buttons: hashset! {},
2996                    pressed_buttons: hashset! {},
2997                    is_precision_scroll: None,
2998                },
2999            };
3000            winner.set_next_result(ProcessNewEventResult::EndGesture(
3001                EndGestureEvent::GeneratedEvent(mouse_event),
3002                Reason::Basic("some reason"),
3003            ));
3004
3005            assert_matches!(
3007                arena.handle_input_event(make_unhandled_touchpad_event()).await.as_slice(),
3008                [
3009                    input_device::InputEvent {
3010                        event_time,
3011                        handled: input_device::Handled::No,
3012                        device_event: input_device::InputDeviceEvent::Mouse(_),
3013                        ..
3014                    },
3015                ] => pretty_assertions::assert_eq!(*event_time, zx::MonotonicInstant::from_nanos(123))
3016            );
3017        }
3018
3019        #[test_case(Some(MouseEvent{
3020            timestamp: zx::MonotonicInstant::from_nanos(123),
3021            mouse_data: mouse_binding::MouseEvent {
3022                location: mouse_binding::MouseLocation::Relative(
3023                    mouse_binding::RelativeLocation {
3024                        millimeters: Position::zero(),
3025                    },
3026                ),
3027                wheel_delta_v: None,
3028                wheel_delta_h: None,
3029                phase: mouse_binding::MousePhase::Move,
3030                affected_buttons: hashset! {},
3031                pressed_buttons: hashset! {},
3032                is_precision_scroll: None,
3033            },
3034        }); "with_mouse_event")]
3035        #[test_case(None; "without_mouse_event")]
3036        #[fuchsia::test(allow_stalls = false)]
3037        async fn remains_in_forwarding_on_continue_gesture(mouse_event: Option<MouseEvent>) {
3038            let winner = StubWinner::new();
3039            let arena = make_forwarding_arena(winner.clone(), None);
3040            winner.set_next_result(ProcessNewEventResult::ContinueGesture(
3041                mouse_event,
3042                winner.clone().into(),
3043            ));
3044            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
3045            assert_matches!(*arena.mutable_state.borrow(), MutableState::Forwarding { .. });
3046        }
3047
3048        #[fuchsia::test(allow_stalls = false)]
3049        async fn transitions_to_idle_on_end_gesture_with_touchpad_event() {
3050            let winner = StubWinner::new();
3051            let arena = make_forwarding_arena(winner.clone(), None);
3052            let mouse_event = MouseEvent {
3053                timestamp: zx::MonotonicInstant::from_nanos(123),
3054                mouse_data: mouse_binding::MouseEvent {
3055                    location: mouse_binding::MouseLocation::Relative(
3056                        mouse_binding::RelativeLocation { millimeters: Position::zero() },
3057                    ),
3058                    wheel_delta_v: None,
3059                    wheel_delta_h: None,
3060                    phase: mouse_binding::MousePhase::Move,
3061                    affected_buttons: hashset! {},
3062                    pressed_buttons: hashset! {},
3063                    is_precision_scroll: None,
3064                },
3065            };
3066            winner.set_next_result(ProcessNewEventResult::EndGesture(
3067                EndGestureEvent::GeneratedEvent(mouse_event),
3068                Reason::Basic("some reason"),
3069            ));
3070            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
3071            assert_matches!(*arena.mutable_state.borrow(), MutableState::Idle { .. });
3072        }
3073
3074        #[fuchsia::test(allow_stalls = false)]
3075        async fn transitions_to_chain_on_end_gesture_with_touchpad_event() {
3076            let winner = StubWinner::new();
3077            let arena = make_forwarding_arena(winner.clone(), None);
3078            let mouse_event = MouseEvent {
3079                timestamp: zx::MonotonicInstant::from_nanos(123),
3080                mouse_data: mouse_binding::MouseEvent {
3081                    location: mouse_binding::MouseLocation::Relative(
3082                        mouse_binding::RelativeLocation { millimeters: Position::zero() },
3083                    ),
3084                    wheel_delta_v: None,
3085                    wheel_delta_h: None,
3086                    phase: mouse_binding::MousePhase::Move,
3087                    affected_buttons: hashset! {},
3088                    pressed_buttons: hashset! {},
3089                    is_precision_scroll: None,
3090                },
3091            };
3092            winner.set_next_result(ProcessNewEventResult::EndGesture(
3093                EndGestureEvent::GeneratedEvent(mouse_event),
3094                Reason::Basic("some reason"),
3095            ));
3096            let touchpad_event = input_device::InputEvent {
3097                event_time: zx::MonotonicInstant::from_nanos(123456),
3098                device_event: input_device::InputDeviceEvent::Touchpad(
3099                    touch_binding::TouchpadEvent {
3100                        injector_contacts: vec![touch_binding::TouchContact {
3101                            id: 1,
3102                            position: Position { x: 0.0, y: 0.0 },
3103                            ..TOUCH_CONTACT_INDEX_FINGER
3104                        }],
3105                        pressed_buttons: hashset! {},
3106                    },
3107                ),
3108                device_descriptor: make_touchpad_descriptor(),
3109                trace_id: None,
3110                handled: input_device::Handled::No,
3111            };
3112            arena.clone().handle_input_event(touchpad_event).await;
3113            assert_matches!(*arena.mutable_state.borrow(), MutableState::Chain);
3114        }
3115
3116        #[fuchsia::test(allow_stalls = false)]
3117        async fn transitions_to_idle_on_end_gesture_without_touchpad_event() {
3118            let winner = StubWinner::new();
3119            let arena = make_forwarding_arena(winner.clone(), None);
3120            winner.set_next_result(ProcessNewEventResult::EndGesture(
3121                EndGestureEvent::NoEvent,
3122                Reason::Basic("reason"),
3123            ));
3124            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
3125            assert_matches!(*arena.mutable_state.borrow(), MutableState::Idle);
3126        }
3127
3128        #[fuchsia::test(allow_stalls = false)]
3129        async fn starts_new_contest_on_end_gesture_with_touchpad_event() {
3130            let winner = StubWinner::new();
3134            let contender = StubContender::new();
3135            let arena = make_forwarding_arena(winner.clone(), Some(contender.clone().into()));
3136
3137            winner.set_next_result(ProcessNewEventResult::EndGesture(
3139                EndGestureEvent::UnconsumedEvent(TouchpadEvent {
3140                    timestamp: zx::MonotonicInstant::ZERO,
3141                    contacts: vec![],
3142                    pressed_buttons: vec![],
3143                    filtered_palm_contacts: vec![],
3144                }),
3145                Reason::Basic("reason"),
3146            ));
3147
3148            contender.set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
3150
3151            arena.handle_input_event(make_unhandled_touchpad_event()).await;
3153
3154            assert_eq!(contender.calls_received(), 1);
3156        }
3157    }
3158
3159    mod touchpad_event_payload {
3160        use super::super::{ExamineEventResult, GestureArena, InputHandler, Reason, args};
3161        use super::utils::{
3162            ContenderFactoryOnceOrPanic, StubContender, TOUCH_CONTACT_INDEX_FINGER,
3163        };
3164        use crate::utils::Size;
3165        use crate::{Position, input_device, touch_binding};
3166        use assert_matches::assert_matches;
3167        use fidl_fuchsia_input_report::{self as fidl_input_report, UnitType};
3168
3169        use maplit::hashset;
3170        use std::rc::Rc;
3171        use test_case::test_case;
3172        use test_util::assert_near;
3173
3174        fn make_touchpad_descriptor(
3175            units: Vec<(fidl_input_report::Unit, fidl_input_report::Unit)>,
3176        ) -> input_device::InputDeviceDescriptor {
3177            let contacts: Vec<_> = units
3178                .into_iter()
3179                .map(|(x_unit, y_unit)| touch_binding::ContactDeviceDescriptor {
3180                    x_range: fidl_input_report::Range { min: 0, max: 1_000_000 },
3181                    y_range: fidl_input_report::Range { min: 0, max: 1_000_000 },
3182                    x_unit,
3183                    y_unit,
3184                    pressure_range: None,
3185                    width_range: Some(fidl_input_report::Range { min: 0, max: 10_000 }),
3186                    height_range: Some(fidl_input_report::Range { min: 0, max: 10_000 }),
3187                })
3188                .collect();
3189            input_device::InputDeviceDescriptor::Touchpad(touch_binding::TouchpadDeviceDescriptor {
3190                device_id: 1,
3191                contacts,
3192            })
3193        }
3194
3195        fn make_unhandled_touchpad_event_with_contacts(
3196            contact_position_units: Vec<(fidl_input_report::Unit, fidl_input_report::Unit)>,
3197            injector_contacts: Vec<touch_binding::TouchContact>,
3198        ) -> input_device::InputEvent {
3199            input_device::InputEvent {
3200                device_event: input_device::InputDeviceEvent::Touchpad(
3201                    touch_binding::TouchpadEvent {
3202                        injector_contacts,
3203                        pressed_buttons: hashset! {},
3204                    },
3205                ),
3206                device_descriptor: make_touchpad_descriptor(contact_position_units),
3207                event_time: zx::MonotonicInstant::ZERO,
3208                trace_id: None,
3209                handled: input_device::Handled::No,
3210            }
3211        }
3212
3213        fn make_unhandled_touchpad_event_with_positions(
3214            contact_position_units: Vec<(fidl_input_report::Unit, fidl_input_report::Unit)>,
3215            positions: Vec<Position>,
3216        ) -> input_device::InputEvent {
3217            let injector_contacts: Vec<_> = positions
3218                .into_iter()
3219                .enumerate()
3220                .map(|(i, position)| touch_binding::TouchContact {
3221                    id: u32::try_from(i).unwrap(),
3222                    position,
3223                    contact_size: None,
3224                    pressure: None,
3225                })
3226                .collect();
3227            make_unhandled_touchpad_event_with_contacts(contact_position_units, injector_contacts)
3228        }
3229
3230        #[test_case(
3231            vec![(
3232                fidl_input_report::Unit{ type_: UnitType::Meters, exponent: -6 },
3233                fidl_input_report::Unit{ type_: UnitType::Meters, exponent: -6 },
3234            )],
3235            vec![
3236                touch_binding::TouchContact{
3237                    id: 1,
3238                    position: Position { x: 200000.0, y: 100000.0 },
3239                    contact_size: Some(Size {
3240                        width: 2500.0,
3241                        height: 1500.0,
3242                    }),
3243                    pressure: None,
3244                }
3245            ]; "from_micrometers")]
3246        #[test_case(
3247            vec![(
3248                fidl_input_report::Unit{ type_: UnitType::Meters, exponent: -2 },
3249                fidl_input_report::Unit{ type_: UnitType::Meters, exponent: -2 },
3250            )],
3251            vec![
3252                touch_binding::TouchContact{
3253                    id: 1,
3254                    position: Position { x: 20.0, y: 10.0 },
3255                    contact_size: Some(Size {
3256                        width: 0.25,
3257                        height: 0.15,
3258                    }),
3259                    pressure: None,
3260                }
3261            ]; "from_centimeters")]
3262        #[fuchsia::test(allow_stalls = false)]
3263        async fn provides_recognizer_position_size_in_millimeters(
3264            contact_position_units: Vec<(fidl_input_report::Unit, fidl_input_report::Unit)>,
3265            contacts: Vec<touch_binding::TouchContact>,
3266        ) {
3267            let contender = Box::new(StubContender::new());
3268            let contender_factory = ContenderFactoryOnceOrPanic::new(vec![contender.clone()]);
3269            let arena = Rc::new(GestureArena::new_for_test(
3270                Box::new(contender_factory),
3271                &fuchsia_inspect::Inspector::default(),
3272                1,
3273            ));
3274            contender.set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
3275            arena
3276                .handle_input_event(make_unhandled_touchpad_event_with_contacts(
3277                    contact_position_units,
3278                    contacts,
3279                ))
3280                .await;
3281            assert_matches!(
3282                contender.get_last_touchpad_event().unwrap().contacts.as_slice(),
3283                [touch_binding::TouchContact { position, contact_size: Some(size), .. }] => {
3284                    assert_near!(position.x, 200.0, 1.0);
3285                    assert_near!(position.y, 100.0, 1.0);
3286                    assert_near!(size.width, 2.5, 0.1);
3287                    assert_near!(size.height, 1.5, 0.1);
3288                }
3289            );
3290        }
3291
3292        #[test_case(
3293            touch_binding::TouchpadEvent {
3294                injector_contacts: vec![
3295                    touch_binding::TouchContact{
3296                        id: 1,
3297                        position: Position { x: 0.0, y: 0.0 },
3298                        contact_size: Some(Size {
3299                            width: args::MIN_PALM_SIZE_MM,
3300                            height: 0.15,
3301                        }),
3302                        pressure: None,
3303                    }
3304                ],
3305                pressed_buttons: hashset! {},
3306            }; "only palm contact"
3307        )]
3308        #[fuchsia::test(allow_stalls = false)]
3309        async fn ignore_palm_contact(event: touch_binding::TouchpadEvent) {
3310            let contender = Box::new(StubContender::new());
3311            let contender_factory = ContenderFactoryOnceOrPanic::new(vec![contender.clone()]);
3312            let arena = Rc::new(GestureArena::new_for_test(
3313                Box::new(contender_factory),
3314                &fuchsia_inspect::Inspector::default(),
3315                1,
3316            ));
3317            contender.set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
3318
3319            let input_event = input_device::InputEvent {
3320                device_event: input_device::InputDeviceEvent::Touchpad(event),
3321                device_descriptor: make_touchpad_descriptor(vec![(
3322                    fidl_input_report::Unit { type_: UnitType::Meters, exponent: -3 },
3323                    fidl_input_report::Unit { type_: UnitType::Meters, exponent: -3 },
3324                )]),
3325                event_time: zx::MonotonicInstant::ZERO,
3326                trace_id: None,
3327                handled: input_device::Handled::No,
3328            };
3329
3330            arena.handle_input_event(input_event).await;
3331            assert_matches!(contender.get_last_touchpad_event().unwrap().contacts.as_slice(), []);
3332        }
3333
3334        #[test_case(
3335            touch_binding::TouchpadEvent {
3336                injector_contacts: vec![
3337                    TOUCH_CONTACT_INDEX_FINGER,
3338                    touch_binding::TouchContact{
3339                        id: 1,
3340                        position: Position { x: 0.0, y: 0.0 },
3341                        contact_size: Some(Size {
3342                            width: args::MIN_PALM_SIZE_MM,
3343                            height: 0.15,
3344                        }),
3345                        pressure: None,
3346                    }
3347                ],
3348                pressed_buttons: hashset! {},
3349            }, vec![]; "palm contact and finger"
3350        )]
3351        #[fuchsia::test(allow_stalls = false)]
3352        async fn ignore_palm_contact_keep_finger(
3353            event: touch_binding::TouchpadEvent,
3354            expect_buttons: Vec<u8>,
3355        ) {
3356            let contender = Box::new(StubContender::new());
3357            let contender_factory = ContenderFactoryOnceOrPanic::new(vec![contender.clone()]);
3358            let arena = Rc::new(GestureArena::new_for_test(
3359                Box::new(contender_factory),
3360                &fuchsia_inspect::Inspector::default(),
3361                1,
3362            ));
3363            contender.set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
3364
3365            let input_event = input_device::InputEvent {
3366                device_event: input_device::InputDeviceEvent::Touchpad(event),
3367                device_descriptor: make_touchpad_descriptor(vec![(
3368                    fidl_input_report::Unit { type_: UnitType::Meters, exponent: -3 },
3369                    fidl_input_report::Unit { type_: UnitType::Meters, exponent: -3 },
3370                )]),
3371                event_time: zx::MonotonicInstant::ZERO,
3372                trace_id: None,
3373                handled: input_device::Handled::No,
3374            };
3375
3376            arena.handle_input_event(input_event).await;
3377            let got = contender.get_last_touchpad_event().unwrap();
3378            assert_eq!(got.contacts.as_slice(), [TOUCH_CONTACT_INDEX_FINGER]);
3379            assert_eq!(got.pressed_buttons, expect_buttons);
3380        }
3381
3382        #[test_case(
3383            touch_binding::TouchpadEvent {
3384                injector_contacts: vec![
3385                    touch_binding::TouchContact{
3386                        id: 1,
3387                        position: Position { x: 0.0, y: 0.0 },
3388                        contact_size: Some(Size {
3389                            width: args::MIN_PALM_SIZE_MM,
3390                            height: 0.15,
3391                        }),
3392                        pressure: None,
3393                    }
3394                ],
3395                pressed_buttons: hashset! {1},
3396            }; "palm contact"
3397        )]
3398        #[test_case(
3399            touch_binding::TouchpadEvent {
3400                injector_contacts: vec![
3401                    touch_binding::TouchContact{
3402                        id: 1,
3403                        position: Position { x: 0.0, y: 0.0 },
3404                        contact_size: Some(Size {
3405                            width: args::MIN_PALM_SIZE_MM,
3406                            height: 0.15,
3407                        }),
3408                        pressure: None,
3409                    },
3410                    touch_binding::TouchContact{
3411                        id: 2,
3412                        position: Position { x: 5.0, y: 5.0 },
3413                        contact_size: Some(Size {
3414                            width: args::MIN_PALM_SIZE_MM / 2.0,
3415                            height: 0.15,
3416                        }),
3417                        pressure: None,
3418                    },
3419                ],
3420                pressed_buttons: hashset! {1},
3421            }; "palm and finger contact"
3422        )]
3423        #[fuchsia::test(allow_stalls = false)]
3424        async fn skip_palm_detection_when_button_down(event: touch_binding::TouchpadEvent) {
3425            let contender = Box::new(StubContender::new());
3426            let contender_factory = ContenderFactoryOnceOrPanic::new(vec![contender.clone()]);
3427            let arena = Rc::new(GestureArena::new_for_test(
3428                Box::new(contender_factory),
3429                &fuchsia_inspect::Inspector::default(),
3430                1,
3431            ));
3432            contender.set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
3433
3434            let count_of_contact = event.injector_contacts.len();
3435            let input_event = input_device::InputEvent {
3436                device_event: input_device::InputDeviceEvent::Touchpad(event),
3437                device_descriptor: make_touchpad_descriptor(vec![(
3438                    fidl_input_report::Unit { type_: UnitType::Meters, exponent: -3 },
3439                    fidl_input_report::Unit { type_: UnitType::Meters, exponent: -3 },
3440                )]),
3441                event_time: zx::MonotonicInstant::ZERO,
3442                trace_id: None,
3443                handled: input_device::Handled::No,
3444            };
3445
3446            arena.handle_input_event(input_event).await;
3447            assert_eq!(
3448                contender.get_last_touchpad_event().unwrap().contacts.len(),
3449                count_of_contact
3450            );
3451        }
3452
3453        #[test_case(
3454            vec![(
3455                fidl_input_report::Unit{ type_: UnitType::None, exponent: -6 },
3456                fidl_input_report::Unit{ type_: UnitType::None, exponent: -6 },
3457            )],
3458            vec![];
3459            "both units unspecified")]
3460        #[test_case(
3461            vec![(
3462                fidl_input_report::Unit{ type_: UnitType::None, exponent: -6 },
3463                fidl_input_report::Unit{ type_: UnitType::Meters, exponent: -6 },
3464            )],
3465            vec![];
3466            "x unit unspecified")]
3467        #[test_case(
3468            vec![(
3469                fidl_input_report::Unit{ type_: UnitType::Meters, exponent: -6 },
3470                fidl_input_report::Unit{ type_: UnitType::None, exponent: -6 },
3471            )],
3472            vec![];
3473            "y unit unspecified")]
3474        #[test_case(
3475            vec![(
3476                fidl_input_report::Unit{ type_: UnitType::Meters, exponent: -3 },
3477                fidl_input_report::Unit{ type_: UnitType::Meters, exponent: -6 },
3478            )],
3479            vec![];
3480            "mismatched exponents")]
3481        #[test_case(
3482            vec![
3483                (
3484                    fidl_input_report::Unit{ type_: UnitType::Meters, exponent: -3 },
3485                    fidl_input_report::Unit{ type_: UnitType::Meters, exponent: -3 },
3486                ),
3487                (
3488                    fidl_input_report::Unit{ type_: UnitType::Meters, exponent: -6 },
3489                    fidl_input_report::Unit{ type_: UnitType::Meters, exponent: -6 },
3490                ),
3491            ],
3492            vec![];
3493            "unequal divisors")]
3494        #[fuchsia::test(allow_stalls = false)]
3495        async fn skips_contender_on_bad_descriptor(
3496            contact_position_units: Vec<(fidl_input_report::Unit, fidl_input_report::Unit)>,
3497            positions: Vec<Position>,
3498        ) {
3499            let contender = Box::new(StubContender::new());
3500            let contender_factory = ContenderFactoryOnceOrPanic::new(vec![contender.clone()]);
3501            let arena = Rc::new(GestureArena::new_for_test(
3502                Box::new(contender_factory),
3503                &fuchsia_inspect::Inspector::default(),
3504                1,
3505            ));
3506            contender.set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
3507            arena
3508                .handle_input_event(make_unhandled_touchpad_event_with_positions(
3509                    contact_position_units,
3510                    positions,
3511                ))
3512                .await;
3513            assert_eq!(contender.calls_received(), 0);
3514        }
3515    }
3516
3517    mod inspect {
3518        use super::super::{
3519            Contender, ContenderFactory, DetailedReasonFloat, DetailedReasonInt,
3520            DetailedReasonUint, EndGestureEvent, ExamineEventResult, GestureArena, InputHandler,
3521            MouseEvent, ProcessBufferedEventsResult, ProcessNewEventResult, Reason,
3522            RecognizedGesture, TouchpadEvent, args,
3523        };
3524        use super::utils::{
3525            ContenderFactoryOnceOrPanic, StubContender, StubMatchedContender, StubWinner,
3526            make_touchpad_descriptor, make_unhandled_keyboard_event, make_unhandled_mouse_event,
3527            make_unhandled_touchpad_event,
3528        };
3529        use crate::{Position, Size, input_device, keyboard_binding, mouse_binding, touch_binding};
3530        use assert_matches::assert_matches;
3531        use maplit::hashset;
3532        use std::rc::Rc;
3533        use test_case::test_case;
3534        use {fidl_fuchsia_input_report as fidl_input_report, fuchsia_async as fasync};
3535
3536        struct EmptyContenderFactory {}
3537
3538        impl ContenderFactory for EmptyContenderFactory {
3539            fn make_contenders(&self) -> Vec<Box<dyn crate::gestures::gesture_arena::Contender>> {
3540                vec![]
3541            }
3542        }
3543
3544        #[fuchsia::test]
3545        async fn gesture_arena_initialized_with_inspect_node() {
3546            let inspector = fuchsia_inspect::Inspector::default();
3547            let fake_handlers_node = inspector.root().create_child("input_handlers_node");
3548            let _handler = GestureArena::new_internal(
3549                Box::new(EmptyContenderFactory {}),
3550                &inspector.root(),
3551                2,
3552                &fake_handlers_node,
3553            );
3554            diagnostics_assertions::assert_data_tree!(inspector, root: {
3555                gestures_event_log: {},
3556                input_handlers_node: {
3557                    gesture_arena: {
3558                        events_received_count: 0u64,
3559                        events_handled_count: 0u64,
3560                        last_received_timestamp_ns: 0u64,
3561                        "fuchsia.inspect.Health": {
3562                            status: "STARTING_UP",
3563                            start_timestamp_nanos: diagnostics_assertions::AnyProperty
3566                        },
3567                    }
3568                }
3569            });
3570        }
3571
3572        #[fasync::run_singlethreaded(test)]
3573        async fn gesture_arena_inspect_counts_events() {
3574            let inspector = fuchsia_inspect::Inspector::default();
3575            let fake_handlers_node = inspector.root().create_child("input_handlers_node");
3576            let arena = Rc::new(GestureArena::new_internal(
3577                Box::new(EmptyContenderFactory {}),
3578                &inspector.root(),
3579                1,
3580                &fake_handlers_node,
3581            ));
3582
3583            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
3584            arena.clone().handle_input_event(make_unhandled_mouse_event()).await;
3585            arena.clone().handle_input_event(make_unhandled_keyboard_event()).await;
3586            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
3587
3588            diagnostics_assertions::assert_data_tree!(inspector, root: contains {
3589                input_handlers_node: {
3590                    gesture_arena: {
3591                        events_received_count: 2u64,
3592                        events_handled_count: 0u64,
3593                        last_received_timestamp_ns: 0u64,
3594                        "fuchsia.inspect.Health": {
3595                            status: "STARTING_UP",
3596                            start_timestamp_nanos: diagnostics_assertions::AnyProperty
3599                        },
3600                    }
3601                }
3602            });
3603        }
3604
3605        #[fuchsia::test]
3606        fn logs_to_inspect() {
3607            let mut executor = fasync::TestExecutor::new_with_fake_time();
3608            let basic_mismatch_contender = Box::new(StubContender::new());
3609            let detailed_uint_mismatch_contender = Box::new(StubContender::new());
3610            let detailed_float_mismatch_contender = Box::new(StubContender::new());
3611            let detailed_int_mismatch_contender = Box::new(StubContender::new());
3612            let gesture_matching_contender = Box::new(StubContender::new());
3613            basic_mismatch_contender
3614                .set_next_result(ExamineEventResult::Mismatch(Reason::Basic("some reason")));
3615            detailed_uint_mismatch_contender.set_next_result(ExamineEventResult::Mismatch(
3616                Reason::DetailedUint(DetailedReasonUint {
3617                    criterion: "num_goats_teleported",
3618                    min: Some(10),
3619                    max: Some(30),
3620                    actual: 42,
3621                }),
3622            ));
3623            detailed_float_mismatch_contender.set_next_result(ExamineEventResult::Mismatch(
3624                Reason::DetailedFloat(DetailedReasonFloat {
3625                    criterion: "teleportation_distance_kilometers",
3626                    min: Some(10.125),
3627                    max: Some(30.5),
3628                    actual: 42.0,
3629                }),
3630            ));
3631            detailed_int_mismatch_contender.set_next_result(ExamineEventResult::Mismatch(
3632                Reason::DetailedInt(DetailedReasonInt {
3633                    criterion: "budget_surplus_trillions",
3634                    min: Some(-10),
3635                    max: Some(1),
3636                    actual: -42,
3637                }),
3638            ));
3639
3640            let inspector = fuchsia_inspect::Inspector::default();
3641            let contender_factory = Box::new(ContenderFactoryOnceOrPanic::new(vec![
3642                basic_mismatch_contender,
3643                detailed_uint_mismatch_contender,
3644                detailed_float_mismatch_contender,
3645                detailed_int_mismatch_contender,
3646                gesture_matching_contender.clone(),
3647            ]));
3648            let arena = Rc::new(GestureArena::new_for_test(contender_factory, &inspector, 100));
3649            let touchpad_descriptor = input_device::InputDeviceDescriptor::Touchpad(
3650                touch_binding::TouchpadDeviceDescriptor {
3651                    device_id: 1,
3652                    contacts: vec![touch_binding::ContactDeviceDescriptor {
3653                        x_range: fidl_input_report::Range { min: 0, max: 10_000 },
3654                        y_range: fidl_input_report::Range { min: 0, max: 10_000 },
3655                        x_unit: fidl_input_report::Unit {
3656                            type_: fidl_input_report::UnitType::Meters,
3658                            exponent: -3,
3659                        },
3660                        y_unit: fidl_input_report::Unit {
3661                            type_: fidl_input_report::UnitType::Meters,
3663                            exponent: -3,
3664                        },
3665                        pressure_range: None,
3666                        width_range: Some(fidl_input_report::Range { min: 0, max: 10_000 }),
3667                        height_range: Some(fidl_input_report::Range { min: 0, max: 10_000 }),
3668                    }],
3669                },
3670            );
3671            let keyboard_descriptor = input_device::InputDeviceDescriptor::Keyboard(
3672                keyboard_binding::KeyboardDeviceDescriptor {
3673                    device_id: 2,
3674                    device_information: fidl_fuchsia_input_report::DeviceInformation {
3675                        vendor_id: Some(0),
3676                        product_id: Some(0),
3677                        version: Some(0),
3678                        polling_rate: Some(0),
3679                        ..Default::default()
3680                    },
3681                    keys: vec![fidl_fuchsia_input::Key::A, fidl_fuchsia_input::Key::B],
3682                },
3683            );
3684
3685            let mut handle_event_fut = arena.clone().handle_input_event(input_device::InputEvent {
3687                device_event: input_device::InputDeviceEvent::Touchpad(
3688                    touch_binding::TouchpadEvent {
3689                        injector_contacts: vec![
3690                            touch_binding::TouchContact {
3691                                id: 1u32,
3692                                position: Position { x: 2.0, y: 3.0 },
3693                                contact_size: None,
3694                                pressure: None,
3695                            },
3696                            touch_binding::TouchContact {
3697                                id: 2u32,
3698                                position: Position { x: 40.0, y: 50.0 },
3699                                contact_size: None,
3700                                pressure: None,
3701                            },
3702                        ],
3703                        pressed_buttons: hashset! {1},
3704                    },
3705                ),
3706                device_descriptor: touchpad_descriptor.clone(),
3707                event_time: zx::MonotonicInstant::from_nanos(12_300),
3708                trace_id: None,
3709                handled: input_device::Handled::No,
3710            });
3711            executor.set_fake_time(fasync::MonotonicInstant::from_nanos(10_000_000));
3712            gesture_matching_contender
3713                .set_next_result(ExamineEventResult::Contender(gesture_matching_contender.clone()));
3714            assert_matches!(
3715                executor.run_until_stalled(&mut handle_event_fut),
3716                std::task::Poll::Ready(_)
3717            );
3718
3719            let mut handle_event_fut = arena.clone().handle_input_event(input_device::InputEvent {
3721                device_event: input_device::InputDeviceEvent::Keyboard(
3722                    keyboard_binding::KeyboardEvent::new(
3723                        fidl_fuchsia_input::Key::A,
3724                        fidl_fuchsia_ui_input3::KeyEventType::Pressed,
3725                    ),
3726                ),
3727                device_descriptor: keyboard_descriptor.clone(),
3728                event_time: zx::MonotonicInstant::from_nanos(11_000_000),
3729                trace_id: None,
3730                handled: input_device::Handled::Yes,
3731            });
3732            executor.set_fake_time(fasync::MonotonicInstant::from_nanos(12_000_000));
3733            assert_matches!(
3734                executor.run_until_stalled(&mut handle_event_fut),
3735                std::task::Poll::Ready(_)
3736            );
3737
3738            let mut handle_event_fut = arena.clone().handle_input_event(input_device::InputEvent {
3740                device_event: input_device::InputDeviceEvent::Keyboard(
3741                    keyboard_binding::KeyboardEvent::new(
3742                        fidl_fuchsia_input::Key::B,
3743                        fidl_fuchsia_ui_input3::KeyEventType::Pressed,
3744                    ),
3745                ),
3746                device_descriptor: keyboard_descriptor,
3747                event_time: zx::MonotonicInstant::from_nanos(13_000_000),
3748                trace_id: None,
3749                handled: input_device::Handled::No,
3750            });
3751            executor.set_fake_time(fasync::MonotonicInstant::from_nanos(14_000_000));
3752            assert_matches!(
3753                executor.run_until_stalled(&mut handle_event_fut),
3754                std::task::Poll::Ready(_)
3755            );
3756
3757            let mut handle_event_fut = arena.clone().handle_input_event(input_device::InputEvent {
3759                device_event: input_device::InputDeviceEvent::Touchpad(
3760                    touch_binding::TouchpadEvent {
3761                        injector_contacts: vec![touch_binding::TouchContact {
3762                            id: 1u32,
3763                            position: Position { x: 2.0, y: 3.0 },
3764                            contact_size: Some(Size { width: 3.0, height: 4.0 }),
3765                            pressure: None,
3766                        }],
3767                        pressed_buttons: hashset! {},
3768                    },
3769                ),
3770                device_descriptor: touchpad_descriptor.clone(),
3771                event_time: zx::MonotonicInstant::from_nanos(18_000_000),
3772                trace_id: None,
3773                handled: input_device::Handled::No,
3774            });
3775            let matched_contender = Box::new(StubMatchedContender::new());
3776            matched_contender.set_next_process_buffered_events_result(
3777                ProcessBufferedEventsResult {
3778                    generated_events: vec![],
3779                    winner: None,
3780                    recognized_gesture: RecognizedGesture::Motion,
3781                },
3782            );
3783            gesture_matching_contender
3784                .set_next_result(ExamineEventResult::MatchedContender(matched_contender));
3785            executor.set_fake_time(fasync::MonotonicInstant::from_nanos(19_000_000));
3786            assert_matches!(
3787                executor.run_until_stalled(&mut handle_event_fut),
3788                std::task::Poll::Ready(_)
3789            );
3790
3791            diagnostics_assertions::assert_data_tree!(@executor executor, inspector, root: {
3801                gestures_event_log: {
3802                    "0": {
3803                        touchpad_event: {
3804                            driver_monotonic_nanos: 12_300i64,
3805                            entry_latency_micros: 9987i64,  pressed_buttons: vec![ 1u64 ],
3807                            contacts: {
3808                                "1": {
3809                                    pos_x_mm: 2.0,
3810                                    pos_y_mm: 3.0,
3811                                },
3812                                "2": {
3813                                    pos_x_mm: 40.0,
3814                                    pos_y_mm: 50.0,
3815                                },
3816                            },
3817                            filtered_palm_contacts: {},
3818                        }
3819                    },
3820                    "1": {
3821                        mismatch_event: {
3822                            contender: "utils::StubContender",
3823                            reason: "some reason",
3824                        }
3825                    },
3826                    "2": {
3827                        mismatch_event: {
3828                            contender: "utils::StubContender",
3829                            criterion: "num_goats_teleported",
3830                            min_allowed: 10u64,
3831                            max_allowed: 30u64,
3832                            actual: 42u64,
3833                        }
3834                    },
3835                    "3": {
3836                        mismatch_event: {
3837                            contender: "utils::StubContender",
3838                            criterion: "teleportation_distance_kilometers",
3839                            min_allowed: 10.125,
3840                            max_allowed: 30.5,
3841                            actual: 42.0,
3842                        }
3843                    },
3844                    "4": {
3845                        mismatch_event: {
3846                            contender: "utils::StubContender",
3847                            criterion: "budget_surplus_trillions",
3848                            min_allowed: -10i64,
3849                            max_allowed: 1i64,
3850                            actual: -42i64,
3851                        }
3852                    },
3853                    "5": {
3854                        key_event: {
3855                            driver_monotonic_nanos: 11_000_000i64,
3856                            entry_latency_micros: 1_000i64,  }
3858                    },
3859                    "6": {
3860                        key_event: {
3861                            driver_monotonic_nanos: 13_000_000i64,
3862                            entry_latency_micros: 1_000i64,  }
3864                    },
3865                    "7": {
3866                        touchpad_event: {
3867                            driver_monotonic_nanos: 18_000_000i64,
3868                            entry_latency_micros: 1_000i64,  pressed_buttons: Vec::<u64>::new(),
3870                            contacts: {
3871                                "1": {
3872                                    pos_x_mm: 2.0,
3873                                    pos_y_mm: 3.0,
3874                                    width_mm: 3.0,
3875                                    height_mm: 4.0,
3876                                },
3877                            },
3878                            filtered_palm_contacts: {},
3879                        }
3880                    },
3881                    "8": {
3882                        gesture_start: {
3883                          gesture_name: "motion",
3884                          latency_event_count: 1u64,
3885                          latency_micros: 17_987i64,  }
3887                    },
3888                    "9": {
3889                        gesture_end: {
3890                          gesture_name: "motion",
3891                          contender: "utils::StubMatchedContender",
3892                          event_count: 0u64,
3893                          duration_micros: 0i64,
3894                          reason: "discrete-recognizer",
3895                        }
3896                    }
3897                }
3898            });
3899        }
3900
3901        #[fuchsia::test(allow_stalls = false)]
3902        async fn negative_matching_latency_is_logged_correctly() {
3903            let inspector = fuchsia_inspect::Inspector::default();
3904            let gesture_matching_contender = Box::new(StubContender::new());
3905            let contender_factory = Box::new(ContenderFactoryOnceOrPanic::new(vec![
3906                gesture_matching_contender.clone(),
3907            ]));
3908            let arena = Rc::new(GestureArena::new_for_test(contender_factory, &inspector, 100));
3909
3910            gesture_matching_contender
3911                .set_next_result(ExamineEventResult::Contender(gesture_matching_contender.clone()));
3912            arena
3913                .clone()
3914                .handle_input_event(input_device::InputEvent {
3915                    event_time: zx::MonotonicInstant::from_nanos(15_000),
3916                    ..make_unhandled_touchpad_event()
3917                })
3918                .await;
3919
3920            let matched_contender = Box::new(StubMatchedContender::new());
3921            matched_contender.set_next_process_buffered_events_result(
3922                ProcessBufferedEventsResult {
3923                    generated_events: vec![],
3924                    winner: None,
3925                    recognized_gesture: RecognizedGesture::Motion,
3926                },
3927            );
3928            gesture_matching_contender
3929                .set_next_result(ExamineEventResult::MatchedContender(matched_contender));
3930            arena
3931                .clone()
3932                .handle_input_event(input_device::InputEvent {
3933                    event_time: zx::MonotonicInstant::from_nanos(6_000),
3934                    ..make_unhandled_touchpad_event()
3935                })
3936                .await;
3937
3938            diagnostics_assertions::assert_data_tree!(inspector, root: {
3939                gestures_event_log: {
3940                    "0": contains {},
3941                    "1": contains {},
3942                    "2": {
3943                        gesture_start: {
3944                          gesture_name: diagnostics_assertions::AnyProperty,
3945                          latency_event_count: 1u64,
3946                          latency_micros: -9i64,
3947                        }
3948                    },
3949                    "3": {
3950                        gesture_end: contains {}
3951                    },
3952                }
3953            })
3954        }
3955
3956        struct ContenderFactoryOnceThenEmpty {
3957            contenders: std::cell::Cell<Vec<Box<dyn Contender>>>,
3958        }
3959
3960        impl ContenderFactory for ContenderFactoryOnceThenEmpty {
3961            fn make_contenders(&self) -> Vec<Box<dyn Contender>> {
3962                self.contenders.take()
3963            }
3964        }
3965
3966        #[test_case(EndGestureEvent::NoEvent; "end_gesture_no_event")]
3967        #[test_case(EndGestureEvent::UnconsumedEvent(TouchpadEvent {
3968            timestamp: zx::MonotonicInstant::ZERO,
3969            pressed_buttons: vec![],
3970            contacts: vec![],
3971            filtered_palm_contacts: vec![],
3972        }); "end_gesture_unconsumed_event")]
3973        #[test_case(EndGestureEvent::GeneratedEvent(MouseEvent {
3974            timestamp: zx::MonotonicInstant::ZERO,
3975            mouse_data: mouse_binding::MouseEvent {
3976                location: mouse_binding::MouseLocation::Relative(
3977                    mouse_binding::RelativeLocation {
3978                        millimeters: Position::zero(),
3979                    },
3980                ),
3981                wheel_delta_v: None,
3982                wheel_delta_h: None,
3983                phase: mouse_binding::MousePhase::Move,
3984                affected_buttons: hashset! {},
3985                pressed_buttons: hashset! {},
3986                is_precision_scroll: None,
3987            },
3988        }); "end_gesture_generated_event")]
3989        #[fuchsia::test(allow_stalls = false)]
3990        async fn multi_event_gesture_is_logged_correctly(end_gesture_event: EndGestureEvent) {
3991            let inspector = fuchsia_inspect::Inspector::default();
3993            let matching_contender = Box::new(StubContender::new());
3994            let arena = Rc::new(GestureArena::new_for_test(
3995                Box::new(ContenderFactoryOnceThenEmpty {
4000                    contenders: std::cell::Cell::new(vec![matching_contender.clone()]),
4001                }),
4002                &inspector,
4003                100,
4004            ));
4005            matching_contender
4006                .set_next_result(ExamineEventResult::Contender(matching_contender.clone()));
4007            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
4008
4009            let matched_contender = Box::new(StubMatchedContender::new());
4012            let winner = Box::new(StubWinner::new());
4013            matching_contender
4014                .set_next_result(ExamineEventResult::MatchedContender(matched_contender.clone()));
4015            matched_contender.set_next_process_buffered_events_result(
4016                ProcessBufferedEventsResult {
4017                    generated_events: vec![],
4018                    winner: Some(winner.clone()),
4019                    recognized_gesture: RecognizedGesture::Motion,
4020                },
4021            );
4022            winner.set_next_result(ProcessNewEventResult::ContinueGesture(None, winner.clone()));
4023            arena
4024                .clone()
4025                .handle_input_event(input_device::InputEvent {
4026                    device_event: input_device::InputDeviceEvent::Touchpad(
4027                        touch_binding::TouchpadEvent {
4028                            injector_contacts: vec![],
4029                            pressed_buttons: hashset! {},
4030                        },
4031                    ),
4032                    device_descriptor: make_touchpad_descriptor(),
4033                    event_time: zx::MonotonicInstant::from_nanos(123_000),
4034                    trace_id: None,
4035                    handled: input_device::Handled::No,
4036                })
4037                .await;
4038
4039            winner.set_next_result(ProcessNewEventResult::ContinueGesture(None, winner.clone()));
4041            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await;
4042
4043            winner.set_next_result(ProcessNewEventResult::EndGesture(
4045                end_gesture_event,
4046                Reason::DetailedUint(DetailedReasonUint {
4047                    criterion: "num_goats_teleported",
4048                    min: Some(10),
4049                    max: Some(30),
4050                    actual: 42,
4051                }),
4052            ));
4053            arena
4054                .clone()
4055                .handle_input_event(input_device::InputEvent {
4056                    device_event: input_device::InputDeviceEvent::Touchpad(
4057                        touch_binding::TouchpadEvent {
4058                            injector_contacts: vec![],
4059                            pressed_buttons: hashset! {},
4060                        },
4061                    ),
4062                    device_descriptor: make_touchpad_descriptor(),
4063                    event_time: zx::MonotonicInstant::from_nanos(456_000),
4064                    trace_id: None,
4065                    handled: input_device::Handled::No,
4066                })
4067                .await;
4068
4069            diagnostics_assertions::assert_data_tree!(inspector, root: {
4070                gestures_event_log: {
4071                    "0": { touchpad_event: contains {} },
4072                    "1": { touchpad_event: contains {} },
4073                    "2": { gesture_start: contains {} },
4074                    "3": { touchpad_event: contains {} },
4075                    "4": { touchpad_event: contains {} },
4076                    "5": {
4077                        gesture_end: contains {
4078                            gesture_name: "motion",
4079                            contender: "utils::StubWinner",
4080                            criterion: "num_goats_teleported",
4081                            min_allowed: 10u64,
4082                            max_allowed: 30u64,
4083                            actual: 42u64,
4084                            duration_micros: 333i64, event_count: 2u64,
4086                        },
4087                    },
4088                }
4089            })
4090        }
4091
4092        #[fuchsia::test(allow_stalls = false)]
4093        async fn retains_latest_events_up_to_cap() {
4094            let inspector = fuchsia_inspect::Inspector::default();
4095            let arena = Rc::new(GestureArena::new_for_test(
4096                Box::new(EmptyContenderFactory {}),
4097                &inspector,
4098                2,
4099            ));
4100            arena.clone().handle_input_event(make_unhandled_touchpad_event()).await; arena.clone().handle_input_event(make_unhandled_touchpad_event()).await; arena.clone().handle_input_event(make_unhandled_touchpad_event()).await; arena.clone().handle_input_event(make_unhandled_touchpad_event()).await; arena.clone().handle_input_event(make_unhandled_touchpad_event()).await; diagnostics_assertions::assert_data_tree!(inspector, root: {
4106                gestures_event_log: {
4107                    "3": contains {},
4108                    "4": contains {},
4109                }
4110            })
4111        }
4112
4113        #[fuchsia::test]
4114        fn retains_palm_contacts() {
4115            let mut executor = fasync::TestExecutor::new_with_fake_time();
4116            let inspector = fuchsia_inspect::Inspector::default();
4117            let arena = Rc::new(GestureArena::new_for_test(
4118                Box::new(EmptyContenderFactory {}),
4119                &inspector,
4120                2,
4121            ));
4122            let mut handle_event_fut = arena.clone().handle_input_event(input_device::InputEvent {
4123                device_event: input_device::InputDeviceEvent::Touchpad(
4124                    touch_binding::TouchpadEvent {
4125                        injector_contacts: vec![touch_binding::TouchContact {
4126                            id: 1u32,
4127                            position: Position { x: 2_000.0, y: 1_000.0 },
4128                            contact_size: Some(Size {
4129                                width: (args::MIN_PALM_SIZE_MM + 0.1) * 1_000.0,
4130                                height: 4_000.0,
4131                            }),
4132                            pressure: None,
4133                        }],
4134                        pressed_buttons: hashset! {},
4135                    },
4136                ),
4137                device_descriptor: make_touchpad_descriptor(),
4138                event_time: zx::MonotonicInstant::ZERO,
4139                trace_id: None,
4140                handled: input_device::Handled::No,
4141            });
4142            executor.set_fake_time(fasync::MonotonicInstant::from_nanos(1_000_000));
4143            assert_matches!(
4144                executor.run_until_stalled(&mut handle_event_fut),
4145                std::task::Poll::Ready(_)
4146            );
4147            diagnostics_assertions::assert_data_tree!(@executor executor, inspector, root: {
4148                gestures_event_log: {
4149                    "0": {
4150                        touchpad_event: {
4151                            driver_monotonic_nanos: 0i64,
4152                            entry_latency_micros: 1_000i64,
4153                            pressed_buttons: Vec::<u64>::new(),
4154                            contacts: {},
4155                            filtered_palm_contacts: {
4156                                "1": {
4157                                    pos_x_mm: 2.0,
4158                                    pos_y_mm: 1.0,
4159                                    width_mm: (args::MIN_PALM_SIZE_MM + 0.1) as f64,
4160                                    height_mm: 4.0,
4161                                },
4162                            },
4163                        },
4164                    },
4165                },
4166            });
4167        }
4168    }
4169}
4170
4171