input_pipeline/
inspect_handler.rs

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