Skip to main content

starnix_modules_nmfs/
manager.rs

1// Copyright 2024 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::{NetworkMessage, fuchsia_network_monitor_fs};
6use bstr::BString;
7use fuchsia_component::client::connect_to_protocol_sync;
8use fuchsia_inspect_derive::{IValue, Inspect, Unit, WithInspect};
9use starnix_core::task::Kernel;
10use starnix_core::vfs::fs_registry::FsRegistry;
11use starnix_logging::{log_error, log_info};
12use starnix_sync::{LockDepGuard, LockDepMutex, NmfsNetworkManagerLock};
13use starnix_uapi::error;
14use starnix_uapi::errors::Errno;
15use starnix_uapi::fs_type::FileSystemTypeFlags;
16use std::collections::HashMap;
17use std::collections::hash_map::Entry;
18use thiserror::Error;
19
20use fidl_fuchsia_net_policy_properties as fnp_properties;
21
22/// Manager for communicating network properties.
23#[derive(Inspect)]
24pub(crate) struct NetworkManager {
25    network_registry: fnp_properties::NetworkRegistrySynchronousProxy,
26    #[inspect(forward)]
27    inner: LockDepMutex<IValue<NetworkManagerInner>, NmfsNetworkManagerLock>,
28}
29
30#[derive(Unit, Default)]
31struct NetworkManagerInner {
32    // Keeps track of networks and their [`NetworkMessage`].
33    #[inspect(skip)]
34    default_id: Option<u32>,
35    #[inspect(skip)]
36    networks: HashMap<u32, Option<NetworkMessage>>,
37
38    default_ids_set: SeenSentData,
39    added_networks: SeenSentData,
40    updated_networks: SeenSentData,
41    removed_networks: SeenSentData,
42}
43
44#[derive(Unit, Default)]
45struct SeenSentData {
46    // The number of event occurrences witnessed
47    // by the NetworkManager.
48    seen: u64,
49    // The number of event occurrences that have been
50    // sent successfully to netcfg.
51    sent: u64,
52}
53
54/// Initialize the connection to the NetworkRegistry protocol.
55pub fn nmfs_init(kernel: &Kernel) -> Result<(), anyhow::Error> {
56    // Register the fuchsia_network_monitor_fs in the FsRegistry.
57    let registry = kernel.expando.get::<FsRegistry>();
58    registry.register(
59        b"fuchsia_network_monitor_fs".into(),
60        FileSystemTypeFlags::empty(),
61        fuchsia_network_monitor_fs,
62    );
63
64    // Register the NetworkManager.
65    let network_registry = connect_to_protocol_sync::<fnp_properties::NetworkRegistryMarker>()?;
66    kernel
67        .expando
68        .get_or_init(|| NetworkManager::new_with_proxy(network_registry, &kernel.inspect_node));
69    Ok(())
70}
71
72// The functions that propagate calls to netcfg prioritize maintaining
73// a correct version of local state and logging an error if the netcfg
74// state is not aligned.
75impl NetworkManager {
76    // Create a NetworkManager with a NetworkRegistry protocol connection and `nmfs` inspect node.
77    pub(crate) fn new_with_proxy(
78        proxy: fnp_properties::NetworkRegistrySynchronousProxy,
79        node: &fuchsia_inspect::Node,
80    ) -> Self {
81        Self { network_registry: proxy, inner: Default::default() }
82            .with_inspect(node, "nmfs")
83            .expect("Failed to attach 'nmfs' node")
84    }
85
86    // Locks and returns the inner state of the manager.
87    fn lock(&self) -> LockDepGuard<'_, IValue<NetworkManagerInner>> {
88        self.inner.lock()
89    }
90
91    pub(crate) fn get_default_network_id(&self) -> Option<u32> {
92        self.lock().default_id
93    }
94
95    pub(crate) fn get_network(&self, network_id: &u32) -> Option<Option<NetworkMessage>> {
96        self.lock().networks.get(network_id).cloned()
97    }
98
99    pub(crate) fn get_default_id_as_bytes(&self) -> BString {
100        let default_id = match self.get_default_network_id() {
101            Some(id) => id.to_string(),
102            None => "".to_string(),
103        };
104        default_id.into_bytes().into()
105    }
106
107    pub(crate) fn get_network_by_id_as_bytes(&self, network_id: u32) -> BString {
108        let network_info = match self.get_network(&network_id) {
109            Some(Some(network)) => {
110                serde_json::to_string(&network).unwrap_or_else(|_| "{}".to_string())
111            }
112            // A network with that was created but hasn't yet
113            // been populated with network properties.
114            Some(None) | None => "{}".to_string(),
115        };
116        network_info.into_bytes().into()
117    }
118
119    // Set the default network identifier. Propagate the change
120    // to netcfg.
121    pub(crate) fn set_default_network_id(&self, network_id: Option<u32>) {
122        {
123            let mut inner_guard = self.lock();
124            let mut inner = inner_guard.as_mut();
125            inner.default_id = network_id;
126            inner.default_ids_set.seen += 1;
127        }
128
129        // Only log when there is an internal proxy error.
130        match self.fidl_set_default_network_id(network_id) {
131            Ok(()) => {
132                log_info!("Successfully set network with id {network_id:?} as default in netcfg",);
133                let mut inner_guard = self.lock();
134                inner_guard.as_mut().default_ids_set.sent += 1;
135            }
136            Err(e) => {
137                log_error!(
138                    "Failed to set network with id {network_id:?} as default in netcfg; {e:?}"
139                );
140            }
141        }
142    }
143
144    // Populate a new element in the Map. This does not
145    // propagate to netcfg.
146    //
147    // An error will be returned if a network with the id
148    // exists in the local state.
149    pub(crate) fn add_empty_network(&self, network_id: u32) -> Result<(), Errno> {
150        let mut inner_guard = self.lock();
151        match inner_guard.as_mut().networks.entry(network_id) {
152            Entry::Occupied(_) => {
153                log_error!(
154                    "Failed to add empty network to HashMap, was present for id: {}",
155                    network_id
156                );
157                return error!(EEXIST);
158            }
159            Entry::Vacant(entry) => entry.insert(None),
160        };
161        Ok(())
162    }
163
164    // Add a new network. Propagate the change to netcfg.
165    //
166    // An error will be returned if a network with the id
167    // exists in the local state.
168    pub(crate) fn add_network(&self, network: NetworkMessage) -> Result<(), Errno> {
169        {
170            let mut inner_guard = self.lock();
171            let mut inner = inner_guard.as_mut();
172            match inner.networks.entry(network.netid) {
173                Entry::Occupied(mut entry) => {
174                    // This is deliberately before any Map manipulation because we
175                    // should not modify the Map state if we return an error.
176                    if let Some(network) = entry.get() {
177                        log_error!(
178                            "Failed to add network with id {} to HashMap, already existed",
179                            network.netid
180                        );
181                        return error!(EEXIST);
182                    }
183                    let _ = entry.insert(Some(network.clone()));
184                }
185                Entry::Vacant(entry) => {
186                    let _ = entry.insert(Some(network.clone()));
187                }
188            }
189            inner.added_networks.seen += 1;
190        }
191
192        // Only log when there is an internal proxy error.
193        match self.fidl_add_network(&fnp_properties::Network::from(&network)) {
194            Ok(()) => {
195                log_info!("Successfully added network with id {} to netcfg", network.netid);
196                let mut inner_guard = self.lock();
197                inner_guard.as_mut().added_networks.sent += 1;
198            }
199            Err(e) => {
200                log_error!("Failed to add network with id {:?} to netcfg; {e:?}", network.netid);
201            }
202        }
203
204        Ok(())
205    }
206
207    // Update an existing network. Propagate the change to netcfg.
208    //
209    // An error will be returned if a network with the id does not
210    // exist in the local state.
211    pub(crate) fn update_network(&self, network: NetworkMessage) -> Result<(), Errno> {
212        {
213            let mut inner_guard = self.lock();
214            let mut inner = inner_guard.as_mut();
215            // Ensure that there is a network already present at that netid
216            // prior to modifying the map.
217            let _old_network = match inner.networks.entry(network.netid) {
218                Entry::Occupied(mut entry) => {
219                    if let None = entry.get() {
220                        return error!(ENOENT);
221                    }
222                    entry.insert(Some(network.clone()))
223                }
224                Entry::Vacant(_) => {
225                    return error!(ENOENT);
226                }
227            };
228            inner.updated_networks.seen += 1;
229        };
230
231        // Only log when there is an internal proxy error.
232        match self.fidl_update_network(&fnp_properties::Network::from(&network)) {
233            Ok(()) => {
234                log_info!("Successfully updated network with id {} in netcfg", network.netid);
235                let mut inner_guard = self.lock();
236                inner_guard.as_mut().updated_networks.sent += 1;
237            }
238            Err(e) => {
239                log_error!("Failed to update network with id {} in netcfg; {e:?}", network.netid);
240            }
241        }
242
243        Ok(())
244    }
245
246    // Remove an existing network. Propagate the change to netcfg.
247    //
248    // An error will be returned if a network with the id does not
249    // exist in the local state.
250    pub(crate) fn remove_network(&self, network_id: u32) -> Result<(), Errno> {
251        // Surface an error if the network is the current default
252        // network or if the network is not found.
253        let default_network_id = self.get_default_network_id();
254        if let Some(id) = default_network_id {
255            if id == network_id {
256                return error!(EPERM);
257            }
258        }
259        {
260            let mut inner_guard = self.lock();
261            let mut inner = inner_guard.as_mut();
262            if let None = inner.networks.remove(&network_id) {
263                return error!(ENOENT);
264            }
265            inner.removed_networks.seen += 1;
266        }
267
268        // Only log when there is an internal proxy error.
269        match self.fidl_remove_network(&network_id) {
270            Ok(()) => {
271                log_info!("Successfully removed network with id {network_id} from netcfg",);
272                let mut inner_guard = self.lock();
273                inner_guard.as_mut().removed_networks.sent += 1;
274            }
275            Err(e) => {
276                log_error!("Failed to remove network with id {network_id} in netcfg; {e:?}");
277            }
278        }
279        Ok(())
280    }
281
282    // Call `set_default` on `NetworkRegistry`.
283    fn fidl_set_default_network_id(
284        &self,
285        network_id: Option<u32>,
286    ) -> Result<(), NetworkManagerError> {
287        let network_id = match network_id {
288            Some(id) => fidl_fuchsia_posix_socket::OptionalUint32::Value(id),
289            None => {
290                fidl_fuchsia_posix_socket::OptionalUint32::Unset(fidl_fuchsia_posix_socket::Empty)
291            }
292        };
293        Ok(self.network_registry.set_default(&network_id, zx::MonotonicInstant::INFINITE)??)
294    }
295
296    // Call `add` on `NetworkRegistry`.
297    fn fidl_add_network(
298        &self,
299        network: &fnp_properties::Network,
300    ) -> Result<(), NetworkManagerError> {
301        Ok(self.network_registry.add(&network, zx::MonotonicInstant::INFINITE)??)
302    }
303
304    // Call `update` on `NetworkRegistry`.
305    fn fidl_update_network(
306        &self,
307        network: &fnp_properties::Network,
308    ) -> Result<(), NetworkManagerError> {
309        Ok(self.network_registry.update(&network, zx::MonotonicInstant::INFINITE)??)
310    }
311
312    // Call `remove` on `NetworkRegistry`.
313    fn fidl_remove_network(&self, network_id: &u32) -> Result<(), NetworkManagerError> {
314        Ok(self.network_registry.remove(*network_id, zx::MonotonicInstant::INFINITE)??)
315    }
316}
317
318// Errors produced when communicating updates to
319// netcfg.
320#[derive(Clone, Debug, Error)]
321pub(crate) enum NetworkManagerError {
322    #[error("Error during netcfg Add: {0:?}")]
323    Add(fnp_properties::NetworkRegistryAddError),
324    #[error("Error calling FIDL on netcfg: {0:?}")]
325    Fidl(#[from] fidl::Error),
326    #[error("Error during netcfg Remove: {0:?}")]
327    Remove(fnp_properties::NetworkRegistryRemoveError),
328    #[error("Error during netcfg SetDefault: {0:?}")]
329    SetDefault(fnp_properties::NetworkRegistrySetDefaultError),
330    #[error("Error during netcfg Update: {0:?}")]
331    Update(fnp_properties::NetworkRegistryUpdateError),
332}
333
334impl From<fnp_properties::NetworkRegistryAddError> for NetworkManagerError {
335    fn from(error: fnp_properties::NetworkRegistryAddError) -> Self {
336        NetworkManagerError::Add(error)
337    }
338}
339
340impl From<fnp_properties::NetworkRegistryRemoveError> for NetworkManagerError {
341    fn from(error: fnp_properties::NetworkRegistryRemoveError) -> Self {
342        NetworkManagerError::Remove(error)
343    }
344}
345
346impl From<fnp_properties::NetworkRegistrySetDefaultError> for NetworkManagerError {
347    fn from(error: fnp_properties::NetworkRegistrySetDefaultError) -> Self {
348        NetworkManagerError::SetDefault(error)
349    }
350}
351
352impl From<fnp_properties::NetworkRegistryUpdateError> for NetworkManagerError {
353    fn from(error: fnp_properties::NetworkRegistryUpdateError) -> Self {
354        NetworkManagerError::Update(error)
355    }
356}
357
358#[cfg(test)]
359mod tests {
360    use super::*;
361
362    use assert_matches::assert_matches;
363    use diagnostics_assertions::assert_data_tree;
364    use futures::StreamExt as _;
365    use starnix_uapi::{EEXIST, ENOENT};
366    use test_case::test_case;
367
368    fn test_network_message_from_id(netid: u32) -> NetworkMessage {
369        NetworkMessage { netid, ..Default::default() }
370    }
371
372    #[::fuchsia::test]
373    async fn test_add_empty_network() {
374        let inspector = fuchsia_inspect::Inspector::default();
375        let manager = &setup_proxy(inspector.root(), vec![]);
376
377        let network_id = 1;
378        assert_matches!(manager.add_empty_network(network_id), Ok(()));
379
380        // Ensure we cannot add an empty network with the same id twice.
381        assert_matches!(
382            manager.add_empty_network(network_id),
383            Err(errno) if errno.code.error_code() == EEXIST
384        );
385        assert_matches!(manager.get_network(&network_id), Some(None));
386        // Empty networks don't get sent to netcfg, so they are
387        // ignored in SeenSentData.
388        assert_data_tree!(inspector, root: {
389            nmfs: contains {
390                added_networks: {
391                    seen: 0u64,
392                    sent: 0u64,
393                },
394            },
395        });
396    }
397
398    // Set the `NetworkRegistryMarker` in the NetworkManager and mock out
399    // the responses to `NetworkRegistryRequest`s with provided results.
400    fn setup_proxy(
401        inspect_node: &fuchsia_inspect::Node,
402        results: Vec<Result<(), NetworkManagerError>>,
403    ) -> NetworkManager {
404        let (proxy, mut stream) = fidl::endpoints::create_sync_proxy_and_stream::<
405            fnp_properties::NetworkRegistryMarker,
406        >();
407        let manager = NetworkManager::new_with_proxy(proxy, inspect_node);
408
409        let mut results = results.into_iter();
410        fuchsia_async::Task::spawn(async move {
411            while let Some(item) = stream.next().await {
412                let result = results
413                    .next()
414                    .expect("there should be an equivalent # of results and requests");
415                match item.expect("receive request") {
416                    fnp_properties::NetworkRegistryRequest::SetDefault {
417                        network_id: _,
418                        responder,
419                    } => {
420                        let res = result.map_err(|e| match e {
421                            NetworkManagerError::SetDefault(err) => err,
422                            _ => unreachable!("should have been SetDefault error variant"),
423                        });
424                        responder.send(res).expect("respond to SetDefault");
425                    }
426                    fnp_properties::NetworkRegistryRequest::Add { network: _, responder } => {
427                        let res = result.map_err(|e| match e {
428                            NetworkManagerError::Add(err) => err,
429                            _ => unreachable!("should have been Add error variant"),
430                        });
431                        responder.send(res).expect("respond to Add");
432                    }
433                    fnp_properties::NetworkRegistryRequest::Update { network: _, responder } => {
434                        let res = result.map_err(|e| match e {
435                            NetworkManagerError::Update(err) => err,
436                            _ => unreachable!("should have been Update error variant"),
437                        });
438                        responder.send(res).expect("respond to Update");
439                    }
440                    fnp_properties::NetworkRegistryRequest::Remove { network_id: _, responder } => {
441                        let res = result.map_err(|e| match e {
442                            NetworkManagerError::Remove(err) => err,
443                            _ => unreachable!("should have been Remove error variant"),
444                        });
445                        responder.send(res).expect("respond to Remove");
446                    }
447                }
448            }
449        })
450        .detach();
451        manager
452    }
453
454    // Mock of private `manager::SeenSentData` to use for
455    // improved test case readability.
456    struct SeenSentData {
457        seen: u64,
458        sent: u64,
459    }
460
461    #[test_case(vec![Ok(()), Ok(())], SeenSentData { seen: 2, sent: 2 }; "all_success")]
462    #[test_case(
463        vec![
464            Ok(()),
465            Err(NetworkManagerError::SetDefault(fnp_properties::NetworkRegistrySetDefaultError::NotFound)),
466        ],
467        SeenSentData { seen: 2, sent: 1 };
468        "one_error"
469    )]
470    #[test_case(
471        vec![
472            Err(NetworkManagerError::SetDefault(fnp_properties::NetworkRegistrySetDefaultError::NotFound)),
473            Err(NetworkManagerError::SetDefault(fnp_properties::NetworkRegistrySetDefaultError::NotFound)),
474        ],
475        SeenSentData { seen: 2, sent: 0 };
476        "all_errors"
477    )]
478    #[::fuchsia::test(threads = 2)]
479    async fn test_set_default_network_id_with_proxy(
480        results: Vec<Result<(), NetworkManagerError>>,
481        expected_data: SeenSentData,
482    ) {
483        let inspector = fuchsia_inspect::Inspector::default();
484        let manager = &setup_proxy(inspector.root(), results);
485
486        manager.set_default_network_id(Some(1));
487        assert_eq!(manager.get_default_network_id(), Some(1));
488
489        manager.set_default_network_id(Some(2));
490        assert_eq!(manager.get_default_network_id(), Some(2));
491
492        assert_data_tree!(inspector, root: {
493            nmfs: contains {
494                default_ids_set: {
495                    seen: expected_data.seen,
496                    sent: expected_data.sent,
497                },
498                added_networks: {
499                    seen: 0u64,
500                    sent: 0u64,
501                },
502                updated_networks: {
503                    seen: 0u64,
504                    sent: 0u64,
505                },
506                removed_networks: {
507                    seen: 0u64,
508                    sent: 0u64,
509                },
510            },
511        });
512    }
513
514    #[test_case(vec![Ok(()), Ok(())], SeenSentData { seen: 2, sent: 2 }; "all_success")]
515    #[test_case(
516        vec![
517            Ok(()),
518            Err(NetworkManagerError::Add(fnp_properties::NetworkRegistryAddError::DuplicateNetworkId)),
519        ],
520        SeenSentData { seen: 2, sent: 1 };
521        "one_error"
522    )]
523    #[test_case(
524        vec![
525            Err(NetworkManagerError::Add(fnp_properties::NetworkRegistryAddError::MissingNetworkId)),
526            Err(NetworkManagerError::Add(fnp_properties::NetworkRegistryAddError::MissingNetworkInfo)),
527        ],
528        SeenSentData { seen: 2, sent: 0 };
529        "all_errors"
530    )]
531    #[::fuchsia::test(threads = 2)]
532    async fn test_add_network_with_proxy(
533        results: Vec<Result<(), NetworkManagerError>>,
534        expected_data: SeenSentData,
535    ) {
536        let inspector = fuchsia_inspect::Inspector::default();
537        let manager = &setup_proxy(inspector.root(), results);
538
539        // `add_network` returns Ok(()) as long as the network
540        // addition is applied locally, regardless of if the call
541        // is sent to netcfg successfully.
542        let network1 = test_network_message_from_id(1);
543        assert_matches!(manager.add_network(network1.clone()), Ok(()));
544
545        let network2 = test_network_message_from_id(2);
546        assert_matches!(manager.add_network(network2.clone()), Ok(()));
547
548        // Ensure we cannot add a network with the same id twice. This is
549        // observed fully within the NetworkManager and not netcfg.
550        assert_matches!(
551            manager.add_network(network2.clone()),
552            Err(errno) if errno.code.error_code() == EEXIST
553        );
554
555        assert_data_tree!(inspector, root: {
556            nmfs: contains {
557                default_ids_set: {
558                    seen: 0u64,
559                    sent: 0u64,
560                },
561                added_networks: {
562                    seen: expected_data.seen,
563                    sent: expected_data.sent,
564                },
565                updated_networks: {
566                    seen: 0u64,
567                    sent: 0u64,
568                },
569                removed_networks: {
570                    seen: 0u64,
571                    sent: 0u64,
572                },
573            },
574        });
575    }
576
577    #[test_case(vec![Ok(()), Ok(())], SeenSentData { seen: 2, sent: 2 }; "all_success")]
578    #[test_case(
579        vec![
580            Ok(()),
581            Err(NetworkManagerError::Update(fnp_properties::NetworkRegistryUpdateError::MissingNetworkId)),
582        ],
583        SeenSentData { seen: 2, sent: 1 };
584        "one_error"
585    )]
586    #[test_case(
587        vec![
588            Err(NetworkManagerError::Update(fnp_properties::NetworkRegistryUpdateError::NotFound)),
589            Err(NetworkManagerError::Update(fnp_properties::NetworkRegistryUpdateError::MissingNetworkInfo)),
590        ],
591        SeenSentData { seen: 2, sent: 0 };
592        "all_errors"
593    )]
594    #[::fuchsia::test(threads = 2)]
595    async fn test_update_network_with_proxy(
596        results: Vec<Result<(), NetworkManagerError>>,
597        expected_data: SeenSentData,
598    ) {
599        let inspector = fuchsia_inspect::Inspector::default();
600        let manager = &setup_proxy(inspector.root(), results);
601
602        let network_id = 1;
603        let network = test_network_message_from_id(network_id);
604
605        // Ensure we cannot update a network that doesn't exist.
606        assert_matches!(
607            manager.update_network(network.clone()),
608            Err(errno) if errno.code.error_code() == ENOENT
609        );
610
611        // Insert the network manually and then update the network.
612        {
613            let mut inner_guard = manager.lock();
614            let _ = inner_guard.as_mut().networks.insert(network_id, Some(network.clone()));
615        }
616
617        // `update_network` returns Ok(()) as long as the network
618        // update is applied locally, regardless of if the call
619        // is sent to netcfg successfully. Use the same
620        // network information -- another test verifies that the
621        // change is applied successfully.
622        assert_matches!(manager.update_network(network.clone()), Ok(()));
623        assert_matches!(manager.update_network(network), Ok(()));
624
625        assert_data_tree!(inspector, root: {
626            nmfs: contains {
627                default_ids_set: {
628                    seen: 0u64,
629                    sent: 0u64,
630                },
631                added_networks: {
632                    seen: 0u64,
633                    sent: 0u64,
634                },
635                updated_networks: {
636                    seen: expected_data.seen,
637                    sent: expected_data.sent,
638                },
639                removed_networks: {
640                    seen: 0u64,
641                    sent: 0u64,
642                },
643            },
644        });
645    }
646
647    #[test_case(vec![Ok(())], SeenSentData { seen: 1, sent: 1 }; "success")]
648    #[test_case(
649        vec![
650            Err(NetworkManagerError::Remove(fnp_properties::NetworkRegistryRemoveError::NotFound)),
651        ],
652        SeenSentData { seen: 1, sent: 0 };
653        "error"
654    )]
655    #[::fuchsia::test(threads = 2)]
656    async fn test_remove_network_with_proxy(
657        results: Vec<Result<(), NetworkManagerError>>,
658        expected_data: SeenSentData,
659    ) {
660        let inspector = fuchsia_inspect::Inspector::default();
661        let manager = &setup_proxy(inspector.root(), results);
662
663        let network_id = 1;
664        let network = test_network_message_from_id(network_id);
665
666        // Ensure we cannot remove a network that doesn't exist.
667        assert_matches!(
668            manager.remove_network(network_id),
669            Err(errno) if errno.code.error_code() == ENOENT
670        );
671
672        // Insert the network manually and then remove it.
673        {
674            let mut inner_guard = manager.lock();
675            let _ = inner_guard.as_mut().networks.insert(network_id, Some(network.clone()));
676        }
677
678        // `remove_network` returns Ok(()) as long as the network
679        // removal is applied locally, regardless of if the call
680        // is sent to netcfg successfully.
681        assert_matches!(manager.remove_network(network_id), Ok(()));
682
683        assert_data_tree!(inspector, root: {
684            nmfs: contains {
685                default_ids_set: {
686                    seen: 0u64,
687                    sent: 0u64,
688                },
689                added_networks: {
690                    seen: 0u64,
691                    sent: 0u64,
692                },
693                updated_networks: {
694                    seen: 0u64,
695                    sent: 0u64,
696                },
697                removed_networks: {
698                    seen: expected_data.seen,
699                    sent: expected_data.sent,
700                },
701            },
702        });
703    }
704
705    #[::fuchsia::test(threads = 2)]
706    async fn test_multiple_operations_with_proxy() {
707        let inspector = fuchsia_inspect::Inspector::default();
708        let results = vec![
709            // Network added to Manager, but not added to netcfg.
710            Err(NetworkManagerError::Add(
711                fnp_properties::NetworkRegistryAddError::MissingNetworkId,
712            )),
713            // Network added to Manager and to netcfg.
714            Ok(()),
715            // Network set as default in Manager, but not in netcfg.
716            Err(NetworkManagerError::SetDefault(
717                fnp_properties::NetworkRegistrySetDefaultError::NotFound,
718            )),
719            // Network set as default in Manager and in netcfg.
720            Ok(()),
721            // Network updated in Manager, but not in netcfg.
722            Err(NetworkManagerError::Update(fnp_properties::NetworkRegistryUpdateError::NotFound)),
723            // Network updated in Manager and in netcfg.
724            Ok(()),
725            // Unset the default network so the network is eligible to be removed.
726            Ok(()),
727            // Network removed from Manager, but not from netcfg.
728            Err(NetworkManagerError::Remove(fnp_properties::NetworkRegistryRemoveError::NotFound)),
729            // Network removed from Manager and from netcfg.
730            Ok(()),
731        ];
732        let manager = &setup_proxy(inspector.root(), results);
733
734        // Add a network that doesn't get sent to netcfg.
735        let network1 = test_network_message_from_id(1);
736        assert_matches!(manager.add_network(network1.clone()), Ok(()));
737        assert_data_tree!(inspector, root: {
738            nmfs: contains {
739                added_networks: {
740                    seen: 1u64,
741                    sent: 0u64,
742                },
743            },
744        });
745
746        // Add a network that gets sent to netcfg.
747        let network2 = test_network_message_from_id(2);
748        assert_matches!(manager.add_network(network2.clone()), Ok(()));
749        assert_data_tree!(inspector, root: {
750            nmfs: contains {
751                added_networks: {
752                    seen: 2u64,
753                    sent: 1u64,
754                },
755            },
756        });
757
758        // Set the default network that isn't known to netcfg.
759        manager.set_default_network_id(Some(1));
760        assert_eq!(manager.get_default_network_id(), Some(1));
761        assert_data_tree!(inspector, root: {
762            nmfs: contains {
763                default_ids_set: {
764                    seen: 1u64,
765                    sent: 0u64,
766                },
767            },
768        });
769
770        // Set the default network that is known to netcfg.
771        manager.set_default_network_id(Some(2));
772        assert_eq!(manager.get_default_network_id(), Some(2));
773        assert_data_tree!(inspector, root: {
774            nmfs: contains {
775                default_ids_set: {
776                    seen: 2u64,
777                    sent: 1u64,
778                },
779            },
780        });
781
782        // Update a network not known to netcfg.
783        let mut network1_updated = network1.clone();
784        network1_updated.mark = 1;
785        assert_matches!(manager.update_network(network1_updated.clone()), Ok(()));
786        assert_data_tree!(inspector, root: {
787            nmfs: contains {
788                updated_networks: {
789                    seen: 1u64,
790                    sent: 0u64,
791                },
792            },
793        });
794
795        // Update a network that is known to netcfg.
796        let mut network2_updated = network2.clone();
797        network2_updated.mark = 2;
798        assert_matches!(manager.update_network(network2_updated.clone()), Ok(()));
799        assert_data_tree!(inspector, root: {
800            nmfs: contains {
801                updated_networks: {
802                    seen: 2u64,
803                    sent: 1u64,
804                },
805            },
806        });
807
808        // The default network must be unset first to remove this network.
809        manager.set_default_network_id(None);
810        assert_data_tree!(inspector, root: {
811            nmfs: contains {
812                default_ids_set: {
813                    seen: 3u64,
814                    sent: 2u64,
815                },
816            },
817        });
818
819        // Remove a network that doesn't get sent to netcfg.
820        assert_matches!(manager.remove_network(1), Ok(()));
821        assert_data_tree!(inspector, root: {
822            nmfs: contains {
823                removed_networks: {
824                    seen: 1u64,
825                    sent: 0u64,
826                },
827            },
828        });
829
830        // Remove a network that gets sent to netcfg.
831        assert_matches!(manager.remove_network(2), Ok(()));
832        assert_data_tree!(inspector, root: {
833            nmfs: contains {
834                default_ids_set: {
835                    seen: 3u64,
836                    sent: 2u64,
837                },
838                added_networks: {
839                    seen: 2u64,
840                    sent: 1u64,
841                },
842                updated_networks: {
843                    seen: 2u64,
844                    sent: 1u64,
845                },
846                removed_networks: {
847                    seen: 2u64,
848                    sent: 1u64,
849                },
850            },
851        });
852    }
853}