sl4f_lib/weave/
facade.rs

1// Copyright 2020 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::common_utils::common::LazyProxy;
6use crate::weave::types::{PairingState, ResetConfig};
7use anyhow::Error;
8use fidl::endpoints::create_proxy;
9use fidl_fuchsia_weave::{
10    ErrorCode, FactoryDataManagerMarker, FactoryDataManagerProxy, PairingStateWatcherMarker,
11    PairingStateWatcherProxy, ResetConfigFlags, StackMarker, StackProxy,
12};
13use serde_json::Value;
14
15/// Perform Weave FIDL operations.
16///
17/// Note this object is shared among all threads created by server.
18#[derive(Debug)]
19pub struct WeaveFacade {
20    factory_data_manager: LazyProxy<FactoryDataManagerMarker>,
21    stack: LazyProxy<StackMarker>,
22}
23
24impl WeaveFacade {
25    pub fn new() -> WeaveFacade {
26        WeaveFacade { factory_data_manager: Default::default(), stack: Default::default() }
27    }
28
29    /// Returns the FactoryDataManager proxy provided on instantiation
30    /// or establishes a new connection.
31    fn factory_data_manager(&self) -> Result<FactoryDataManagerProxy, Error> {
32        self.factory_data_manager.get_or_connect()
33    }
34
35    /// Returns the Stack proxy provided on instantiation or establishes a new connection.
36    fn stack(&self) -> Result<StackProxy, Error> {
37        self.stack.get_or_connect()
38    }
39
40    /// Returns the PairingStateWatcher proxy provided on instantiation.
41    fn pairing_state_watcher(&self) -> Result<PairingStateWatcherProxy, Error> {
42        let (pairing_proxy, pairing_server_end) = create_proxy::<PairingStateWatcherMarker>();
43        self.stack()?.get_pairing_state_watcher(pairing_server_end)?;
44        Ok(pairing_proxy)
45    }
46
47    /// Returns a string mapped from the provided Weave error code.
48    fn map_weave_err(&self, code: ErrorCode) -> anyhow::Error {
49        anyhow!(match code {
50            ErrorCode::FileNotFound => "FileNotFound",
51            ErrorCode::CryptoError => "CryptoError",
52            ErrorCode::InvalidArgument => "InvalidArgument",
53            ErrorCode::InvalidState => "InvalidState",
54            ErrorCode::UnspecifiedError => "UnspecifiedError",
55        })
56    }
57
58    /// Returns the pairing code from the FactoryDataManager proxy service.
59    pub async fn get_pairing_code(&self) -> Result<Vec<u8>, Error> {
60        self.factory_data_manager()?.get_pairing_code().await?.map_err(|e| self.map_weave_err(e))
61    }
62
63    /// Returns the qr code from the StackManager proxy service.
64    pub async fn get_qr_code(&self) -> Result<String, Error> {
65        self.stack()?
66            .get_qr_code()
67            .await?
68            .map(|qr_code| qr_code.data)
69            .map_err(|e| self.map_weave_err(e))
70    }
71
72    /// Returns the pairing state from the PairingStateWatcher service.
73    pub async fn get_pairing_state(&self) -> Result<PairingState, Error> {
74        let watch = self.pairing_state_watcher()?.watch_pairing_state().await;
75        watch.map(|pairing_state| pairing_state.into()).map_err(anyhow::Error::from)
76    }
77
78    /// Resets Weave state by wiping the provided configurations.
79    ///
80    /// # Arguments
81    /// * `args`: The JSON indicating the configurations to reset, in the form of a list of bytes.
82    ///
83    /// # JSON Format
84    /// All fields are optional. Fields set to 'true' reset the corresponding configuration in
85    /// weave, and fields left unset default to 'false'.
86    ///
87    /// {
88    ///   network_config: true,
89    ///   fabric_config: false,
90    ///   service_config: true,
91    ///   operational_credentials: false
92    /// }
93    pub async fn reset_config(&self, args: Value) -> Result<(), Error> {
94        let flags: ResetConfig = serde_json::from_value(args)?;
95        self.stack()?
96            .reset_config(ResetConfigFlags::from(flags))
97            .await?
98            .map_err(|e| self.map_weave_err(e))
99    }
100}
101
102#[cfg(test)]
103mod tests {
104    use super::*;
105    use assert_matches::assert_matches;
106    use fidl::endpoints::create_proxy_and_stream;
107    use fidl_fuchsia_weave::{
108        FactoryDataManagerRequest, PairingStateWatcherRequest, QrCode, StackGetQrCodeResult,
109        StackRequest, StackResetConfigResult,
110    };
111    use fuchsia_async as fasync;
112    use futures::prelude::*;
113    use lazy_static::lazy_static;
114    use serde_json::json;
115
116    const PAIRING_CODE: &'static [u8] = b"ABC1234";
117
118    lazy_static! {
119        static ref QR_CODE: QrCode = QrCode { data: String::from("qrcodedata") };
120        static ref PAIRING_STATE: PairingState = PairingState {
121            is_wlan_provisioned: Some(true),
122            is_fabric_provisioned: Some(true),
123            is_service_provisioned: Some(false),
124            is_weave_fully_provisioned: Some(false),
125            is_thread_provisioned: Some(true)
126        };
127        static ref RESET_CONFIG: Value = json!({
128            "network_config": true,
129            // "fabric_config" unset
130            "service_config": true,
131            // "operational_credentials" unset
132        });
133    }
134
135    struct MockStackBuilder {
136        expected_stack: Vec<Box<dyn FnOnce(StackRequest) + Send + 'static>>,
137        expected_pair: Vec<Box<dyn FnOnce(PairingStateWatcherRequest) + Send + 'static>>,
138    }
139
140    impl MockStackBuilder {
141        fn new() -> Self {
142            Self { expected_stack: vec![], expected_pair: vec![] }
143        }
144
145        fn push_stack(mut self, request: impl FnOnce(StackRequest) + Send + 'static) -> Self {
146            self.expected_stack.push(Box::new(request));
147            self
148        }
149
150        fn push_pair(
151            mut self,
152            request: impl FnOnce(PairingStateWatcherRequest) + Send + 'static,
153        ) -> Self {
154            self.expected_pair.push(Box::new(request));
155            self
156        }
157
158        fn expect_get_qr_code(self, result: StackGetQrCodeResult) -> Self {
159            self.push_stack(move |req| match req {
160                StackRequest::GetQrCode { responder } => {
161                    responder.send(result.as_ref().map_err(|e| *e)).unwrap()
162                }
163                req => panic!("unexpected request: {:?}", req),
164            })
165        }
166
167        fn expect_get_pairing_state(self, result: PairingState) -> Self {
168            self.push_pair(move |req| match req {
169                PairingStateWatcherRequest::WatchPairingState { responder } => {
170                    responder.send(&fidl_fuchsia_weave::PairingState::from(result)).unwrap();
171                }
172            })
173        }
174
175        fn expect_reset_config(
176            self,
177            _expected_flags: fidl_fuchsia_weave::ResetConfigFlags,
178            result: StackResetConfigResult,
179        ) -> Self {
180            self.push_stack(move |req| match req {
181                StackRequest::ResetConfig { responder, flags } => {
182                    assert_matches!(flags, _expected_flags);
183                    responder.send(result).unwrap()
184                }
185                req => panic!("unexpected request: {:?}", req),
186            })
187        }
188
189        fn build_stack(self) -> (WeaveFacade, impl Future<Output = ()>) {
190            let (proxy, mut stream) = create_proxy_and_stream::<StackMarker>();
191            let fut = async move {
192                let _ = &self;
193                for expected in self.expected_stack {
194                    expected(stream.next().await.unwrap().unwrap());
195                }
196                assert_matches!(stream.next().await, None);
197            };
198            let facade = WeaveFacade::new();
199            facade.stack.set(proxy).expect("just-created facade should have empty stack");
200            (facade, fut)
201        }
202        fn build_stack_and_pairing_state_watcher(self) -> (WeaveFacade, impl Future<Output = ()>) {
203            let (proxy, mut stream) = create_proxy_and_stream::<StackMarker>();
204            let stream_fut = async move {
205                let _ = &self;
206                match stream.next().await {
207                    Some(Ok(StackRequest::GetPairingStateWatcher {
208                        watcher,
209                        control_handle: _,
210                    })) => {
211                        let mut into_stream = watcher.into_stream();
212                        for expected in self.expected_pair {
213                            expected(into_stream.next().await.unwrap().unwrap());
214                        }
215                        assert_matches!(into_stream.next().await, None);
216                    }
217                    err => panic!("Error in request handler: {:?}", err),
218                }
219            };
220            let facade = WeaveFacade::new();
221            facade.stack.set(proxy).expect("just-created facade should have empty stack");
222            (facade, stream_fut)
223        }
224    }
225
226    struct MockFactoryDataManagerBuilder {
227        expected: Vec<Box<dyn FnOnce(FactoryDataManagerRequest) + Send + 'static>>,
228    }
229
230    impl MockFactoryDataManagerBuilder {
231        fn new() -> Self {
232            Self { expected: vec![] }
233        }
234
235        fn push(
236            mut self,
237            request: impl FnOnce(FactoryDataManagerRequest) + Send + 'static,
238        ) -> Self {
239            self.expected.push(Box::new(request));
240            self
241        }
242
243        fn expect_get_pairing_code(self, result: Result<&'static [u8], ErrorCode>) -> Self {
244            self.push(move |req| match req {
245                FactoryDataManagerRequest::GetPairingCode { responder } => {
246                    responder.send(result).unwrap()
247                }
248                _ => {}
249            })
250        }
251
252        fn build(self) -> (WeaveFacade, impl Future<Output = ()>) {
253            let (proxy, mut stream) =
254                fidl::endpoints::create_proxy_and_stream::<FactoryDataManagerMarker>();
255            let fut = async move {
256                for expected in self.expected {
257                    expected(stream.next().await.unwrap().unwrap());
258                }
259                assert_matches!(stream.next().await, None);
260            };
261            let facade = WeaveFacade::new();
262            facade
263                .factory_data_manager
264                .set(proxy)
265                .expect("just-created facade should have empty fdm");
266            (facade, fut)
267        }
268    }
269
270    #[fasync::run_singlethreaded(test)]
271    async fn test_get_pairing_code() {
272        let (facade, pairing_code_fut) =
273            MockFactoryDataManagerBuilder::new().expect_get_pairing_code(Ok(PAIRING_CODE)).build();
274
275        let facade_fut = async move {
276            assert_eq!(facade.get_pairing_code().await.unwrap(), PAIRING_CODE);
277        };
278
279        future::join(facade_fut, pairing_code_fut).await;
280    }
281
282    #[fasync::run_singlethreaded(test)]
283    async fn test_get_qr_code() {
284        let qr_clone: StackGetQrCodeResult = Ok((*QR_CODE).clone());
285        let (facade, qr_code_fut) =
286            MockStackBuilder::new().expect_get_qr_code(qr_clone).build_stack();
287
288        let facade_fut = async move {
289            assert_eq!(facade.get_qr_code().await.unwrap(), *QR_CODE.data);
290        };
291
292        future::join(facade_fut, qr_code_fut).await;
293    }
294
295    #[fasync::run_singlethreaded(test)]
296    async fn test_get_pairing_state() {
297        let (facade, pairing_state_fut) = MockStackBuilder::new()
298            .expect_get_pairing_state(*PAIRING_STATE)
299            .build_stack_and_pairing_state_watcher();
300
301        let facade_fut = async move {
302            let pairing_state = facade.get_pairing_state().await.unwrap();
303            assert_eq!(pairing_state, *PAIRING_STATE);
304        };
305
306        future::join(facade_fut, pairing_state_fut).await;
307    }
308
309    #[allow(clippy::unit_cmp)] // TODO(https://fxbug.dev/42176996)
310    #[fasync::run_singlethreaded(test)]
311    async fn test_reset_config() {
312        let flags: ResetConfigFlags = fidl_fuchsia_weave::ResetConfigFlags::NETWORK_CONFIG
313            | fidl_fuchsia_weave::ResetConfigFlags::SERVICE_CONFIG;
314        let (facade, reset_config_fut) =
315            MockStackBuilder::new().expect_reset_config(flags, Ok(())).build_stack();
316
317        let facade_fut =
318            async move { assert_eq!(facade.reset_config(RESET_CONFIG.clone()).await.unwrap(), ()) };
319
320        future::join(facade_fut, reset_config_fut).await;
321    }
322}