blob: 9f866bb8f00b6e8bd00fba546ef9ab55c9f53376 [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::{
common_utils::common::macros::{fx_err_and_bail, with_line},
light::types::{SerializableGroupInfo, SerializableInfo, SerializableRgb},
};
use anyhow::Error;
use fidl_fuchsia_hardware_light::{LightMarker, LightProxy, Rgb};
use fuchsia_syslog::macros::*;
use glob::glob;
use parking_lot::{RwLock, RwLockUpgradableReadGuard};
/// Perform Light operations.
///
/// Note this object is shared among all threads created by server.
///
#[derive(Debug)]
pub struct LightFacade {
proxy: RwLock<Option<LightProxy>>,
}
impl LightFacade {
pub fn new() -> Self {
Self { proxy: RwLock::new(None) }
}
#[cfg(test)]
fn new_with_proxy(proxy: LightProxy) -> Self {
Self { proxy: RwLock::new(Some(proxy)) }
}
fn get_proxy(&self) -> Result<LightProxy, Error> {
let tag = "LightFacade::get_proxy";
let lock = self.proxy.upgradable_read();
if let Some(proxy) = lock.as_ref() {
Ok(proxy.clone())
} else {
let (proxy, server) = match fidl::endpoints::create_proxy::<LightMarker>() {
Ok(r) => r,
Err(e) => fx_err_and_bail!(
&with_line!(tag),
format_err!("Failed to get light proxy {:?}", e)
),
};
let found_path = glob("/dev/class/light/*")?.filter_map(|entry| entry.ok()).next();
match found_path {
Some(path) => {
fdio::service_connect(path.to_string_lossy().as_ref(), server.into_channel())?;
*RwLockUpgradableReadGuard::upgrade(lock) = Some(proxy.clone());
Ok(proxy)
}
None => fx_err_and_bail!(&with_line!(tag), format_err!("Failed to find device")),
}
}
}
pub async fn get_num_lights(&self) -> Result<u32, Error> {
Ok(self.get_proxy()?.get_num_lights().await?)
}
pub async fn get_num_light_groups(&self) -> Result<u32, Error> {
Ok(self.get_proxy()?.get_num_light_groups().await?)
}
pub async fn get_info(&self, index: u32) -> Result<SerializableInfo, Error> {
let tag = "LightFacade::get_info";
match self.get_proxy()?.get_info(index).await? {
Ok(r) => Ok(SerializableInfo::new(&r)),
Err(e) => fx_err_and_bail!(&with_line!(tag), format_err!("GetInfo failed {:?}", e)),
}
}
pub async fn get_current_simple_value(&self, index: u32) -> Result<bool, Error> {
let tag = "LightFacade::get_current_simple_value";
match self.get_proxy()?.get_current_simple_value(index).await? {
Ok(r) => Ok(r),
Err(e) => fx_err_and_bail!(
&with_line!(tag),
format_err!("GetCurrentSimpleValue failed {:?}", e)
),
}
}
pub async fn set_simple_value(&self, index: u32, value: bool) -> Result<(), Error> {
let tag = "LightFacade::set_simple_value";
match self.get_proxy()?.set_simple_value(index, value).await? {
Ok(r) => Ok(r),
Err(e) => {
fx_err_and_bail!(&with_line!(tag), format_err!("SetSimpleValue failed {:?}", e))
}
}
}
pub async fn get_current_brightness_value(&self, index: u32) -> Result<f64, Error> {
let tag = "LightFacade::get_current_brightness_value";
match self.get_proxy()?.get_current_brightness_value(index).await? {
Ok(r) => Ok(r),
Err(e) => fx_err_and_bail!(
&with_line!(tag),
format_err!("GetCurrentBrightnessValue failed {:?}", e)
),
}
}
pub async fn set_brightness_value(&self, index: u32, value: f64) -> Result<(), Error> {
let tag = "LightFacade::set_brightness_value";
match self.get_proxy()?.set_brightness_value(index, value).await? {
Ok(r) => Ok(r),
Err(e) => {
fx_err_and_bail!(&with_line!(tag), format_err!("SetBrightnessValue failed {:?}", e))
}
}
}
pub async fn get_current_rgb_value(&self, index: u32) -> Result<SerializableRgb, Error> {
let tag = "LightFacade::get_current_rgb_value";
match self.get_proxy()?.get_current_rgb_value(index).await? {
Ok(r) => Ok(SerializableRgb::new(&r)),
Err(e) => {
fx_err_and_bail!(&with_line!(tag), format_err!("GetCurrentRgbValue failed {:?}", e))
}
}
}
pub async fn set_rgb_value(&self, index: u32, value: SerializableRgb) -> Result<(), Error> {
let tag = "LightFacade::set_rgb_value";
let mut rgb = Rgb { red: value.red, green: value.green, blue: value.blue };
match self.get_proxy()?.set_rgb_value(index, &mut rgb).await? {
Ok(r) => Ok(r),
Err(e) => fx_err_and_bail!(&with_line!(tag), format_err!("SetRgbValue failed {:?}", e)),
}
}
pub async fn get_group_info(&self, index: u32) -> Result<SerializableGroupInfo, Error> {
let tag = "LightFacade::get_group_info";
match self.get_proxy()?.get_group_info(index).await? {
Ok(r) => Ok(SerializableGroupInfo::new(&r)),
Err(e) => {
fx_err_and_bail!(&with_line!(tag), format_err!("GetGroupInfo failed {:?}", e))
}
}
}
pub async fn get_group_current_simple_value(&self, index: u32) -> Result<Vec<bool>, Error> {
let tag = "LightFacade::get_group_current_simple_value";
match self.get_proxy()?.get_group_current_simple_value(index).await? {
Ok(r) => match r {
Some(v) => Ok(v),
None => fx_err_and_bail!(&with_line!(tag), format_err!("Expected a vector")),
},
Err(e) => fx_err_and_bail!(
&with_line!(tag),
format_err!("GetGroupCurrentSimpleValue failed {:?}", e)
),
}
}
pub async fn set_group_simple_value(&self, index: u32, value: Vec<bool>) -> Result<(), Error> {
let tag = "LightFacade::set_group_simple_value";
match self.get_proxy()?.set_group_simple_value(index, &mut value.into_iter()).await? {
Ok(_) => Ok(()),
Err(e) => fx_err_and_bail!(
&with_line!(tag),
format_err!("SetGroupSimpleValue failed {:?}", e)
),
}
}
pub async fn get_group_current_brightness_value(&self, index: u32) -> Result<Vec<f64>, Error> {
let tag = "LightFacade::get_group_current_brightness_value";
match self.get_proxy()?.get_group_current_brightness_value(index).await? {
Ok(r) => match r {
Some(v) => Ok(v),
None => fx_err_and_bail!(&with_line!(tag), format_err!("Expected a vector")),
},
Err(e) => fx_err_and_bail!(
&with_line!(tag),
format_err!("GetGroupCurrentBrightnessValue failed {:?}", e)
),
}
}
pub async fn set_group_brightness_value(
&self,
index: u32,
value: Vec<f64>,
) -> Result<(), Error> {
let tag = "LightFacade::set_group_brightness_value";
match self.get_proxy()?.set_group_brightness_value(index, &value).await? {
Ok(_) => Ok(()),
Err(e) => fx_err_and_bail!(
&with_line!(tag),
format_err!("SetGroupBrightnessValue failed {:?}", e)
),
}
}
pub async fn get_group_current_rgb_value(
&self,
index: u32,
) -> Result<Vec<SerializableRgb>, Error> {
let tag = "LightFacade::get_group_current_rgb_value";
let values = match self.get_proxy()?.get_group_current_rgb_value(index).await? {
Ok(r) => match r {
Some(v) => v,
None => fx_err_and_bail!(&with_line!(tag), format_err!("Expected a vector")),
},
Err(e) => fx_err_and_bail!(
&with_line!(tag),
format_err!("GetGroupCurrentRgbValue failed {:?}", e)
),
};
let ret = values
.into_iter()
.map(|x| SerializableRgb { red: x.red, green: x.green, blue: x.blue })
.collect::<Vec<_>>();
Ok(ret)
}
pub async fn set_group_rgb_value(
&self,
index: u32,
value: Vec<SerializableRgb>,
) -> Result<(), Error> {
let tag = "LightFacade::set_group_rgb_value";
let mut rgb = value
.into_iter()
.map(|x| Rgb { red: x.red, green: x.green, blue: x.blue })
.collect::<Vec<Rgb>>();
match self.get_proxy()?.set_group_rgb_value(index, &mut rgb.iter_mut()).await? {
Ok(_) => Ok(()),
Err(e) => {
fx_err_and_bail!(&with_line!(tag), format_err!("SetGroupRgbValue failed {:?}", e))
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::light::types::SerializableCapability;
use fidl_fuchsia_hardware_light::{Capability, GroupInfo, Info, LightError, LightRequest, Rgb};
use futures::{future::Future, join, stream::StreamExt};
use matches::assert_matches;
struct MockLightBuilder {
expected: Vec<Box<dyn FnOnce(LightRequest) + Send + 'static>>,
}
impl MockLightBuilder {
fn new() -> Self {
Self { expected: vec![] }
}
fn push(mut self, request: impl FnOnce(LightRequest) + Send + 'static) -> Self {
self.expected.push(Box::new(request));
self
}
fn expect_get_num_lights(self, count: u32) -> Self {
self.push(move |req| match req {
LightRequest::GetNumLights { responder } => responder.send(count).unwrap(),
req => panic!("unexpected request: {:?}", req),
})
}
fn expect_get_num_light_groups(self, count: u32) -> Self {
self.push(move |req| match req {
LightRequest::GetNumLightGroups { responder } => responder.send(count).unwrap(),
req => panic!("unexpected request: {:?}", req),
})
}
fn expect_get_info(self, idx: u32, res: Result<SerializableInfo, LightError>) -> Self {
self.push(move |req| match req {
LightRequest::GetInfo { index, responder } => {
assert_eq!(idx, index);
responder
.send(&mut res.map(|info| Info {
name: info.name.clone(),
capability: Capability::from(info.capability),
}))
.unwrap()
}
req => panic!("unexpected request: {:?}", req),
})
}
fn expect_get_current_simple_value(self, idx: u32, res: Result<bool, LightError>) -> Self {
self.push(move |req| match req {
LightRequest::GetCurrentSimpleValue { index, responder } => {
assert_eq!(idx, index);
responder.send(&mut res.map(Into::into)).unwrap()
}
req => panic!("unexpected request: {:?}", req),
})
}
fn expect_set_simple_value(self, idx: u32, val: bool, res: Result<(), LightError>) -> Self {
self.push(move |req| match req {
LightRequest::SetSimpleValue { index, value, responder } => {
assert_eq!(idx, index);
assert_eq!(val, value);
responder.send(&mut res.map(Into::into)).unwrap()
}
req => panic!("unexpected request: {:?}", req),
})
}
fn expect_get_current_brightness_value(
self,
idx: u32,
res: Result<f64, LightError>,
) -> Self {
self.push(move |req| match req {
LightRequest::GetCurrentBrightnessValue { index, responder } => {
assert_eq!(idx, index);
responder.send(&mut res.map(Into::into)).unwrap()
}
req => panic!("unexpected request: {:?}", req),
})
}
fn expect_set_brightness_value(
self,
idx: u32,
val: f64,
res: Result<(), LightError>,
) -> Self {
self.push(move |req| match req {
LightRequest::SetBrightnessValue { index, value, responder } => {
assert_eq!(idx, index);
assert_eq!(val, value);
responder.send(&mut res.map(Into::into)).unwrap()
}
req => panic!("unexpected request: {:?}", req),
})
}
fn expect_get_current_rgb_value(
self,
idx: u32,
res: Result<SerializableRgb, LightError>,
) -> Self {
self.push(move |req| match req {
LightRequest::GetCurrentRgbValue { index, responder } => {
assert_eq!(idx, index);
responder
.send(&mut res.map(|rgb| Rgb {
red: rgb.red,
green: rgb.green,
blue: rgb.blue,
}))
.unwrap()
}
req => panic!("unexpected request: {:?}", req),
})
}
fn expect_set_rgb_value(
self,
idx: u32,
val: SerializableRgb,
res: Result<(), LightError>,
) -> Self {
self.push(move |req| match req {
LightRequest::SetRgbValue { index, value, responder } => {
assert_eq!(idx, index);
assert_eq!(val, SerializableRgb::new(&value));
responder.send(&mut res.map(Into::into)).unwrap()
}
req => panic!("unexpected request: {:?}", req),
})
}
fn expect_get_group_info(
self,
id: u32,
res: Result<SerializableGroupInfo, LightError>,
) -> Self {
self.push(move |req| match req {
LightRequest::GetGroupInfo { group_id, responder } => {
assert_eq!(id, group_id);
responder
.send(&mut res.map(|info| GroupInfo {
name: info.name.clone(),
count: info.count,
capability: Capability::from(info.capability),
}))
.unwrap()
}
req => panic!("unexpected request: {:?}", req),
})
}
fn expect_get_group_current_simple_value(
self,
id: u32,
res: Result<Vec<bool>, LightError>,
) -> Self {
self.push(move |req| match req {
LightRequest::GetGroupCurrentSimpleValue { group_id, responder } => {
assert_eq!(id, group_id);
responder.send(&mut res.map(Into::into)).unwrap()
}
req => panic!("unexpected request: {:?}", req),
})
}
fn expect_set_group_simple_value(
self,
id: u32,
vals: Vec<bool>,
res: Result<(), LightError>,
) -> Self {
self.push(move |req| match req {
LightRequest::SetGroupSimpleValue { group_id, values, responder } => {
assert_eq!(id, group_id);
assert_eq!(vals, values);
responder.send(&mut res.map(Into::into)).unwrap()
}
req => panic!("unexpected request: {:?}", req),
})
}
fn expect_get_group_current_brightness_value(
self,
id: u32,
res: Result<Vec<f64>, LightError>,
) -> Self {
self.push(move |req| match req {
LightRequest::GetGroupCurrentBrightnessValue { group_id, responder } => {
assert_eq!(id, group_id);
responder.send(&mut res.map(Into::into)).unwrap()
}
req => panic!("unexpected request: {:?}", req),
})
}
fn expect_set_group_brightness_value(
self,
id: u32,
vals: Vec<f64>,
res: Result<(), LightError>,
) -> Self {
self.push(move |req| match req {
LightRequest::SetGroupBrightnessValue { group_id, values, responder } => {
assert_eq!(id, group_id);
assert_eq!(vals, values);
responder.send(&mut res.map(Into::into)).unwrap()
}
req => panic!("unexpected request: {:?}", req),
})
}
fn expect_get_group_current_rgb_value(
self,
id: u32,
res: Result<Option<Vec<SerializableRgb>>, LightError>,
) -> Self {
self.push(move |req| match req {
LightRequest::GetGroupCurrentRgbValue { group_id, responder } => {
assert_eq!(id, group_id);
responder
.send(&mut res.map(|opt| {
opt.map(|vec| {
vec.iter().map(|x| Into::into(x.clone())).collect::<Vec<_>>()
})
}))
.unwrap()
}
req => panic!("unexpected request: {:?}", req),
})
}
fn expect_set_group_rgb_value(
self,
id: u32,
vals: Vec<SerializableRgb>,
res: Result<(), LightError>,
) -> Self {
self.push(move |req| match req {
LightRequest::SetGroupRgbValue { group_id, values, responder } => {
assert_eq!(id, group_id);
assert_eq!(vals, values.iter().map(|x| Into::into(*x)).collect::<Vec<_>>());
responder.send(&mut res.map(Into::into)).unwrap()
}
req => panic!("unexpected request: {:?}", req),
})
}
fn build(self) -> (LightFacade, impl Future<Output = ()>) {
let (proxy, mut stream) =
fidl::endpoints::create_proxy_and_stream::<LightMarker>().unwrap();
let fut = async move {
for expected in self.expected {
expected(stream.next().await.unwrap().unwrap());
}
assert_matches!(stream.next().await, None);
};
(LightFacade::new_with_proxy(proxy), fut)
}
}
#[fuchsia_async::run_singlethreaded(test)]
async fn get_num_lights_ok() {
let (facade, lights) = MockLightBuilder::new().expect_get_num_lights(3).build();
let test = async move {
assert_matches!(facade.get_num_lights().await, Ok(3));
};
join!(lights, test);
}
#[fuchsia_async::run_singlethreaded(test)]
async fn get_num_light_groups_ok() {
let (facade, lights) = MockLightBuilder::new().expect_get_num_light_groups(5).build();
let test = async move {
assert_matches!(facade.get_num_light_groups().await, Ok(5));
};
join!(lights, test);
}
#[fuchsia_async::run_singlethreaded(test)]
async fn get_info_ok() {
let (facade, lights) = MockLightBuilder::new()
.expect_get_info(
4,
Ok(SerializableInfo {
name: "test_light".to_string(),
capability: SerializableCapability::Rgb,
}),
)
.build();
let test = async move {
assert_matches!(
facade.get_info(4).await,
Ok(info) if info == SerializableInfo {
name: "test_light".to_string(),
capability: SerializableCapability::Rgb
}
);
};
join!(lights, test);
}
#[fuchsia_async::run_singlethreaded(test)]
async fn get_current_simple_value_ok() {
let (facade, lights) =
MockLightBuilder::new().expect_get_current_simple_value(2, Ok(true)).build();
let test = async move {
assert_matches!(facade.get_current_simple_value(2).await, Ok(true));
};
join!(lights, test);
}
#[fuchsia_async::run_singlethreaded(test)]
async fn set_simple_value_ok() {
let (facade, lights) =
MockLightBuilder::new().expect_set_simple_value(1, false, Ok(())).build();
let test = async move {
assert_matches!(facade.set_simple_value(1, false).await, Ok(()));
};
join!(lights, test);
}
#[fuchsia_async::run_singlethreaded(test)]
async fn get_current_brightness_value_ok() {
let (facade, lights) =
MockLightBuilder::new().expect_get_current_brightness_value(0, Ok(0.3321)).build();
let test = async move {
assert_matches!(facade.get_current_brightness_value(0).await, Ok(v) if v == 0.3321);
};
join!(lights, test);
}
#[fuchsia_async::run_singlethreaded(test)]
async fn set_brightness_value_ok() {
let (facade, lights) =
MockLightBuilder::new().expect_set_brightness_value(1, 0.2515, Ok(())).build();
let test = async move {
assert_matches!(facade.set_brightness_value(1, 0.2515).await, Ok(()));
};
join!(lights, test);
}
#[fuchsia_async::run_singlethreaded(test)]
async fn get_current_rgb_value_ok() {
let (facade, lights) = MockLightBuilder::new()
.expect_get_current_rgb_value(
4,
Ok(SerializableRgb { red: 0.21, green: 0.94, blue: 0.59 }),
)
.build();
let test = async move {
assert_matches!(
facade.get_current_rgb_value(4).await,
Ok(rgb) if rgb == SerializableRgb { red: 0.21, green: 0.94, blue: 0.59 }
);
};
join!(lights, test);
}
#[fuchsia_async::run_singlethreaded(test)]
async fn set_rgb_value_ok() {
let (facade, lights) = MockLightBuilder::new()
.expect_set_rgb_value(
3,
SerializableRgb { red: 0.33, green: 0.45, blue: 0.155 },
Ok(()),
)
.build();
let test = async move {
assert_matches!(
facade
.set_rgb_value(3, SerializableRgb { red: 0.33, green: 0.45, blue: 0.155 })
.await,
Ok(())
);
};
join!(lights, test);
}
#[fuchsia_async::run_singlethreaded(test)]
async fn get_group_info_ok() {
let (facade, lights) = MockLightBuilder::new()
.expect_get_group_info(
2,
Ok(SerializableGroupInfo {
name: "test_light_group".to_string(),
count: 5,
capability: SerializableCapability::Brightness,
}),
)
.build();
let test = async move {
assert_matches!(
facade.get_group_info(2).await,
Ok(info) if info == SerializableGroupInfo {
name: "test_light_group".to_string(),
count: 5,
capability: SerializableCapability::Brightness
}
);
};
join!(lights, test);
}
#[fuchsia_async::run_singlethreaded(test)]
async fn get_group_current_simple_value_ok() {
let (facade, lights) = MockLightBuilder::new()
.expect_get_group_current_simple_value(0, Ok(vec![false, true, false]))
.build();
let test = async move {
assert_matches!(
facade.get_group_current_simple_value(0).await,
Ok(v) if v == vec![false, true, false]
);
};
join!(lights, test);
}
#[fuchsia_async::run_singlethreaded(test)]
async fn set_group_simple_value_ok() {
let (facade, lights) = MockLightBuilder::new()
.expect_set_group_simple_value(3, vec![true, true, false], Ok(()))
.build();
let test = async move {
assert_matches!(
facade.set_group_simple_value(3, vec![true, true, false]).await,
Ok(())
);
};
join!(lights, test);
}
#[fuchsia_async::run_singlethreaded(test)]
async fn get_group_current_brightness_value_ok() {
let (facade, lights) = MockLightBuilder::new()
.expect_get_group_current_brightness_value(1, Ok(vec![0.46, 0.2, 0.77, 0.315, 0.8]))
.build();
let test = async move {
assert_matches!(
facade.get_group_current_brightness_value(1).await,
Ok(v) if v == vec![0.46, 0.2, 0.77, 0.315, 0.8]
);
};
join!(lights, test);
}
#[fuchsia_async::run_singlethreaded(test)]
async fn set_group_brightness_value_ok() {
let (facade, lights) = MockLightBuilder::new()
.expect_set_group_brightness_value(2, vec![0.0, 0.96, 0.37, 0.63], Ok(()))
.build();
let test = async move {
assert_matches!(
facade.set_group_brightness_value(2, vec![0.0, 0.96, 0.37, 0.63]).await,
Ok(())
);
};
join!(lights, test);
}
#[fuchsia_async::run_singlethreaded(test)]
async fn get_group_current_rgb_value_ok() {
let (facade, lights) = MockLightBuilder::new()
.expect_get_group_current_rgb_value(
0,
Ok(Some(vec![
SerializableRgb { red: 0.24, green: 0.41, blue: 0.121 },
SerializableRgb { red: 0.83, green: 0.23, blue: 0.155 },
])),
)
.build();
let test = async move {
assert_matches!(
facade.get_group_current_rgb_value(0).await,
Ok(v) if v == vec![
SerializableRgb { red: 0.24, green: 0.41, blue: 0.121 },
SerializableRgb { red: 0.83, green: 0.23, blue: 0.155 }
]
);
};
join!(lights, test);
}
#[fuchsia_async::run_singlethreaded(test)]
async fn set_group_rgb_value_ok() {
let (facade, lights) = MockLightBuilder::new()
.expect_set_group_rgb_value(
1,
vec![
SerializableRgb { red: 0.0, green: 0.3, blue: 0.3 },
SerializableRgb { red: 0.111, green: 0.222, blue: 0.11 },
SerializableRgb { red: 0.194, green: 0.9, blue: 0.70 },
],
Ok(()),
)
.build();
let test = async move {
assert_matches!(
facade
.set_group_rgb_value(
1,
vec![
SerializableRgb { red: 0.0, green: 0.3, blue: 0.3 },
SerializableRgb { red: 0.111, green: 0.222, blue: 0.11 },
SerializableRgb { red: 0.194, green: 0.9, blue: 0.70 }
]
)
.await,
Ok(())
);
};
join!(lights, test);
}
}