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