mock_metrics/
lib.rs

1// Copyright 2022 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 fidl_fuchsia_metrics::{self as fidl, MetricEvent};
6use fuchsia_async as fasync;
7use fuchsia_sync::Mutex;
8use futures::TryStreamExt as _;
9use std::sync::Arc;
10use std::time::Duration;
11
12pub struct MockMetricEventLogger {
13    cobalt_events: Mutex<Vec<MetricEvent>>,
14}
15
16impl MockMetricEventLogger {
17    fn new() -> Self {
18        Self { cobalt_events: Mutex::new(vec![]) }
19    }
20
21    pub fn clone_metric_events(&self) -> Vec<MetricEvent> {
22        self.cobalt_events.lock().clone()
23    }
24
25    async fn run_logger(self: Arc<Self>, mut stream: fidl::MetricEventLoggerRequestStream) {
26        while let Some(event) = stream.try_next().await.unwrap() {
27            match event {
28                fidl::MetricEventLoggerRequest::LogMetricEvents { events, responder } => {
29                    self.cobalt_events.lock().extend(events);
30                    let _ = responder.send(Ok(()));
31                }
32                _ => {
33                    panic!("unhandled MetricEventLogger method {event:?}");
34                }
35            }
36        }
37    }
38}
39
40pub struct MockMetricEventLoggerFactory {
41    loggers: Mutex<Vec<Arc<MockMetricEventLogger>>>,
42    project_id: u32,
43}
44
45impl MockMetricEventLoggerFactory {
46    #[allow(clippy::new_without_default)]
47    pub fn new() -> Self {
48        Self::with_id(cobalt_sw_delivery_registry::PROJECT_ID)
49    }
50
51    pub fn with_id(id: u32) -> Self {
52        Self { loggers: Mutex::new(vec![]), project_id: id }
53    }
54
55    pub fn clone_loggers(&self) -> Vec<Arc<MockMetricEventLogger>> {
56        self.loggers.lock().clone()
57    }
58
59    pub async fn run_logger_factory(
60        self: Arc<Self>,
61        mut stream: fidl::MetricEventLoggerFactoryRequestStream,
62    ) {
63        while let Some(event) = stream.try_next().await.unwrap() {
64            match event {
65                fidl::MetricEventLoggerFactoryRequest::CreateMetricEventLogger {
66                    project_spec,
67                    logger,
68                    responder,
69                } => {
70                    assert_eq!(project_spec.project_id, Some(self.project_id));
71                    let mock_logger = Arc::new(MockMetricEventLogger::new());
72                    self.loggers.lock().push(mock_logger.clone());
73                    fasync::Task::spawn(mock_logger.run_logger(logger.into_stream())).detach();
74                    let _ = responder.send(Ok(()));
75                }
76                _ => {
77                    panic!("unhandled MetricEventLoggerFactory method: {event:?}");
78                }
79            }
80        }
81    }
82
83    pub async fn wait_for_at_least_n_events_with_metric_id(
84        &self,
85        n: usize,
86        id: u32,
87    ) -> Vec<MetricEvent> {
88        loop {
89            let events: Vec<MetricEvent> = self
90                .loggers
91                .lock()
92                .iter()
93                .flat_map(|logger| logger.cobalt_events.lock().clone())
94                .filter(|MetricEvent { metric_id, .. }| *metric_id == id)
95                .collect();
96            if events.len() >= n {
97                return events;
98            }
99            fasync::Timer::new(Duration::from_millis(10)).await;
100        }
101    }
102}
103
104#[cfg(test)]
105mod tests {
106    use super::*;
107    use ::fidl::endpoints::create_proxy_and_stream;
108    use fidl_fuchsia_metrics::ProjectSpec;
109
110    #[fasync::run_singlethreaded(test)]
111    async fn test_mock_metrics() {
112        let mock = Arc::new(MockMetricEventLoggerFactory::new());
113        let (factory, stream) = create_proxy_and_stream::<fidl::MetricEventLoggerFactoryMarker>();
114
115        let task = fasync::Task::spawn(Arc::clone(&mock).run_logger_factory(stream));
116
117        let (logger, server_end) = ::fidl::endpoints::create_proxy();
118        factory
119            .create_metric_event_logger(
120                &ProjectSpec {
121                    project_id: Some(cobalt_sw_delivery_registry::PROJECT_ID),
122                    ..Default::default()
123                },
124                server_end,
125            )
126            .await
127            .unwrap()
128            .unwrap();
129        drop(factory);
130        task.await;
131
132        let events = &[MetricEvent {
133            metric_id: 42,
134            event_codes: vec![],
135            payload: fidl::MetricEventPayload::Count(0),
136        }];
137        logger.log_metric_events(events).await.unwrap().unwrap();
138        let mock_events = mock.wait_for_at_least_n_events_with_metric_id(1, 42).await;
139        assert_eq!(mock_events, events);
140    }
141}