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