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