1use crate::input_device::{Handled, InputDeviceType, InputEvent, InputEventType};
6use crate::input_handler::{Handler, InputHandler};
7use async_trait::async_trait;
8use fuchsia_inspect::health::Reporter;
9use fuchsia_inspect::{
10 self as inspect, ExponentialHistogramParams, HistogramProperty, Inspector, NumericProperty,
11 Property,
12};
13
14use futures::FutureExt;
15use futures::lock::Mutex;
16use inspect::Node;
17use std::cell::RefCell;
18use std::collections::{HashMap, HashSet, VecDeque};
19use std::fmt::Debug;
20use std::rc::Rc;
21use std::sync::Arc;
22
23const MAX_RECENT_EVENT_LOG_SIZE: usize = 125;
24const LATENCY_HISTOGRAM_PROPERTIES: ExponentialHistogramParams<i64> = ExponentialHistogramParams {
25 floor: 0,
26 initial_step: 1,
27 step_multiplier: 10,
28 buckets: 7,
39};
40
41#[derive(Debug)]
42struct EventCounters {
43 _node: inspect::Node,
45 events_count: inspect::UintProperty,
47 handled_events_count: inspect::UintProperty,
49 last_seen_timestamp_ns: inspect::IntProperty,
52 last_generated_timestamp_ns: inspect::IntProperty,
55}
56
57impl EventCounters {
58 fn add_new_into(
59 map: &mut HashMap<InputEventType, EventCounters>,
60 root: &inspect::Node,
61 event_type: InputEventType,
62 ) {
63 let node = root.create_child(format!("{}", event_type));
64 let events_count = node.create_uint("events_count", 0);
65 let handled_events_count = node.create_uint("handled_events_count", 0);
66 let last_seen_timestamp_ns = node.create_int("last_seen_timestamp_ns", 0);
67 let last_generated_timestamp_ns = node.create_int("last_generated_timestamp_ns", 0);
68 let new_counters = EventCounters {
69 _node: node,
70 events_count,
71 handled_events_count,
72 last_seen_timestamp_ns,
73 last_generated_timestamp_ns,
74 };
75 map.insert(event_type, new_counters);
76 }
77
78 pub fn count_event(
79 &self,
80 time: zx::MonotonicInstant,
81 event_time: zx::MonotonicInstant,
82 handled: &Handled,
83 ) {
84 self.events_count.add(1);
85 if *handled == Handled::Yes {
86 self.handled_events_count.add(1);
87 }
88 self.last_seen_timestamp_ns.set(time.into_nanos());
89 self.last_generated_timestamp_ns.set(event_time.into_nanos());
90 }
91}
92
93pub(crate) struct CircularBuffer<T> {
94 _size: usize,
96 _events: VecDeque<T>,
98}
99
100pub(crate) trait BufferNode {
101 fn get_name(&self) -> &'static str;
102 fn record_inspect(&self, node: &Node);
103}
104
105impl<T> CircularBuffer<T>
106where
107 T: BufferNode,
108{
109 pub(crate) fn new(size: usize) -> Self {
110 let events = VecDeque::with_capacity(size);
111 CircularBuffer { _size: size, _events: events }
112 }
113
114 pub(crate) fn push(&mut self, event: T) {
115 if self._events.len() >= self._size {
116 std::mem::drop(self._events.pop_front());
117 }
118 self._events.push_back(event);
119 }
120
121 pub(crate) fn record_all_lazy_inspect(
122 &self,
123 inspector: inspect::Inspector,
124 ) -> inspect::Inspector {
125 self._events.iter().enumerate().for_each(|(i, event)| {
126 inspector.root().record_child(format!("{:03}_{}", i, event.get_name()), move |node| {
129 event.record_inspect(node)
130 });
131 });
132 inspector
133 }
134}
135
136impl BufferNode for InputEvent {
137 fn get_name(&self) -> &'static str {
138 self.get_event_type()
139 }
140
141 fn record_inspect(&self, node: &Node) {
142 InputEvent::record_inspect(self, node);
143 }
144}
145
146pub struct InspectHandler<F> {
151 now: RefCell<F>,
153 node: inspect::Node,
155 events_count: inspect::UintProperty,
157 last_seen_timestamp_ns: inspect::IntProperty,
160 last_generated_timestamp_ns: inspect::IntProperty,
163 events_by_type: HashMap<InputEventType, EventCounters>,
165 recent_events_log: Option<Arc<Mutex<CircularBuffer<InputEvent>>>>,
167 pipeline_latency_ms: inspect::IntExponentialHistogramProperty,
171 health_node: RefCell<fuchsia_inspect::health::Node>,
173}
174
175impl<F: FnMut() -> zx::MonotonicInstant + 'static> Debug for InspectHandler<F> {
176 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
177 f.debug_struct("InspectHandler")
178 .field("node", &self.node)
179 .field("events_count", &self.events_count)
180 .field("last_seen_timestamp_ns", &self.last_seen_timestamp_ns)
181 .field("last_generated_timestamp_ns", &self.last_generated_timestamp_ns)
182 .field("events_by_type", &self.events_by_type)
183 .field("recent_events_log", &self.recent_events_log)
184 .field("pipeline_latency_ms", &self.pipeline_latency_ms)
185 .finish()
186 }
187}
188
189impl<F: FnMut() -> zx::MonotonicInstant + 'static> Handler for InspectHandler<F> {
190 fn set_handler_healthy(self: std::rc::Rc<Self>) {
191 self.health_node.borrow_mut().set_ok();
192 }
193
194 fn set_handler_unhealthy(self: std::rc::Rc<Self>, msg: &str) {
195 self.health_node.borrow_mut().set_unhealthy(msg);
196 }
197
198 fn get_name(&self) -> &'static str {
199 "InspectHandler"
200 }
201
202 fn interest(&self) -> Vec<InputEventType> {
203 vec![
204 InputEventType::Keyboard,
205 InputEventType::LightSensor,
206 InputEventType::ConsumerControls,
207 InputEventType::Mouse,
208 InputEventType::TouchScreen,
209 InputEventType::Touchpad,
210 #[cfg(test)]
211 InputEventType::Fake,
212 ]
213 }
214}
215
216#[async_trait(?Send)]
217impl<F: FnMut() -> zx::MonotonicInstant + 'static> InputHandler for InspectHandler<F> {
218 async fn handle_input_event(self: Rc<Self>, input_event: InputEvent) -> Vec<InputEvent> {
219 fuchsia_trace::duration!("input", "inspect_handler");
220 let tracing_id = input_event.trace_id.unwrap_or_else(|| 0.into());
221 fuchsia_trace::flow_step!("input", "event_in_input_pipeline", tracing_id);
222
223 let event_time = input_event.event_time;
224 let now = (self.now.borrow_mut())();
225 self.events_count.add(1);
226 self.last_seen_timestamp_ns.set(now.into_nanos());
227 self.last_generated_timestamp_ns.set(event_time.into_nanos());
228 let event_type = InputEventType::from(&input_event.device_event);
229 self.events_by_type
230 .get(&event_type)
231 .unwrap_or_else(|| panic!("no event counters for {}", event_type))
232 .count_event(now, event_time, &input_event.handled);
233 if let Some(recent_events_log) = &self.recent_events_log {
234 recent_events_log.lock().await.push(input_event.clone());
235 }
236 self.pipeline_latency_ms.insert((now - event_time).into_millis());
237 vec![input_event]
238 }
239}
240
241pub fn make_inspect_handler(
245 node: inspect::Node,
246 supported_input_devices: &HashSet<&InputDeviceType>,
247 displays_recent_events: bool,
248) -> Rc<InspectHandler<fn() -> zx::MonotonicInstant>> {
249 InspectHandler::new_internal(
250 node,
251 zx::MonotonicInstant::get,
252 supported_input_devices,
253 displays_recent_events,
254 )
255}
256
257impl<F> InspectHandler<F> {
258 fn new_internal(
261 node: inspect::Node,
262 now: F,
263 supported_input_devices: &HashSet<&InputDeviceType>,
264 displays_recent_events: bool,
265 ) -> Rc<Self> {
266 let event_count = node.create_uint("events_count", 0);
267 let last_seen_timestamp_ns = node.create_int("last_seen_timestamp_ns", 0);
268 let last_generated_timestamp_ns = node.create_int("last_generated_timestamp_ns", 0);
269
270 let recent_events_log = match displays_recent_events {
271 true => {
272 let recent_events =
273 Arc::new(Mutex::new(CircularBuffer::new(MAX_RECENT_EVENT_LOG_SIZE)));
274 record_lazy_recent_events(&node, Arc::clone(&recent_events));
275 Some(recent_events)
276 }
277 false => None,
278 };
279
280 let pipeline_latency_ms = node
281 .create_int_exponential_histogram("pipeline_latency_ms", LATENCY_HISTOGRAM_PROPERTIES);
282
283 let mut health_node = fuchsia_inspect::health::Node::new(&node);
284 health_node.set_starting_up();
285
286 let mut events_by_type = HashMap::new();
287 if supported_input_devices.contains(&InputDeviceType::Keyboard) {
288 EventCounters::add_new_into(&mut events_by_type, &node, InputEventType::Keyboard);
289 }
290 if supported_input_devices.contains(&InputDeviceType::ConsumerControls) {
291 EventCounters::add_new_into(
292 &mut events_by_type,
293 &node,
294 InputEventType::ConsumerControls,
295 );
296 }
297 if supported_input_devices.contains(&InputDeviceType::LightSensor) {
298 EventCounters::add_new_into(&mut events_by_type, &node, InputEventType::LightSensor);
299 }
300 if supported_input_devices.contains(&InputDeviceType::Mouse) {
301 EventCounters::add_new_into(&mut events_by_type, &node, InputEventType::Mouse);
302 }
303 if supported_input_devices.contains(&InputDeviceType::Touch) {
304 EventCounters::add_new_into(&mut events_by_type, &node, InputEventType::TouchScreen);
305 EventCounters::add_new_into(&mut events_by_type, &node, InputEventType::Touchpad);
306 }
307 #[cfg(test)]
308 EventCounters::add_new_into(&mut events_by_type, &node, InputEventType::Fake);
309
310 Rc::new(Self {
311 now: RefCell::new(now),
312 node,
313 events_count: event_count,
314 last_seen_timestamp_ns,
315 last_generated_timestamp_ns,
316 events_by_type,
317 recent_events_log,
318 pipeline_latency_ms,
319 health_node: RefCell::new(health_node),
320 })
321 }
322}
323
324fn record_lazy_recent_events(
325 node: &inspect::Node,
326 recent_events: Arc<Mutex<CircularBuffer<InputEvent>>>,
327) {
328 node.record_lazy_child("recent_events_log", move || {
329 let recent_events_clone = Arc::clone(&recent_events);
330 async move {
331 let inspector = Inspector::default();
332 Ok(recent_events_clone.lock().await.record_all_lazy_inspect(inspector))
333 }
334 .boxed()
335 });
336}
337
338#[cfg(test)]
339mod tests {
340 use super::*;
341 use crate::input_device::{self, InputDeviceDescriptor, InputDeviceEvent};
342 use crate::keyboard_binding::KeyboardDeviceDescriptor;
343 use crate::light_sensor::types::Rgbc;
344 use crate::light_sensor_binding::{LightSensorDeviceDescriptor, LightSensorEvent};
345 use crate::mouse_binding::{
346 MouseDeviceDescriptor, MouseLocation, MousePhase, PrecisionScroll, RawWheelDelta,
347 WheelDelta,
348 };
349 use crate::testing_utilities::{
350 consumer_controls_device_descriptor, create_consumer_controls_event,
351 create_fake_handled_input_event, create_fake_input_event, create_keyboard_event,
352 create_mouse_event, create_touch_contact, create_touch_screen_event, create_touchpad_event,
353 };
354 use crate::touch_binding::{TouchScreenDeviceDescriptor, TouchpadDeviceDescriptor};
355 use crate::utils::Position;
356 use diagnostics_assertions::{AnyProperty, assert_data_tree};
357 use fidl::endpoints::create_proxy_and_stream;
358 use fidl_fuchsia_input_report::InputDeviceMarker;
359 use fuchsia_async as fasync;
360 use maplit::{hashmap, hashset};
361 use test_case::test_case;
362
363 fn fixed_now() -> zx::MonotonicInstant {
364 zx::MonotonicInstant::ZERO + zx::MonotonicDuration::from_nanos(42)
365 }
366
367 #[fasync::run_singlethreaded(test)]
368 async fn circular_buffer_no_overflow() {
369 let mut circular_buffer = CircularBuffer::new(MAX_RECENT_EVENT_LOG_SIZE);
370 assert_eq!(circular_buffer._size, MAX_RECENT_EVENT_LOG_SIZE);
371
372 let first_event_time = zx::MonotonicInstant::get();
373 circular_buffer.push(create_fake_input_event(first_event_time));
374 let second_event_time = zx::MonotonicInstant::get();
375 circular_buffer.push(create_fake_input_event(second_event_time));
376
377 for _i in 2..MAX_RECENT_EVENT_LOG_SIZE {
379 let curr_event_time = zx::MonotonicInstant::get();
380 circular_buffer.push(create_fake_input_event(curr_event_time));
381 match circular_buffer._events.back() {
382 Some(event) => assert_eq!(event.event_time, curr_event_time),
383 None => assert!(false),
384 }
385 }
386
387 match circular_buffer._events.front() {
389 Some(event) => assert_eq!(event.event_time, first_event_time),
390 None => assert!(false),
391 }
392
393 let last_event_time = zx::MonotonicInstant::get();
395 circular_buffer.push(create_fake_input_event(last_event_time));
396 match circular_buffer._events.front() {
397 Some(event) => assert_eq!(event.event_time, second_event_time),
398 None => assert!(false),
399 }
400 match circular_buffer._events.back() {
401 Some(event) => assert_eq!(event.event_time, last_event_time),
402 None => assert!(false),
403 }
404 }
405
406 #[fasync::run_singlethreaded(test)]
407 async fn recent_events_log_records_inspect() {
408 let inspector = fuchsia_inspect::Inspector::default();
409
410 let recent_events_log =
411 Arc::new(Mutex::new(CircularBuffer::new(MAX_RECENT_EVENT_LOG_SIZE)));
412 record_lazy_recent_events(inspector.root(), Arc::clone(&recent_events_log));
413
414 let keyboard_descriptor = InputDeviceDescriptor::Keyboard(KeyboardDeviceDescriptor {
415 keys: vec![fidl_fuchsia_input::Key::A, fidl_fuchsia_input::Key::B],
416 ..Default::default()
417 });
418 let mouse_descriptor = InputDeviceDescriptor::Mouse(MouseDeviceDescriptor {
419 device_id: 1u32,
420 absolute_x_range: None,
421 absolute_y_range: None,
422 wheel_v_range: None,
423 wheel_h_range: None,
424 buttons: None,
425 counts_per_mm: 12u32,
426 });
427 let touch_screen_descriptor =
428 InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
429 device_id: 1,
430 contacts: vec![],
431 });
432 let touchpad_descriptor = InputDeviceDescriptor::Touchpad(TouchpadDeviceDescriptor {
433 device_id: 1,
434 contacts: vec![],
435 });
436
437 let pressed_buttons = HashSet::from([1u8, 21u8, 15u8]);
438 let mut pressed_buttons_vec: Vec<u64> = vec![];
439 pressed_buttons.iter().for_each(|button| {
440 pressed_buttons_vec.push(*button as u64);
441 });
442
443 let (light_sensor_proxy, _) = create_proxy_and_stream::<InputDeviceMarker>();
444
445 let recent_events = vec![
446 create_keyboard_event(
447 fidl_fuchsia_input::Key::A,
448 fidl_fuchsia_ui_input3::KeyEventType::Pressed,
449 None,
450 &keyboard_descriptor,
451 None,
452 ),
453 create_consumer_controls_event(
454 vec![
455 fidl_fuchsia_input_report::ConsumerControlButton::VolumeUp,
456 fidl_fuchsia_input_report::ConsumerControlButton::VolumeUp,
457 fidl_fuchsia_input_report::ConsumerControlButton::Pause,
458 fidl_fuchsia_input_report::ConsumerControlButton::VolumeDown,
459 fidl_fuchsia_input_report::ConsumerControlButton::MicMute,
460 fidl_fuchsia_input_report::ConsumerControlButton::CameraDisable,
461 fidl_fuchsia_input_report::ConsumerControlButton::FactoryReset,
462 fidl_fuchsia_input_report::ConsumerControlButton::Reboot,
463 ],
464 zx::MonotonicInstant::get(),
465 &consumer_controls_device_descriptor(),
466 ),
467 create_mouse_event(
468 MouseLocation::Absolute(Position { x: 7.0f32, y: 15.0f32 }),
469 Some(WheelDelta {
470 raw_data: RawWheelDelta::Ticks(5i64),
471 physical_pixel: Some(8.0f32),
472 }),
473 Some(WheelDelta {
474 raw_data: RawWheelDelta::Millimeters(10.0f32),
475 physical_pixel: Some(8.0f32),
476 }),
477 Some(PrecisionScroll::Yes),
478 MousePhase::Move,
479 HashSet::from([1u8]),
480 pressed_buttons.clone(),
481 zx::MonotonicInstant::get(),
482 &mouse_descriptor,
483 ),
484 create_touch_screen_event(
485 hashmap! {
486 fidl_fuchsia_ui_input::PointerEventPhase::Add
487 => vec![create_touch_contact(1u32, Position { x: 10.0, y: 30.0 })],
488 fidl_fuchsia_ui_input::PointerEventPhase::Move
489 => vec![create_touch_contact(1u32, Position { x: 11.0, y: 31.0 })],
490 },
491 zx::MonotonicInstant::get(),
492 &touch_screen_descriptor,
493 ),
494 create_touchpad_event(
495 vec![
496 create_touch_contact(1u32, Position { x: 0.0, y: 0.0 }),
497 create_touch_contact(2u32, Position { x: 10.0, y: 10.0 }),
498 ],
499 HashSet::new(),
500 zx::MonotonicInstant::get(),
501 &touchpad_descriptor,
502 ),
503 InputEvent {
504 device_event: InputDeviceEvent::LightSensor(LightSensorEvent {
505 device_proxy: light_sensor_proxy,
506 rgbc: Rgbc { red: 1, green: 2, blue: 3, clear: 14747 },
507 }),
508 device_descriptor: InputDeviceDescriptor::LightSensor(
509 LightSensorDeviceDescriptor {
510 vendor_id: 1,
511 product_id: 2,
512 device_id: 3,
513 sensor_layout: Rgbc { red: 1, green: 2, blue: 3, clear: 4 },
514 },
515 ),
516 event_time: zx::MonotonicInstant::get(),
517 handled: input_device::Handled::No,
518 trace_id: None,
519 },
520 create_keyboard_event(
521 fidl_fuchsia_input::Key::B,
522 fidl_fuchsia_ui_input3::KeyEventType::Pressed,
523 None,
524 &keyboard_descriptor,
525 None,
526 ),
527 ];
528
529 for event in recent_events.into_iter() {
530 recent_events_log.lock().await.push(event);
531 }
532
533 assert_data_tree!(inspector, root: {
534 recent_events_log: {
535 "000_keyboard_event": {
536 event_time: AnyProperty,
537 },
538 "001_consumer_controls_event": {
539 event_time: AnyProperty,
540 pressed_buttons: vec!["volume_up", "volume_up", "pause", "volume_down", "mic_mute", "camera_disable", "factory_reset", "reboot"],
541 },
542 "002_mouse_event": {
543 event_time: AnyProperty,
544 location_absolute: { x: 7.0f64, y: 15.0f64},
545 wheel_delta_v: {
546 ticks: 5i64,
547 physical_pixel: 8.0f64,
548 },
549 wheel_delta_h: {
550 millimeters: 10.0f64,
551 physical_pixel: 8.0f64,
552 },
553 is_precision_scroll: "yes",
554 phase: "move",
555 affected_buttons: vec![1u64],
556 pressed_buttons: pressed_buttons_vec.clone(),
557 },
558 "003_touch_screen_event": {
559 event_time: AnyProperty,
560 injector_contacts: {
561 add: {
562 "1": {
563 position_x_mm: 10.0f64,
564 position_y_mm: 30.0f64,
565 },
566 },
567 change: {
568 "1": {
569 position_x_mm: 11.0f64,
570 position_y_mm: 31.0f64,
571 },
572 },
573 remove: {},
574 },
575 pressed_buttons: Vec::<String>::new(),
576 },
577 "004_touchpad_event": {
578 event_time: AnyProperty,
579 pressed_buttons: Vec::<u64>::new(),
580 injector_contacts: {
581 "1": {
582 position_x_mm: 0.0f64,
583 position_y_mm: 0.0f64,
584 },
585 "2": {
586 position_x_mm: 10.0f64,
587 position_y_mm: 10.0f64,
588 },
589 },
590 },
591 "005_light_sensor_event": {
592 event_time: AnyProperty,
593 red: 1u64,
594 green: 2u64,
595 blue: 3u64,
596 clear: 14747u64,
597 },
598 "006_keyboard_event": {
599 event_time: AnyProperty,
600 },
601 }
602 });
603 }
604
605 #[fasync::run_singlethreaded(test)]
606 async fn verify_inspect_no_recent_events_log() {
607 let inspector = inspect::Inspector::default();
608 let root = inspector.root();
609 let test_node = root.create_child("test_node");
610 let supported_input_devices: HashSet<&InputDeviceType> = HashSet::from([
611 &input_device::InputDeviceType::Keyboard,
612 &input_device::InputDeviceType::ConsumerControls,
613 &input_device::InputDeviceType::LightSensor,
614 &input_device::InputDeviceType::Mouse,
615 &input_device::InputDeviceType::Touch,
616 ]);
617
618 let handler = super::InspectHandler::new_internal(
619 test_node,
620 fixed_now,
621 &supported_input_devices,
622 false,
623 );
624 assert_data_tree!(inspector, root: {
625 test_node: contains {
626 events_count: 0u64,
627 last_seen_timestamp_ns: 0i64,
628 last_generated_timestamp_ns: 0i64,
629 consumer_controls: {
630 events_count: 0u64,
631 handled_events_count: 0u64,
632 last_generated_timestamp_ns: 0i64,
633 last_seen_timestamp_ns: 0i64,
634 },
635 fake: {
636 events_count: 0u64,
637 handled_events_count: 0u64,
638 last_generated_timestamp_ns: 0i64,
639 last_seen_timestamp_ns: 0i64,
640 },
641 keyboard: {
642 events_count: 0u64,
643 handled_events_count: 0u64,
644 last_generated_timestamp_ns: 0i64,
645 last_seen_timestamp_ns: 0i64,
646 },
647 light_sensor: {
648 events_count: 0u64,
649 handled_events_count: 0u64,
650 last_generated_timestamp_ns: 0i64,
651 last_seen_timestamp_ns: 0i64,
652 },
653 mouse: {
654 events_count: 0u64,
655 handled_events_count: 0u64,
656 last_generated_timestamp_ns: 0i64,
657 last_seen_timestamp_ns: 0i64,
658 },
659 touch_screen: {
660 events_count: 0u64,
661 handled_events_count: 0u64,
662 last_generated_timestamp_ns: 0i64,
663 last_seen_timestamp_ns: 0i64,
664 },
665 touchpad: {
666 events_count: 0u64,
667 handled_events_count: 0u64,
668 last_generated_timestamp_ns: 0i64,
669 last_seen_timestamp_ns: 0i64,
670 },
671 }
672 });
673
674 handler
675 .clone()
676 .handle_input_event(create_fake_input_event(zx::MonotonicInstant::from_nanos(43i64)))
677 .await;
678 assert_data_tree!(inspector, root: {
679 test_node: contains {
680 events_count: 1u64,
681 last_seen_timestamp_ns: 42i64,
682 last_generated_timestamp_ns: 43i64,
683 consumer_controls: {
684 events_count: 0u64,
685 handled_events_count: 0u64,
686 last_generated_timestamp_ns: 0i64,
687 last_seen_timestamp_ns: 0i64,
688 },
689 fake: {
690 events_count: 1u64,
691 handled_events_count: 0u64,
692 last_generated_timestamp_ns: 43i64,
693 last_seen_timestamp_ns: 42i64,
694 },
695 keyboard: {
696 events_count: 0u64,
697 handled_events_count: 0u64,
698 last_generated_timestamp_ns: 0i64,
699 last_seen_timestamp_ns: 0i64,
700 },
701 light_sensor: {
702 events_count: 0u64,
703 handled_events_count: 0u64,
704 last_generated_timestamp_ns: 0i64,
705 last_seen_timestamp_ns: 0i64,
706 },
707 mouse: {
708 events_count: 0u64,
709 handled_events_count: 0u64,
710 last_generated_timestamp_ns: 0i64,
711 last_seen_timestamp_ns: 0i64,
712 },
713 touch_screen: {
714 events_count: 0u64,
715 handled_events_count: 0u64,
716 last_generated_timestamp_ns: 0i64,
717 last_seen_timestamp_ns: 0i64,
718 },
719 touchpad: {
720 events_count: 0u64,
721 handled_events_count: 0u64,
722 last_generated_timestamp_ns: 0i64,
723 last_seen_timestamp_ns: 0i64,
724 },
725 }
726 });
727
728 handler
729 .clone()
730 .handle_input_event(create_fake_input_event(zx::MonotonicInstant::from_nanos(44i64)))
731 .await;
732 assert_data_tree!(inspector, root: {
733 test_node: contains {
734 events_count: 2u64,
735 last_seen_timestamp_ns: 42i64,
736 last_generated_timestamp_ns: 44i64,
737 consumer_controls: {
738 events_count: 0u64,
739 handled_events_count: 0u64,
740 last_generated_timestamp_ns: 0i64,
741 last_seen_timestamp_ns: 0i64,
742 },
743 fake: {
744 events_count: 2u64,
745 handled_events_count: 0u64,
746 last_generated_timestamp_ns: 44i64,
747 last_seen_timestamp_ns: 42i64,
748 },
749 keyboard: {
750 events_count: 0u64,
751 handled_events_count: 0u64,
752 last_generated_timestamp_ns: 0i64,
753 last_seen_timestamp_ns: 0i64,
754 },
755 light_sensor: {
756 events_count: 0u64,
757 handled_events_count: 0u64,
758 last_generated_timestamp_ns: 0i64,
759 last_seen_timestamp_ns: 0i64,
760 },
761 mouse: {
762 events_count: 0u64,
763 handled_events_count: 0u64,
764 last_generated_timestamp_ns: 0i64,
765 last_seen_timestamp_ns: 0i64,
766 },
767 touch_screen: {
768 events_count: 0u64,
769 handled_events_count: 0u64,
770 last_generated_timestamp_ns: 0i64,
771 last_seen_timestamp_ns: 0i64,
772 },
773 touchpad: {
774 events_count: 0u64,
775 handled_events_count: 0u64,
776 last_generated_timestamp_ns: 0i64,
777 last_seen_timestamp_ns: 0i64,
778 },
779 }
780 });
781
782 handler
783 .clone()
784 .handle_input_event(create_fake_handled_input_event(zx::MonotonicInstant::from_nanos(
785 44,
786 )))
787 .await;
788 assert_data_tree!(inspector, root: {
789 test_node: contains {
790 events_count: 3u64,
791 last_seen_timestamp_ns: 42i64,
792 last_generated_timestamp_ns: 44i64,
793 consumer_controls: {
794 events_count: 0u64,
795 handled_events_count: 0u64,
796 last_generated_timestamp_ns: 0i64,
797 last_seen_timestamp_ns: 0i64,
798 },
799 fake: {
800 events_count: 3u64,
801 handled_events_count: 1u64,
802 last_generated_timestamp_ns: 44i64,
803 last_seen_timestamp_ns: 42i64,
804 },
805 keyboard: {
806 events_count: 0u64,
807 handled_events_count: 0u64,
808 last_generated_timestamp_ns: 0i64,
809 last_seen_timestamp_ns: 0i64,
810 },
811 light_sensor: {
812 events_count: 0u64,
813 handled_events_count: 0u64,
814 last_generated_timestamp_ns: 0i64,
815 last_seen_timestamp_ns: 0i64,
816 },
817 mouse: {
818 events_count: 0u64,
819 handled_events_count: 0u64,
820 last_generated_timestamp_ns: 0i64,
821 last_seen_timestamp_ns: 0i64,
822 },
823 touch_screen: {
824 events_count: 0u64,
825 handled_events_count: 0u64,
826 last_generated_timestamp_ns: 0i64,
827 last_seen_timestamp_ns: 0i64,
828 },
829 touchpad: {
830 events_count: 0u64,
831 handled_events_count: 0u64,
832 last_generated_timestamp_ns: 0i64,
833 last_seen_timestamp_ns: 0i64,
834 },
835 }
836 });
837 }
838
839 #[fasync::run_singlethreaded(test)]
840 async fn verify_inspect_with_recent_events_log() {
841 let inspector = inspect::Inspector::default();
842 let root = inspector.root();
843 let test_node = root.create_child("test_node");
844 let supported_input_devices: HashSet<&InputDeviceType> = HashSet::from([
845 &input_device::InputDeviceType::Keyboard,
846 &input_device::InputDeviceType::ConsumerControls,
847 &input_device::InputDeviceType::LightSensor,
848 &input_device::InputDeviceType::Mouse,
849 &input_device::InputDeviceType::Touch,
850 ]);
851
852 let handler = super::InspectHandler::new_internal(
853 test_node,
854 fixed_now,
855 &supported_input_devices,
856 true,
857 );
858 assert_data_tree!(inspector, root: {
859 test_node: contains {
860 events_count: 0u64,
861 last_seen_timestamp_ns: 0i64,
862 last_generated_timestamp_ns: 0i64,
863 recent_events_log: {},
864 consumer_controls: {
865 events_count: 0u64,
866 handled_events_count: 0u64,
867 last_generated_timestamp_ns: 0i64,
868 last_seen_timestamp_ns: 0i64,
869 },
870 fake: {
871 events_count: 0u64,
872 handled_events_count: 0u64,
873 last_generated_timestamp_ns: 0i64,
874 last_seen_timestamp_ns: 0i64,
875 },
876 keyboard: {
877 events_count: 0u64,
878 handled_events_count: 0u64,
879 last_generated_timestamp_ns: 0i64,
880 last_seen_timestamp_ns: 0i64,
881 },
882 light_sensor: {
883 events_count: 0u64,
884 handled_events_count: 0u64,
885 last_generated_timestamp_ns: 0i64,
886 last_seen_timestamp_ns: 0i64,
887 },
888 mouse: {
889 events_count: 0u64,
890 handled_events_count: 0u64,
891 last_generated_timestamp_ns: 0i64,
892 last_seen_timestamp_ns: 0i64,
893 },
894 touch_screen: {
895 events_count: 0u64,
896 handled_events_count: 0u64,
897 last_generated_timestamp_ns: 0i64,
898 last_seen_timestamp_ns: 0i64,
899 },
900 touchpad: {
901 events_count: 0u64,
902 handled_events_count: 0u64,
903 last_generated_timestamp_ns: 0i64,
904 last_seen_timestamp_ns: 0i64,
905 },
906 }
907 });
908
909 handler
910 .clone()
911 .handle_input_event(create_fake_input_event(zx::MonotonicInstant::from_nanos(43i64)))
912 .await;
913 assert_data_tree!(inspector, root: {
914 test_node: contains {
915 events_count: 1u64,
916 last_seen_timestamp_ns: 42i64,
917 last_generated_timestamp_ns: 43i64,
918 recent_events_log: {
919 "000_fake_event": {
920 event_time: 43i64,
921 },
922 },
923 consumer_controls: {
924 events_count: 0u64,
925 handled_events_count: 0u64,
926 last_generated_timestamp_ns: 0i64,
927 last_seen_timestamp_ns: 0i64,
928 },
929 fake: {
930 events_count: 1u64,
931 handled_events_count: 0u64,
932 last_generated_timestamp_ns: 43i64,
933 last_seen_timestamp_ns: 42i64,
934 },
935 keyboard: {
936 events_count: 0u64,
937 handled_events_count: 0u64,
938 last_generated_timestamp_ns: 0i64,
939 last_seen_timestamp_ns: 0i64,
940 },
941 light_sensor: {
942 events_count: 0u64,
943 handled_events_count: 0u64,
944 last_generated_timestamp_ns: 0i64,
945 last_seen_timestamp_ns: 0i64,
946 },
947 mouse: {
948 events_count: 0u64,
949 handled_events_count: 0u64,
950 last_generated_timestamp_ns: 0i64,
951 last_seen_timestamp_ns: 0i64,
952 },
953 touch_screen: {
954 events_count: 0u64,
955 handled_events_count: 0u64,
956 last_generated_timestamp_ns: 0i64,
957 last_seen_timestamp_ns: 0i64,
958 },
959 touchpad: {
960 events_count: 0u64,
961 handled_events_count: 0u64,
962 last_generated_timestamp_ns: 0i64,
963 last_seen_timestamp_ns: 0i64,
964 },
965 }
966 });
967
968 handler
969 .clone()
970 .handle_input_event(create_fake_input_event(zx::MonotonicInstant::from_nanos(44i64)))
971 .await;
972 assert_data_tree!(inspector, root: {
973 test_node: contains {
974 events_count: 2u64,
975 last_seen_timestamp_ns: 42i64,
976 last_generated_timestamp_ns: 44i64,
977 recent_events_log: {
978 "000_fake_event": {
979 event_time: 43i64,
980 },
981 "001_fake_event": {
982 event_time: 44i64,
983 },
984 },
985 consumer_controls: {
986 events_count: 0u64,
987 handled_events_count: 0u64,
988 last_generated_timestamp_ns: 0i64,
989 last_seen_timestamp_ns: 0i64,
990 },
991 fake: {
992 events_count: 2u64,
993 handled_events_count: 0u64,
994 last_generated_timestamp_ns: 44i64,
995 last_seen_timestamp_ns: 42i64,
996 },
997 keyboard: {
998 events_count: 0u64,
999 handled_events_count: 0u64,
1000 last_generated_timestamp_ns: 0i64,
1001 last_seen_timestamp_ns: 0i64,
1002 },
1003 light_sensor: {
1004 events_count: 0u64,
1005 handled_events_count: 0u64,
1006 last_generated_timestamp_ns: 0i64,
1007 last_seen_timestamp_ns: 0i64,
1008 },
1009 mouse: {
1010 events_count: 0u64,
1011 handled_events_count: 0u64,
1012 last_generated_timestamp_ns: 0i64,
1013 last_seen_timestamp_ns: 0i64,
1014 },
1015 touch_screen: {
1016 events_count: 0u64,
1017 handled_events_count: 0u64,
1018 last_generated_timestamp_ns: 0i64,
1019 last_seen_timestamp_ns: 0i64,
1020 },
1021 touchpad: {
1022 events_count: 0u64,
1023 handled_events_count: 0u64,
1024 last_generated_timestamp_ns: 0i64,
1025 last_seen_timestamp_ns: 0i64,
1026 },
1027 }
1028 });
1029
1030 handler
1031 .clone()
1032 .handle_input_event(create_fake_handled_input_event(zx::MonotonicInstant::from_nanos(
1033 44,
1034 )))
1035 .await;
1036 assert_data_tree!(inspector, root: {
1037 test_node: contains {
1038 events_count: 3u64,
1039 last_seen_timestamp_ns: 42i64,
1040 last_generated_timestamp_ns: 44i64,
1041 recent_events_log: {
1042 "000_fake_event": {
1043 event_time: 43i64,
1044 },
1045 "001_fake_event": {
1046 event_time: 44i64,
1047 },
1048 "002_fake_event": {
1049 event_time: 44i64,
1050 },
1051 },
1052 consumer_controls: {
1053 events_count: 0u64,
1054 handled_events_count: 0u64,
1055 last_generated_timestamp_ns: 0i64,
1056 last_seen_timestamp_ns: 0i64,
1057 },
1058 fake: {
1059 events_count: 3u64,
1060 handled_events_count: 1u64,
1061 last_generated_timestamp_ns: 44i64,
1062 last_seen_timestamp_ns: 42i64,
1063 },
1064 keyboard: {
1065 events_count: 0u64,
1066 handled_events_count: 0u64,
1067 last_generated_timestamp_ns: 0i64,
1068 last_seen_timestamp_ns: 0i64,
1069 },
1070 light_sensor: {
1071 events_count: 0u64,
1072 handled_events_count: 0u64,
1073 last_generated_timestamp_ns: 0i64,
1074 last_seen_timestamp_ns: 0i64,
1075 },
1076 mouse: {
1077 events_count: 0u64,
1078 handled_events_count: 0u64,
1079 last_generated_timestamp_ns: 0i64,
1080 last_seen_timestamp_ns: 0i64,
1081 },
1082 touch_screen: {
1083 events_count: 0u64,
1084 handled_events_count: 0u64,
1085 last_generated_timestamp_ns: 0i64,
1086 last_seen_timestamp_ns: 0i64,
1087 },
1088 touchpad: {
1089 events_count: 0u64,
1090 handled_events_count: 0u64,
1091 last_generated_timestamp_ns: 0i64,
1092 last_seen_timestamp_ns: 0i64,
1093 },
1094 }
1095 });
1096 }
1097
1098 #[test_case([i64::MIN]; "min value")]
1099 #[test_case([-1]; "negative value")]
1100 #[test_case([0]; "zero")]
1101 #[test_case([1]; "positive value")]
1102 #[test_case([i64::MAX]; "max value")]
1103 #[test_case([1_000_000, 10_000_000, 100_000_000, 1000_000_000]; "multiple values")]
1104 #[fuchsia::test(allow_stalls = false)]
1105 async fn updates_latency_histogram(
1106 latencies_nsec: impl IntoIterator<Item = i64> + Clone + 'static,
1107 ) {
1108 let inspector = inspect::Inspector::default();
1109 let root = inspector.root();
1110 let test_node = root.create_child("test_node");
1111
1112 let mut seen_timestamps =
1113 latencies_nsec.clone().into_iter().map(zx::MonotonicInstant::from_nanos);
1114 let now = move || {
1115 seen_timestamps.next().expect("internal error: test has more events than latencies")
1116 };
1117 let handler = super::InspectHandler::new_internal(
1118 test_node,
1119 now,
1120 &hashset! {},
1121 false,
1122 );
1123 for _latency in latencies_nsec.clone() {
1124 handler
1125 .clone()
1126 .handle_input_event(create_fake_input_event(zx::MonotonicInstant::ZERO))
1127 .await;
1128 }
1129
1130 let mut histogram_assertion = diagnostics_assertions::HistogramAssertion::exponential(
1131 super::LATENCY_HISTOGRAM_PROPERTIES,
1132 );
1133 histogram_assertion
1134 .insert_values(latencies_nsec.into_iter().map(|nsec| nsec / 1000 / 1000));
1135 assert_data_tree!(inspector, root: {
1136 test_node: contains {
1137 pipeline_latency_ms: histogram_assertion
1138 }
1139 })
1140 }
1141}