blob: 1f94f9de30a21ee61f81a7ae9db8674c0737fce7 [file] [log] [blame]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use crate::common_utils::common::macros::{fx_err_and_bail, with_line};
use crate::power::types;
use anyhow::Error;
use fidl_fuchsia_power_metrics::{Metric, Power, RecorderMarker, RecorderProxy, StatisticsArgs};
use fuchsia_component::client::connect_to_protocol;
use serde_json::Value;
const CLIENT_ID: &'static str = "sl4f";
#[derive(Debug)]
pub struct PowerFacade {
/// Optional logger proxy for testing.
logger_proxy: Option<RecorderProxy>,
}
impl PowerFacade {
pub fn new() -> PowerFacade {
PowerFacade { logger_proxy: None }
}
fn get_logger_proxy(&self) -> Result<RecorderProxy, Error> {
if let Some(proxy) = &self.logger_proxy {
Ok(proxy.clone())
} else {
match connect_to_protocol::<RecorderMarker>() {
Ok(proxy) => Ok(proxy),
Err(e) => fx_err_and_bail!(
&with_line!("PowerFacade::get_logger_proxy"),
format_err!("Failed to create proxy: {:?}", e)
),
}
}
}
/// Initiates fixed-duration logging with the Recorder service.
///
/// # Arguments
/// * `args`: JSON value containing the StartLoggingRequest information:
/// Key `sampling_interval_ms` specifies the interval for polling the sensors.
/// Key `duration_ms` specifies the duration of logging.
/// Key `statistics_interval_ms` specifies the interval for summarizing statistics; if
/// omitted, statistics is disabled. Refer to `fuchsia.power.metrics` for more details.
pub async fn start_logging(&self, args: Value) -> Result<types::RecorderResult, Error> {
let req: types::StartLoggingRequest = serde_json::from_value(args)?;
let statistics_args = req
.statistics_interval_ms
.map(|i| Box::new(StatisticsArgs { statistics_interval_ms: i }));
let proxy = self.get_logger_proxy()?;
// Calls into `fuchsia.power.metrics.Recorder`.
proxy
.start_logging(
CLIENT_ID,
&[Metric::Power(Power {
sampling_interval_ms: req.sampling_interval_ms,
statistics_args,
})],
req.duration_ms,
/* output_samples_to_syslog */ false,
/* output_stats_to_syslog */ false,
)
.await?
.map_err(|e| format_err!("Received RecorderError: {:?}", e))?;
Ok(types::RecorderResult::Success)
}
/// Initiates durationless logging with the Recorder service.
///
/// # Arguments
/// * `args`: JSON value containing the StartLoggingRequest information:
/// Key `sampling_interval_ms` specifies the interval for polling the sensors.
/// Key `statistics_interval_ms` specifies the interval for summarizing statistics; if
/// omitted, statistics is disabled. Refer to `fuchsia.power.metrics` for more details.
pub async fn start_logging_forever(&self, args: Value) -> Result<types::RecorderResult, Error> {
let req: types::StartLoggingForeverRequest = serde_json::from_value(args)?;
let statistics_args = req
.statistics_interval_ms
.map(|i| Box::new(StatisticsArgs { statistics_interval_ms: i }));
let proxy = self.get_logger_proxy()?;
// Calls into `fuchsia.power.metrics.Recorder`.
proxy
.start_logging_forever(
CLIENT_ID,
&[Metric::Power(Power {
sampling_interval_ms: req.sampling_interval_ms,
statistics_args,
})],
/* output_samples_to_syslog */ false,
/* output_stats_to_syslog */ false,
)
.await?
.map_err(|e| format_err!("Received RecorderError: {:?}", e))?;
Ok(types::RecorderResult::Success)
}
/// Terminates logging by the Recorder service.
pub async fn stop_logging(&self) -> Result<types::RecorderResult, Error> {
self.get_logger_proxy()?.stop_logging(CLIENT_ID).await?;
Ok(types::RecorderResult::Success)
}
}
#[cfg(test)]
mod tests {
use super::*;
use assert_matches::assert_matches;
use fidl::endpoints::create_proxy_and_stream;
use fidl_fuchsia_power_metrics::RecorderRequest;
use fuchsia_async as fasync;
use futures::prelude::*;
use serde_json::json;
/// Tests that the `start_logging` method correctly queries the logger.
#[fasync::run_singlethreaded(test)]
async fn test_start_logging() {
let query_sampling_interval_ms = 500;
let query_duration_ms = 10_000;
let query_statitics_interval_ms = 1_000;
let (proxy, mut stream) = create_proxy_and_stream::<RecorderMarker>().unwrap();
let _stream_task = fasync::Task::local(async move {
match stream.try_next().await {
Ok(Some(RecorderRequest::StartLogging {
client_id,
metrics,
duration_ms,
output_samples_to_syslog,
output_stats_to_syslog,
responder,
})) => {
assert_eq!(String::from("sl4f"), client_id);
assert_eq!(metrics.len(), 1);
assert_eq!(
metrics[0],
Metric::Power(Power {
sampling_interval_ms: query_sampling_interval_ms,
statistics_args: Some(Box::new(StatisticsArgs {
statistics_interval_ms: query_statitics_interval_ms,
})),
}),
);
assert_eq!(output_samples_to_syslog, false);
assert_eq!(output_stats_to_syslog, false);
assert_eq!(duration_ms, query_duration_ms);
responder.send(Ok(())).unwrap();
}
other => panic!("Unexpected stream item: {:?}", other),
}
});
let facade = PowerFacade { logger_proxy: Some(proxy) };
assert_matches!(
facade
.start_logging(json!({
"sampling_interval_ms": query_sampling_interval_ms,
"statistics_interval_ms": query_statitics_interval_ms,
"duration_ms": query_duration_ms
}))
.await,
Ok(types::RecorderResult::Success)
);
}
/// Tests that the `start_logging_forever` method correctly queries the logger.
#[fasync::run_singlethreaded(test)]
async fn test_start_logging_forever() {
let query_sampling_interval_ms = 500;
let (proxy, mut stream) = create_proxy_and_stream::<RecorderMarker>().unwrap();
let _stream_task = fasync::Task::local(async move {
match stream.try_next().await {
Ok(Some(RecorderRequest::StartLoggingForever {
client_id,
metrics,
output_samples_to_syslog,
output_stats_to_syslog,
responder,
})) => {
assert_eq!(String::from("sl4f"), client_id);
assert_eq!(metrics.len(), 1);
assert_eq!(
metrics[0],
Metric::Power(Power {
sampling_interval_ms: query_sampling_interval_ms,
statistics_args: None,
}),
);
assert_eq!(output_samples_to_syslog, false);
assert_eq!(output_stats_to_syslog, false);
responder.send(Ok(())).unwrap();
}
other => panic!("Unexpected stream item: {:?}", other),
}
});
let facade = PowerFacade { logger_proxy: Some(proxy) };
assert_matches!(
facade
.start_logging_forever(json!({
"sampling_interval_ms": query_sampling_interval_ms
}))
.await,
Ok(types::RecorderResult::Success)
);
}
/// Tests that the `stop_logging` method correctly queries the logger.
#[fasync::run_singlethreaded(test)]
async fn test_stop_logging() {
let (proxy, mut stream) = create_proxy_and_stream::<RecorderMarker>().unwrap();
let _stream_task = fasync::Task::local(async move {
match stream.try_next().await {
Ok(Some(RecorderRequest::StopLogging { client_id, responder })) => {
assert_eq!(String::from("sl4f"), client_id);
responder.send(true).unwrap()
}
other => panic!("Unexpected stream item: {:?}", other),
}
});
let facade = PowerFacade { logger_proxy: Some(proxy) };
assert_matches!(facade.stop_logging().await, Ok(types::RecorderResult::Success));
}
}