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