blob: 07cea5a4c434b2258bbe0dac4b287a7c42e19567 [file] [log] [blame]
// Copyright 2025 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.
//! This module provides a mock for the driver framework provided Node and NodeController.
use anyhow::Result;
use fidl::endpoints::ServerEnd;
use fuchsia_sync::Mutex;
use futures::TryStreamExt;
use std::collections::HashMap;
use std::sync::{Arc, Weak};
use {
fidl_fuchsia_device_fs as fdf_devfs, fidl_fuchsia_driver_framework as fdf_fidl,
fuchsia_async as fasync, zx,
};
/// This represents a node in the NodeManage.
pub type NodeId = usize;
/// The TestNode backs the driver framework provided protocols Node and NodeController.
struct TestNode {
id: NodeId,
context: Weak<NodeManager>,
name: String,
children: HashMap<String, NodeId>,
properties: Vec<fdf_fidl::NodeProperty2>,
parent: Option<NodeId>,
devfs_connector_client: Option<fdf_devfs::ConnectorProxy>,
scope: fasync::Scope,
}
impl TestNode {
fn serve_node(&self, server_end: ServerEnd<fdf_fidl::NodeMarker>) {
let mut stream = server_end.into_stream();
let node_id = self.id;
let context = self.context.clone();
self.scope.spawn(async move {
while let Some(request) = stream.try_next().await.unwrap() {
if let fdf_fidl::NodeRequest::AddChild { args, controller, node, responder } =
request
{
let name = args.name.as_ref().unwrap();
let _ = context.upgrade().expect("manager").new_node(
name,
Some(node_id),
args.properties2,
Some(controller),
node,
args.devfs_args,
);
let _ = responder.send(Ok(()));
}
}
context.upgrade().expect("manager").remove_node(&node_id);
});
}
fn serve_controller(&self, server_end: ServerEnd<fdf_fidl::NodeControllerMarker>) {
let mut stream = server_end.into_stream();
let node_id = self.id;
let context = self.context.clone();
self.scope.spawn(async move {
while let Some(request) = stream.try_next().await.unwrap() {
match request {
fdf_fidl::NodeControllerRequest::Remove { control_handle: _ } => {
context.upgrade().expect("manager").remove_node(&node_id);
}
fdf_fidl::NodeControllerRequest::RequestBind { payload: _, responder } => {
let _ = responder.send(Ok(()));
}
_ => (),
}
}
});
}
}
pub(crate) struct NodeManager {
nodes: Mutex<HashMap<NodeId, TestNode>>,
next_id: Mutex<NodeId>,
}
/// A handle to nodes running inside the test.
pub struct NodeHandle {
manager: Weak<NodeManager>,
id: NodeId,
}
impl NodeHandle {
pub(crate) fn new(manager: Weak<NodeManager>, id: NodeId) -> Self {
Self { manager, id }
}
/// Gets the name of the node.
pub fn name(&self) -> String {
self.manager.upgrade().expect("manager").name(&self.id)
}
/// Gets the children of the node.
pub fn children(&self) -> HashMap<String, NodeHandle> {
self.manager
.upgrade()
.expect("manager")
.children(&self.id)
.into_iter()
.map(|(n, id)| (n, NodeHandle::new(self.manager.clone(), id)))
.collect()
}
/// Gets the properties of the node.
pub fn properties(&self) -> Vec<fdf_fidl::NodeProperty2> {
self.manager.upgrade().expect("manager").properties(&self.id)
}
/// Gets the parent of the node.
pub fn parent(&self) -> Option<NodeHandle> {
self.manager
.upgrade()
.expect("manager")
.parent(&self.id)
.map(|id| NodeHandle::new(self.manager.clone(), id))
}
/// Connects to the node's devfs entry.
pub async fn connect_to_device(&self) -> Result<zx::Channel, anyhow::Error> {
self.manager.upgrade().expect("manager").connect_to_device(self.id).await
}
}
impl NodeManager {
pub(crate) fn new() -> Arc<Self> {
Arc::new(Self { nodes: Mutex::new(HashMap::new()), next_id: Mutex::new(0) })
}
pub(crate) fn create_root_node(
self: &Arc<Self>,
node: ServerEnd<fdf_fidl::NodeMarker>,
) -> NodeId {
self.new_node("root", None, None, None, Some(node), None)
}
fn new_node(
self: &Arc<Self>,
name: &str,
parent: Option<NodeId>,
properties: Option<Vec<fdf_fidl::NodeProperty2>>,
controller: Option<ServerEnd<fdf_fidl::NodeControllerMarker>>,
node: Option<ServerEnd<fdf_fidl::NodeMarker>>,
devfs_args: Option<fdf_fidl::DevfsAddArgs>,
) -> NodeId {
let mut next_id = self.next_id.lock();
let child_id = *next_id;
*next_id += 1;
drop(next_id);
let devfs_connector_client = {
if let Some(fdf_fidl::DevfsAddArgs { connector: Some(client), .. }) = devfs_args {
Some(client.into_proxy())
} else {
None
}
};
let child_node = TestNode {
id: child_id,
context: Arc::downgrade(self),
name: name.to_string(),
children: HashMap::new(),
properties: properties.unwrap_or_default(),
parent,
devfs_connector_client,
scope: fasync::Scope::new(),
};
if let Some(parent_id) = parent {
let mut nodes = self.nodes.lock();
nodes.get_mut(&parent_id).expect("parent").children.insert(name.to_string(), child_id);
log::info!("adding child {name} to parent {parent_id}");
}
if let Some(controller) = controller {
child_node.serve_controller(controller);
}
if let Some(node) = node {
child_node.serve_node(node);
}
self.nodes.lock().insert(child_id, child_node);
child_id
}
async fn connect_to_device(&self, node_id: NodeId) -> Result<zx::Channel, anyhow::Error> {
let (client_end, server_end) = zx::Channel::create();
let nodes = self.nodes.lock();
let node = nodes.get(&node_id).expect("node");
if let Some(connector) = node.devfs_connector_client.as_ref() {
connector.connect(server_end).unwrap();
Ok(client_end)
} else {
Err(anyhow::anyhow!("Devfs connector not found"))
}
}
fn children(self: &Arc<Self>, node_id: &NodeId) -> HashMap<String, NodeId> {
let nodes = self.nodes.lock();
nodes.get(node_id).expect("node").children.clone()
}
fn parent(&self, node_id: &NodeId) -> Option<NodeId> {
let nodes = self.nodes.lock();
nodes.get(node_id).expect("node").parent
}
fn name(&self, node_id: &NodeId) -> String {
let nodes = self.nodes.lock();
nodes.get(node_id).expect("node").name.clone()
}
fn properties(&self, node_id: &NodeId) -> Vec<fdf_fidl::NodeProperty2> {
let nodes = self.nodes.lock();
nodes.get(node_id).expect("node").properties.clone()
}
fn remove_node(self: &Arc<Self>, node_id: &NodeId) {
let children = self.children(node_id);
for child_id in children.values() {
self.remove_node(child_id);
}
if let Some(parent_id) = self.parent(node_id) {
let name = self.name(node_id);
let mut nodes = self.nodes.lock();
nodes.get_mut(&parent_id).expect("parent").children.remove(&name);
}
self.nodes.lock().remove(node_id);
}
}