fdf_component/testing/
node.rs

1// Copyright 2025 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
5//! This module provides a mock for the driver framework provided Node and NodeController.
6
7use anyhow::Result;
8use fidl::endpoints::ServerEnd;
9use fuchsia_sync::Mutex;
10use futures::TryStreamExt;
11use std::collections::HashMap;
12use std::sync::{Arc, Weak};
13use {
14    fidl_fuchsia_device_fs as fdf_devfs, fidl_fuchsia_driver_framework as fdf_fidl,
15    fuchsia_async as fasync, zx,
16};
17
18/// This represents a node in the NodeManage.
19pub type NodeId = usize;
20
21/// The TestNode backs the driver framework provided protocols Node and NodeController.
22struct TestNode {
23    id: NodeId,
24    context: Weak<NodeManager>,
25    name: String,
26    children: HashMap<String, NodeId>,
27    properties: Vec<fdf_fidl::NodeProperty2>,
28    parent: Option<NodeId>,
29    devfs_connector_client: Option<fdf_devfs::ConnectorProxy>,
30    scope: fasync::Scope,
31}
32
33impl TestNode {
34    fn serve_node(&self, server_end: ServerEnd<fdf_fidl::NodeMarker>) {
35        let mut stream = server_end.into_stream();
36        let node_id = self.id;
37        let context = self.context.clone();
38        self.scope.spawn(async move {
39            while let Some(request) = stream.try_next().await.unwrap() {
40                if let fdf_fidl::NodeRequest::AddChild { args, controller, node, responder } =
41                    request
42                {
43                    let name = args.name.as_ref().unwrap();
44                    let _ = context.upgrade().expect("manager").new_node(
45                        name,
46                        Some(node_id),
47                        args.properties2,
48                        Some(controller),
49                        node,
50                        args.devfs_args,
51                    );
52
53                    let _ = responder.send(Ok(()));
54                }
55            }
56
57            context.upgrade().expect("manager").remove_node(&node_id);
58        });
59    }
60
61    fn serve_controller(&self, server_end: ServerEnd<fdf_fidl::NodeControllerMarker>) {
62        let mut stream = server_end.into_stream();
63        let node_id = self.id;
64        let context = self.context.clone();
65        self.scope.spawn(async move {
66            while let Some(request) = stream.try_next().await.unwrap() {
67                match request {
68                    fdf_fidl::NodeControllerRequest::Remove { control_handle: _ } => {
69                        context.upgrade().expect("manager").remove_node(&node_id);
70                    }
71                    fdf_fidl::NodeControllerRequest::RequestBind { payload: _, responder } => {
72                        let _ = responder.send(Ok(()));
73                    }
74                    _ => (),
75                }
76            }
77        });
78    }
79}
80
81pub(crate) struct NodeManager {
82    nodes: Mutex<HashMap<NodeId, TestNode>>,
83    next_id: Mutex<NodeId>,
84}
85
86/// A handle to nodes running inside the test.
87pub struct NodeHandle {
88    manager: Weak<NodeManager>,
89    id: NodeId,
90}
91
92impl NodeHandle {
93    pub(crate) fn new(manager: Weak<NodeManager>, id: NodeId) -> Self {
94        Self { manager, id }
95    }
96
97    /// Gets the name of the node.
98    pub fn name(&self) -> String {
99        self.manager.upgrade().expect("manager").name(&self.id)
100    }
101
102    /// Gets the children of the node.
103    pub fn children(&self) -> HashMap<String, NodeHandle> {
104        self.manager
105            .upgrade()
106            .expect("manager")
107            .children(&self.id)
108            .into_iter()
109            .map(|(n, id)| (n, NodeHandle::new(self.manager.clone(), id)))
110            .collect()
111    }
112
113    /// Gets the properties of the node.
114    pub fn properties(&self) -> Vec<fdf_fidl::NodeProperty2> {
115        self.manager.upgrade().expect("manager").properties(&self.id)
116    }
117
118    /// Gets the parent of the node.
119    pub fn parent(&self) -> Option<NodeHandle> {
120        self.manager
121            .upgrade()
122            .expect("manager")
123            .parent(&self.id)
124            .map(|id| NodeHandle::new(self.manager.clone(), id))
125    }
126
127    /// Connects to the node's devfs entry.
128    pub async fn connect_to_device(&self) -> Result<zx::Channel, anyhow::Error> {
129        self.manager.upgrade().expect("manager").connect_to_device(self.id).await
130    }
131}
132
133impl NodeManager {
134    pub(crate) fn new() -> Arc<Self> {
135        Arc::new(Self { nodes: Mutex::new(HashMap::new()), next_id: Mutex::new(0) })
136    }
137
138    pub(crate) fn create_root_node(
139        self: &Arc<Self>,
140        node: ServerEnd<fdf_fidl::NodeMarker>,
141    ) -> NodeId {
142        self.new_node("root", None, None, None, Some(node), None)
143    }
144
145    fn new_node(
146        self: &Arc<Self>,
147        name: &str,
148        parent: Option<NodeId>,
149        properties: Option<Vec<fdf_fidl::NodeProperty2>>,
150        controller: Option<ServerEnd<fdf_fidl::NodeControllerMarker>>,
151        node: Option<ServerEnd<fdf_fidl::NodeMarker>>,
152        devfs_args: Option<fdf_fidl::DevfsAddArgs>,
153    ) -> NodeId {
154        let mut next_id = self.next_id.lock();
155        let child_id = *next_id;
156        *next_id += 1;
157        drop(next_id);
158
159        let devfs_connector_client = {
160            if let Some(fdf_fidl::DevfsAddArgs { connector: Some(client), .. }) = devfs_args {
161                Some(client.into_proxy())
162            } else {
163                None
164            }
165        };
166
167        let child_node = TestNode {
168            id: child_id,
169            context: Arc::downgrade(self),
170            name: name.to_string(),
171            children: HashMap::new(),
172            properties: properties.unwrap_or_default(),
173            parent,
174            devfs_connector_client,
175            scope: fasync::Scope::new(),
176        };
177
178        if let Some(parent_id) = parent {
179            let mut nodes = self.nodes.lock();
180            nodes.get_mut(&parent_id).expect("parent").children.insert(name.to_string(), child_id);
181            log::info!("adding child {name} to parent {parent_id}");
182        }
183
184        if let Some(controller) = controller {
185            child_node.serve_controller(controller);
186        }
187
188        if let Some(node) = node {
189            child_node.serve_node(node);
190        }
191
192        self.nodes.lock().insert(child_id, child_node);
193        child_id
194    }
195
196    async fn connect_to_device(&self, node_id: NodeId) -> Result<zx::Channel, anyhow::Error> {
197        let (client_end, server_end) = zx::Channel::create();
198        let nodes = self.nodes.lock();
199        let node = nodes.get(&node_id).expect("node");
200        if let Some(connector) = node.devfs_connector_client.as_ref() {
201            connector.connect(server_end).unwrap();
202            Ok(client_end)
203        } else {
204            Err(anyhow::anyhow!("Devfs connector not found"))
205        }
206    }
207
208    fn children(self: &Arc<Self>, node_id: &NodeId) -> HashMap<String, NodeId> {
209        let nodes = self.nodes.lock();
210        nodes.get(node_id).expect("node").children.clone()
211    }
212
213    fn parent(&self, node_id: &NodeId) -> Option<NodeId> {
214        let nodes = self.nodes.lock();
215        nodes.get(node_id).expect("node").parent
216    }
217
218    fn name(&self, node_id: &NodeId) -> String {
219        let nodes = self.nodes.lock();
220        nodes.get(node_id).expect("node").name.clone()
221    }
222
223    fn properties(&self, node_id: &NodeId) -> Vec<fdf_fidl::NodeProperty2> {
224        let nodes = self.nodes.lock();
225        nodes.get(node_id).expect("node").properties.clone()
226    }
227
228    fn remove_node(self: &Arc<Self>, node_id: &NodeId) {
229        let children = self.children(node_id);
230        for child_id in children.values() {
231            self.remove_node(child_id);
232        }
233
234        if let Some(parent_id) = self.parent(node_id) {
235            let name = self.name(node_id);
236            let mut nodes = self.nodes.lock();
237            nodes.get_mut(&parent_id).expect("parent").children.remove(&name);
238        }
239
240        self.nodes.lock().remove(node_id);
241    }
242}