blob: d0509f036d6498ba3d169f66febdaab45b4a6c76 [file] [log] [blame]
// Copyright 2019 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 anyhow::{format_err, Context as _, Error};
use fidl_fuchsia_devicesettings::{
DeviceSettingsManagerRequest, DeviceSettingsManagerRequestStream, DeviceSettingsWatcherProxy,
Status, ValueType,
};
use fuchsia_async as fasync;
use fuchsia_component::server::{ServiceFs, ServiceFsDir};
use futures::{lock::Mutex, StreamExt, TryStreamExt};
use std::collections::HashMap;
use structopt::StructOpt;
#[derive(StructOpt, Debug)]
#[structopt(name = "mock_device_settings")]
/// Serves DeviceSettingsManager with a memory-backed database
struct Opt {
#[structopt(short = "s")]
/// Starts with a pre-set string key, format [k]=[v]
/// e.g. DeviceName=my-device-name
string_key: Vec<String>,
#[structopt(short = "i")]
/// Starts with a pre-set integer key, format [k]=[v]
/// e.g. Audio=100
int_key: Vec<String>,
}
#[derive(Debug)]
enum Key {
StringKey(String),
IntKey(i64),
}
struct DeviceSettingsManagerServer {
keys: HashMap<String, Key>,
watchers: HashMap<String, Vec<DeviceSettingsWatcherProxy>>,
}
impl DeviceSettingsManagerServer {
fn run_watchers(&mut self, key: &str, t: ValueType) {
if let Some(m) = self.watchers.get_mut(key) {
m.retain(|w| {
if let Err(e) = w.on_change_settings(t) {
if e.is_closed() {
return false;
}
log::info!("Error call watcher: {:?}", e);
}
return true;
});
}
}
}
fn split_once(in_string: &str) -> Result<(&str, &str), Error> {
let mut splitter = in_string.splitn(2, '=');
let first = splitter.next().ok_or(format_err!("Invalid key value format"))?;
let second = splitter.next().ok_or(format_err!("Invalid key value format"))?;
Ok((first, second))
}
fn config_state(state: &mut DeviceSettingsManagerServer, opt: Opt) -> Result<(), Error> {
for s_key in opt.string_key {
let (k, v) = split_once(&s_key)?;
log::info!("Startup {}={}", k, v);
if let Some(previous_value) =
state.keys.insert(String::from(k), Key::StringKey(String::from(v)))
{
panic!("duplicate key {}={} in startup, previously set to {:?}", k, v, previous_value);
}
}
for i_key in opt.int_key {
let (k, v) = split_once(&i_key)?;
let v = v.parse::<i64>()?;
log::info!("Startup {}={}", k, v);
if let Some(previous_value) = state.keys.insert(String::from(k), Key::IntKey(v)) {
panic!("duplicate key {}={} in startup, previously set to {:?}", k, v, previous_value);
}
}
Ok(())
}
async fn run_request_stream(
state: &Mutex<DeviceSettingsManagerServer>,
stream: DeviceSettingsManagerRequestStream,
) {
stream
.try_for_each(move |req| async move {
let mut state = state.lock().await;
match req {
DeviceSettingsManagerRequest::GetInteger { key, responder } => {
match state.keys.get(&key) {
None => {
log::info!("Key {} doesn't exist", key);
responder.send(0, Status::ErrNotSet)
}
Some(Key::IntKey(val)) => responder.send(*val, Status::Ok),
_ => {
log::info!("Key {} is not an integer", key);
responder.send(0, Status::ErrIncorrectType)
}
}
}
DeviceSettingsManagerRequest::GetString { key, responder } => {
match state.keys.get(&key) {
None => {
log::info!("Key {} doesn't exist", key);
responder.send("", Status::ErrNotSet)
}
Some(Key::StringKey(val)) => responder.send(val, Status::Ok),
_ => {
log::info!("Key {} is not a string", key);
responder.send("", Status::ErrIncorrectType)
}
}
}
DeviceSettingsManagerRequest::SetInteger { key, val, responder } => {
match state.keys.get(&key) {
Some(Key::IntKey(_)) => {
log::info!("Set {}={}", key, val);
state.run_watchers(&key, ValueType::Number);
let _: Option<Key> = state.keys.insert(key, Key::IntKey(val));
responder.send(true)
}
_ => {
log::info!("Failed to set integer key {}={}", key, val);
responder.send(false)
}
}
}
DeviceSettingsManagerRequest::SetString { key, val, responder } => {
match state.keys.get(&key) {
Some(Key::StringKey(_)) => {
log::info!("Set {}={}", key, val);
state.run_watchers(&key, ValueType::Text);
let _: Option<Key> = state.keys.insert(key, Key::StringKey(val));
responder.send(true)
}
_ => {
log::info!("Failed to set string key {}={}", key, val);
responder.send(false)
}
}
}
DeviceSettingsManagerRequest::Watch { key, watcher, responder } => {
match state.keys.get(&key) {
None => {
log::info!("Can't watch key {}, it doesn't exist", key);
responder.send(Status::ErrInvalidSetting)
}
_ => match watcher.into_proxy() {
Ok(watcher) => {
let mv = state.watchers.entry(key).or_insert(Vec::new());
mv.push(watcher);
responder.send(Status::Ok)
}
Err(e) => {
log::info!("Error watching key {}: {}", key, e);
responder.send(Status::ErrUnknown)
}
},
}
}
}
})
.await
.unwrap_or_else(|e| {
log::error!("error running mock device settings server: {:?}", e);
})
}
#[fasync::run_singlethreaded]
async fn main() -> Result<(), Error> {
let () = fuchsia_syslog::init().context("cannot init logger")?;
let opt = Opt::from_args();
let mut state = DeviceSettingsManagerServer { keys: HashMap::new(), watchers: HashMap::new() };
let () = config_state(&mut state, opt)?;
let state = Mutex::new(state);
let mut fs = ServiceFs::new();
let _: &mut ServiceFsDir<'_, _> =
fs.dir("svc").add_fidl_service(|stream: DeviceSettingsManagerRequestStream| stream);
let _: &mut ServiceFs<_> = fs.take_and_serve_directory_handle()?;
let () = fs.for_each_concurrent(None, |stream| run_request_stream(&state, stream)).await;
Ok(())
}
#[cfg(test)]
mod test {
use anyhow::Context as _;
use fidl_fuchsia_devicesettings::{
DeviceSettingsManagerMarker, DeviceSettingsWatcherMarker, DeviceSettingsWatcherRequest,
Status,
};
use fuchsia_async as fasync;
use fuchsia_component::client;
use futures::{future, TryStreamExt};
#[fasync::run_singlethreaded]
#[test]
async fn test_string_key() {
let manager = client::connect_to_service::<DeviceSettingsManagerMarker>().unwrap();
let res = manager
.set_string("StringKey", "HelloWorld")
.await
.context("can't set string")
.unwrap();
assert_eq!(res, true);
let (val, status) =
manager.get_string("StringKey").await.context("can't get string").unwrap();
assert_eq!(status, Status::Ok);
assert_eq!(val, "HelloWorld");
}
#[fasync::run_singlethreaded]
#[test]
async fn test_int_key() {
let manager = client::connect_to_service::<DeviceSettingsManagerMarker>().unwrap();
let res = manager.set_integer("IntKey", 1234).await.context("can't set int").unwrap();
assert_eq!(res, true);
let (val, status) = manager.get_integer("IntKey").await.context("can't get int").unwrap();
assert_eq!(status, Status::Ok);
assert_eq!(val, 1234);
}
#[fasync::run_singlethreaded]
#[test]
async fn test_watch_key() {
let manager = client::connect_to_service::<DeviceSettingsManagerMarker>().unwrap();
let (watcher_client_end, watcher_server_end) =
fidl::endpoints::create_endpoints::<DeviceSettingsWatcherMarker>().unwrap();
let status =
manager.watch("WatchKey", watcher_client_end).await.context("can't set watch").unwrap();
assert_eq!(status, Status::Ok);
let res = manager.set_integer("WatchKey", 3456).await.context("can't set int").unwrap();
assert_eq!(res, true);
let (val, status) = manager.get_integer("WatchKey").await.context("can't get int").unwrap();
assert_eq!(status, Status::Ok);
assert_eq!(val, 3456);
let mut wait_watch = watcher_server_end
.into_stream()
.context("Can't take request stream")
.unwrap()
.try_filter_map(|req| match req {
DeviceSettingsWatcherRequest::OnChangeSettings { type_: _, control_handle: _ } => {
future::ok(Some(()))
}
});
let () = wait_watch
.try_next()
.await
.expect("failed to watch")
.expect("stream ended unexpectedly");
}
}