use fidl_fuchsia_metrics::{self as fidl, MetricEvent};
use fuchsia_async as fasync;
use fuchsia_sync::Mutex;
use futures::TryStreamExt as _;
use std::sync::Arc;
use std::time::Duration;
pub struct MockMetricEventLogger {
cobalt_events: Mutex<Vec<MetricEvent>>,
}
impl MockMetricEventLogger {
fn new() -> Self {
Self { cobalt_events: Mutex::new(vec![]) }
}
pub fn clone_metric_events(&self) -> Vec<MetricEvent> {
self.cobalt_events.lock().clone()
}
async fn run_logger(self: Arc<Self>, mut stream: fidl::MetricEventLoggerRequestStream) {
while let Some(event) = stream.try_next().await.unwrap() {
match event {
fidl::MetricEventLoggerRequest::LogMetricEvents { events, responder } => {
self.cobalt_events.lock().extend(events);
let _ = responder.send(Ok(()));
}
_ => {
panic!("unhandled MetricEventLogger method {event:?}");
}
}
}
}
}
pub struct MockMetricEventLoggerFactory {
loggers: Mutex<Vec<Arc<MockMetricEventLogger>>>,
project_id: u32,
}
impl MockMetricEventLoggerFactory {
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
Self::with_id(cobalt_sw_delivery_registry::PROJECT_ID)
}
pub fn with_id(id: u32) -> Self {
Self { loggers: Mutex::new(vec![]), project_id: id }
}
pub fn clone_loggers(&self) -> Vec<Arc<MockMetricEventLogger>> {
self.loggers.lock().clone()
}
pub async fn run_logger_factory(
self: Arc<Self>,
mut stream: fidl::MetricEventLoggerFactoryRequestStream,
) {
while let Some(event) = stream.try_next().await.unwrap() {
match event {
fidl::MetricEventLoggerFactoryRequest::CreateMetricEventLogger {
project_spec,
logger,
responder,
} => {
assert_eq!(project_spec.project_id, Some(self.project_id));
let mock_logger = Arc::new(MockMetricEventLogger::new());
self.loggers.lock().push(mock_logger.clone());
fasync::Task::spawn(mock_logger.run_logger(logger.into_stream())).detach();
let _ = responder.send(Ok(()));
}
_ => {
panic!("unhandled MetricEventLoggerFactory method: {event:?}");
}
}
}
}
pub async fn wait_for_at_least_n_events_with_metric_id(
&self,
n: usize,
id: u32,
) -> Vec<MetricEvent> {
loop {
let events: Vec<MetricEvent> = self
.loggers
.lock()
.iter()
.flat_map(|logger| logger.cobalt_events.lock().clone())
.filter(|MetricEvent { metric_id, .. }| *metric_id == id)
.collect();
if events.len() >= n {
return events;
}
fasync::Timer::new(Duration::from_millis(10)).await;
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use ::fidl::endpoints::create_proxy_and_stream;
use fidl_fuchsia_metrics::ProjectSpec;
#[fasync::run_singlethreaded(test)]
async fn test_mock_metrics() {
let mock = Arc::new(MockMetricEventLoggerFactory::new());
let (factory, stream) = create_proxy_and_stream::<fidl::MetricEventLoggerFactoryMarker>();
let task = fasync::Task::spawn(Arc::clone(&mock).run_logger_factory(stream));
let (logger, server_end) = ::fidl::endpoints::create_proxy();
factory
.create_metric_event_logger(
&ProjectSpec {
project_id: Some(cobalt_sw_delivery_registry::PROJECT_ID),
..Default::default()
},
server_end,
)
.await
.unwrap()
.unwrap();
drop(factory);
task.await;
let events = &[MetricEvent {
metric_id: 42,
event_codes: vec![],
payload: fidl::MetricEventPayload::Count(0),
}];
logger.log_metric_events(events).await.unwrap().unwrap();
let mock_events = mock.wait_for_at_least_n_events_with_metric_id(1, 42).await;
assert_eq!(mock_events, events);
}
}