blob: d7137214ced26d6fe691cf9c3955ecfc206bf089 [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::get_proxy_or_connect;
use crate::weave::types::PairingState;
use anyhow::Error;
use fidl::endpoints::create_proxy;
use fidl_fuchsia_weave::{
ErrorCode, FactoryDataManagerMarker, FactoryDataManagerProxy, PairingStateWatcherMarker,
PairingStateWatcherProxy, StackMarker, StackProxy,
};
use parking_lot::RwLock;
use std::convert::From;
/// Perform Weave FIDL operations.
///
/// Note this object is shared among all threads created by server.
#[derive(Debug)]
pub struct WeaveFacade {
factory_data_manager: RwLock<Option<FactoryDataManagerProxy>>,
stack: RwLock<Option<StackProxy>>,
}
impl WeaveFacade {
pub fn new() -> WeaveFacade {
WeaveFacade { factory_data_manager: RwLock::new(None), stack: RwLock::new(None) }
}
/// Returns the FactoryDataManager proxy provided on instantiation
/// or establishes a new connection.
fn factory_data_manager(&self) -> Result<FactoryDataManagerProxy, Error> {
get_proxy_or_connect::<FactoryDataManagerMarker>(&self.factory_data_manager)
}
/// Returns the Stack proxy provided on instantiation or establishes a new connection.
fn stack(&self) -> Result<StackProxy, Error> {
get_proxy_or_connect::<StackMarker>(&self.stack)
}
/// Returns the PairingStateWatcher proxy provided on instantiation.
fn pairing_state_watcher(&self) -> Result<PairingStateWatcherProxy, Error> {
let (pairing_proxy, pairing_server_end) = create_proxy::<PairingStateWatcherMarker>()?;
self.stack()?.get_pairing_state_watcher(pairing_server_end)?;
Ok(pairing_proxy)
}
/// Returns a string mapped from the provided Weave error code.
fn map_weave_err(&self, code: ErrorCode) -> anyhow::Error {
anyhow!(match code {
ErrorCode::FileNotFound => "FileNotFound",
ErrorCode::CryptoError => "CryptoError",
ErrorCode::InvalidArgument => "InvalidArgument",
ErrorCode::InvalidState => "InvalidState",
ErrorCode::UnspecifiedError => "UnspecifiedError",
})
}
/// Returns the pairing code from the FactoryDataManager proxy service.
pub async fn get_pairing_code(&self) -> Result<Vec<u8>, Error> {
self.factory_data_manager()?.get_pairing_code().await?.map_err(|e| self.map_weave_err(e))
}
/// Returns the qr code from the StackManager proxy service.
pub async fn get_qr_code(&self) -> Result<String, Error> {
self.stack()?
.get_qr_code()
.await?
.map(|qr_code| qr_code.data)
.map_err(|e| self.map_weave_err(e))
}
/// Returns the pairing state from the PairingStateWatcher service.
pub async fn get_pairing_state(&self) -> Result<PairingState, Error> {
let watch = self.pairing_state_watcher()?.watch_pairing_state().await;
watch.map(|pairing_state| pairing_state.into()).map_err(anyhow::Error::from)
}
}
#[cfg(test)]
mod tests {
use super::*;
use fidl::endpoints::create_proxy_and_stream;
use fidl_fuchsia_weave::{
FactoryDataManagerGetPairingCodeResult, FactoryDataManagerRequest,
PairingStateWatcherRequest, QrCode, StackGetQrCodeResult, StackRequest,
};
use fuchsia_async as fasync;
use futures::prelude::*;
use lazy_static::lazy_static;
use matches::assert_matches;
lazy_static! {
static ref PAIRING_CODE: Vec<u8> = b"ABC1234".to_vec();
static ref QR_CODE: QrCode = QrCode { data: String::from("qrcodedata") };
static ref PAIRING_STATE: PairingState = PairingState {
is_wlan_provisioned: Some(true),
is_fabric_provisioned: Some(true),
is_service_provisioned: Some(false),
is_weave_fully_provisioned: Some(false),
is_thread_provisioned: Some(true)
};
}
struct MockStackBuilder {
expected_stack: Vec<Box<dyn FnOnce(StackRequest) + Send + 'static>>,
expected_pair: Vec<Box<dyn FnOnce(PairingStateWatcherRequest) + Send + 'static>>,
}
impl MockStackBuilder {
fn new() -> Self {
Self { expected_stack: vec![], expected_pair: vec![] }
}
fn push_stack(mut self, request: impl FnOnce(StackRequest) + Send + 'static) -> Self {
self.expected_stack.push(Box::new(request));
self
}
fn push_pair(
mut self,
request: impl FnOnce(PairingStateWatcherRequest) + Send + 'static,
) -> Self {
self.expected_pair.push(Box::new(request));
self
}
fn expect_get_qr_code(self, mut result: StackGetQrCodeResult) -> Self {
self.push_stack(move |req| match req {
StackRequest::GetQrCode { responder } => responder.send(&mut result).unwrap(),
req => panic!("unexpected request: {:?}", req),
})
}
fn expect_get_pairing_state(self, result: PairingState) -> Self {
self.push_pair(move |req| match req {
PairingStateWatcherRequest::WatchPairingState { responder } => {
responder.send(fidl_fuchsia_weave::PairingState::from(result)).unwrap();
}
})
}
fn build_stack(self) -> (WeaveFacade, impl Future<Output = ()>) {
let (proxy, mut stream) = create_proxy_and_stream::<StackMarker>().unwrap();
let fut = async move {
for expected in self.expected_stack {
expected(stream.next().await.unwrap().unwrap());
}
assert_matches!(stream.next().await, None);
};
(
WeaveFacade {
stack: RwLock::new(Some(proxy)),
factory_data_manager: RwLock::new(None),
},
fut,
)
}
fn build_stack_and_pairing_state_watcher(self) -> (WeaveFacade, impl Future<Output = ()>) {
let (proxy, mut stream) = create_proxy_and_stream::<StackMarker>().unwrap();
let stream_fut = async move {
match stream.next().await {
Some(Ok(StackRequest::GetPairingStateWatcher {
watcher,
control_handle: _,
})) => {
let mut into_stream = watcher.into_stream().unwrap();
for expected in self.expected_pair {
expected(into_stream.next().await.unwrap().unwrap());
}
assert_matches!(into_stream.next().await, None);
}
err => panic!("Error in request handler: {:?}", err),
}
};
(
WeaveFacade {
stack: RwLock::new(Some(proxy)),
factory_data_manager: RwLock::new(None),
},
stream_fut,
)
}
}
struct MockFactoryDataManagerBuilder {
expected: Vec<Box<dyn FnOnce(FactoryDataManagerRequest) + Send + 'static>>,
}
impl MockFactoryDataManagerBuilder {
fn new() -> Self {
Self { expected: vec![] }
}
fn push(
mut self,
request: impl FnOnce(FactoryDataManagerRequest) + Send + 'static,
) -> Self {
self.expected.push(Box::new(request));
self
}
fn expect_get_pairing_code(
self,
mut result: FactoryDataManagerGetPairingCodeResult,
) -> Self {
self.push(move |req| match req {
FactoryDataManagerRequest::GetPairingCode { responder } => {
responder.send(&mut result).unwrap()
}
_ => {}
})
}
fn build(self) -> (WeaveFacade, impl Future<Output = ()>) {
let (proxy, mut stream) =
fidl::endpoints::create_proxy_and_stream::<FactoryDataManagerMarker>().unwrap();
let fut = async move {
for expected in self.expected {
expected(stream.next().await.unwrap().unwrap());
}
assert_matches!(stream.next().await, None);
};
(
WeaveFacade {
stack: RwLock::new(None),
factory_data_manager: RwLock::new(Some(proxy)),
},
fut,
)
}
}
#[fasync::run_singlethreaded(test)]
async fn test_get_pairing_code() {
let (facade, pairing_code_fut) = MockFactoryDataManagerBuilder::new()
.expect_get_pairing_code(Ok(PAIRING_CODE.to_vec()))
.build();
let facade_fut = async move {
assert_eq!(facade.get_pairing_code().await.unwrap(), *PAIRING_CODE);
};
future::join(facade_fut, pairing_code_fut).await;
}
#[fasync::run_singlethreaded(test)]
async fn test_get_qr_code() {
let qr_clone: StackGetQrCodeResult = Ok((*QR_CODE).clone());
let (facade, qr_code_fut) =
MockStackBuilder::new().expect_get_qr_code(qr_clone).build_stack();
let facade_fut = async move {
assert_eq!(facade.get_qr_code().await.unwrap(), *QR_CODE.data);
};
future::join(facade_fut, qr_code_fut).await;
}
#[fasync::run_singlethreaded(test)]
async fn test_get_pairing_state() {
let (facade, pairing_state_fut) = MockStackBuilder::new()
.expect_get_pairing_state(*PAIRING_STATE)
.build_stack_and_pairing_state_watcher();
let facade_fut = async move {
let pairing_state = facade.get_pairing_state().await.unwrap();
assert_eq!(pairing_state, *PAIRING_STATE);
};
future::join(facade_fut, pairing_state_fut).await;
}
}