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