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