blob: b63aa6aeefe9df9006a53132077cd35722f08801 [file] [log] [blame]
// Copyright 2020 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::{
backlight::types::SerializableState,
common_utils::common::macros::{fx_err_and_bail, with_line},
};
use anyhow::Error;
use fidl_fuchsia_hardware_backlight::{DeviceMarker, DeviceProxy, State};
use fuchsia_syslog::macros::*;
use parking_lot::{RwLock, RwLockUpgradableReadGuard};
use serde_json::Value;
#[derive(Debug)]
pub struct BacklightFacade {
proxy: RwLock<Option<DeviceProxy>>,
}
impl std::convert::From<SerializableState> for State {
fn from(state: SerializableState) -> Self {
Self { backlight_on: state.backlight_on, brightness: state.brightness }
}
}
impl BacklightFacade {
pub fn new() -> BacklightFacade {
Self { proxy: RwLock::new(None) }
}
#[cfg(test)]
fn new_with_proxy(proxy: DeviceProxy) -> Self {
Self { proxy: RwLock::new(Some(proxy)) }
}
fn get_proxy(&self) -> Result<DeviceProxy, Error> {
let lock = self.proxy.upgradable_read();
if let Some(proxy) = lock.as_ref() {
Ok(proxy.clone())
} else {
let tag = "BacklightFacade::get_proxy";
let (proxy, server) = match fidl::endpoints::create_proxy::<DeviceMarker>() {
Ok(r) => r,
Err(e) => fx_err_and_bail!(
&with_line!(tag),
format_err!("Failed to get backlight proxy: {:?}", e)
),
};
match fdio::service_connect("/dev/class/backlight/000", server.into_channel()) {
Ok(r) => r,
Err(e) => fx_err_and_bail!(
&with_line!(tag),
format_err!("Failed to connect to backlight service: {:?}", e)
),
}
*RwLockUpgradableReadGuard::upgrade(lock) = Some(proxy.clone());
Ok(proxy)
}
}
pub async fn get_state_normalized(&self, _args: Value) -> Result<SerializableState, Error> {
match self.get_proxy()?.get_state_normalized().await? {
Ok(r) => Ok(SerializableState::from(r)),
Err(e) => {
let tag = "BacklightFacade::get_state_normalized";
fx_err_and_bail!(
&with_line!(tag),
format_err!("GetStateNormalized failed: {:?}", e)
)
}
}
}
pub async fn set_state_normalized(&self, args: Value) -> Result<(), Error> {
let state: SerializableState = serde_json::from_value(args)?;
match self.get_proxy()?.set_state_normalized(&mut State::from(state)).await? {
Ok(r) => Ok(r),
Err(e) => {
let tag = "BacklightFacade::set_state_normalized";
fx_err_and_bail!(
&with_line!(tag),
format_err!("SetStateNormalized failed: {:?}", e)
)
}
}
}
pub async fn get_normalized_brightness_scale(&self, _args: Value) -> Result<f64, Error> {
match self.get_proxy()?.get_normalized_brightness_scale().await? {
Ok(r) => Ok(r),
Err(e) => {
let tag = "BacklightFacade::get_normalized_brightness_scale";
fx_err_and_bail!(
&with_line!(tag),
format_err!("GetNormalizedBrightnessScale failed: {:?}", e)
)
}
}
}
pub async fn set_normalized_brightness_scale(&self, args: Value) -> Result<(), Error> {
let scale: f64 = serde_json::from_value(args)?;
match self.get_proxy()?.set_normalized_brightness_scale(scale).await? {
Ok(r) => Ok(r),
Err(e) => {
let tag = "BacklightFacade::set_normalized_brightness_scale";
fx_err_and_bail!(
&with_line!(tag),
format_err!("SetNormalizedBrightnessScale failed: {:?}", e)
)
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use fidl_fuchsia_hardware_backlight::DeviceRequest;
use futures::{future::Future, join, stream::StreamExt};
use matches::assert_matches;
use serde_json::json;
struct MockBacklightBuilder {
expected: Vec<Box<dyn FnOnce(DeviceRequest) + Send + 'static>>,
}
impl MockBacklightBuilder {
fn new() -> Self {
Self { expected: vec![] }
}
fn push(mut self, request: impl FnOnce(DeviceRequest) + Send + 'static) -> Self {
self.expected.push(Box::new(request));
self
}
fn expect_get_state_normalized(self, state: State) -> Self {
self.push(move |req| match req {
DeviceRequest::GetStateNormalized { responder } => {
assert_matches!(responder.send(&mut Ok(state)), Ok(()));
}
req => panic!("unexpected request: {:?}", req),
})
}
fn expect_set_state_normalized(self, expected_state: State) -> Self {
self.push(move |req| match req {
DeviceRequest::SetStateNormalized { state, responder } => {
assert_eq!(state, expected_state);
assert_matches!(responder.send(&mut Ok(())), Ok(()));
}
req => panic!("unexpected request: {:?}", req),
})
}
fn expect_get_normalized_brightness_scale(self, scale: f64) -> Self {
self.push(move |req| match req {
DeviceRequest::GetNormalizedBrightnessScale { responder } => {
assert_matches!(responder.send(&mut Ok(scale)), Ok(()));
}
req => panic!("unexpected request: {:?}", req),
})
}
fn expect_set_normalized_brightness_scale(self, expected_scale: f64) -> Self {
self.push(move |req| match req {
DeviceRequest::SetNormalizedBrightnessScale { scale, responder } => {
assert_eq!(scale, expected_scale);
assert_matches!(responder.send(&mut Ok(())), Ok(()));
}
req => panic!("unexpected request: {:?}", req),
})
}
fn build(self) -> (BacklightFacade, impl Future<Output = ()>) {
let (proxy, mut stream) =
fidl::endpoints::create_proxy_and_stream::<DeviceMarker>().unwrap();
let fut = async move {
for expected in self.expected {
expected(stream.next().await.unwrap().unwrap());
}
assert_matches!(stream.next().await, None);
};
(BacklightFacade::new_with_proxy(proxy), fut)
}
}
#[fuchsia_async::run_singlethreaded(test)]
async fn get_state_normalized() {
let (facade, expectations) = MockBacklightBuilder::new()
.expect_get_state_normalized(State { backlight_on: true, brightness: 0.25 })
.build();
let test = async move {
let result = facade.get_state_normalized(Value::Null).await;
assert!(result.is_ok());
assert_eq!(result.unwrap(), SerializableState { backlight_on: true, brightness: 0.25 });
};
join!(expectations, test);
}
#[fuchsia_async::run_singlethreaded(test)]
async fn set_state_normalized() {
let (facade, expectations) = MockBacklightBuilder::new()
.expect_set_state_normalized(State { backlight_on: false, brightness: 0.75 })
.build();
let test = async move {
let result = facade
.set_state_normalized(json!({"backlight_on": false, "brightness": 0.75}))
.await;
assert!(result.is_ok());
};
join!(expectations, test);
}
#[fuchsia_async::run_singlethreaded(test)]
async fn get_normalized_brightness_scale() {
let (facade, expectations) =
MockBacklightBuilder::new().expect_get_normalized_brightness_scale(0.75).build();
let test = async move {
let result = facade.get_normalized_brightness_scale(Value::Null).await;
assert!(result.is_ok());
assert_eq!(result.unwrap(), 0.75);
};
join!(expectations, test);
}
#[fuchsia_async::run_singlethreaded(test)]
async fn set_normalized_brightness_scale() {
let (facade, expectations) =
MockBacklightBuilder::new().expect_set_normalized_brightness_scale(0.25).build();
let test = async move {
let result = facade.set_normalized_brightness_scale(json!(0.25)).await;
assert!(result.is_ok());
};
join!(expectations, test);
}
}