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