blob: 64ac307b1cb5417eaa69db5f9cc6b09721f3d279 [file] [log] [blame]
// Copyright 2021 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.
mod test_utils;
use {
anyhow::{Context, Result},
fidl_fuchsia_driver_development as fdd, fidl_fuchsia_driver_framework as fdf,
fidl_fuchsia_driver_test as fdt,
fuchsia_async::{self as fasync},
fuchsia_component_test::{RealmBuilder, RealmInstance},
fuchsia_driver_test::{DriverTestRealmBuilder, DriverTestRealmInstance},
fuchsia_zircon_status as zx_status,
};
const SAMPLE_DRIVER_URL: &str = "fuchsia-boot:///dtr#meta/sample-driver.cm";
const PARENT_DRIVER_URL: &str = "fuchsia-boot:///dtr#meta/test-parent-sys.cm";
const FAKE_DRIVER_URL: &str = "fuchsia-boot:///dtr#meta/driver-test-realm-fake-driver.cm";
fn get_no_protocol_property_list() -> Option<[fdf::NodeProperty; 3]> {
Some([
fdf::NodeProperty {
key: fdf::NodePropertyKey::IntValue(bind::ddk_bind_constants::BIND_PROTOCOL),
value: fdf::NodePropertyValue::IntValue(28), // ZX_PROTOCOL_MISC
},
fdf::NodeProperty {
key: fdf::NodePropertyKey::StringValue("fuchsia.driver.compat.Service".to_string()),
value: fdf::NodePropertyValue::StringValue(
"fuchsia.driver.compat.Service.ZirconTransport".to_string(),
),
},
fdf::NodeProperty {
key: fdf::NodePropertyKey::StringValue(String::from(
bind_fuchsia_platform::DRIVER_FRAMEWORK_VERSION,
)),
value: fdf::NodePropertyValue::IntValue(2),
},
])
}
fn get_test_parent_property_list() -> Option<[fdf::NodeProperty; 3]> {
Some([
fdf::NodeProperty {
key: fdf::NodePropertyKey::IntValue(bind::ddk_bind_constants::BIND_PROTOCOL),
value: fdf::NodePropertyValue::IntValue(bind_fuchsia_test::BIND_PROTOCOL_PARENT),
},
fdf::NodeProperty {
key: fdf::NodePropertyKey::StringValue("fuchsia.driver.compat.Service".to_string()),
value: fdf::NodePropertyValue::StringValue(
"fuchsia.driver.compat.Service.ZirconTransport".to_string(),
),
},
fdf::NodeProperty {
key: fdf::NodePropertyKey::StringValue(String::from(
bind_fuchsia_platform::DRIVER_FRAMEWORK_VERSION,
)),
value: fdf::NodePropertyValue::IntValue(2),
},
])
}
fn assert_not_found_error(error: fidl::Error) {
if let fidl::Error::ClientChannelClosed { status, protocol_name: _ } = error {
assert_eq!(status, zx_status::Status::NOT_FOUND);
} else {
panic!("Expcted ClientChannelClosed error");
}
}
fn send_get_device_info_request(
service: &fdd::ManagerProxy,
device_filter: &[&str],
exact_match: bool,
) -> Result<fdd::NodeInfoIteratorProxy> {
let (iterator, iterator_server) =
fidl::endpoints::create_proxy::<fdd::NodeInfoIteratorMarker>()?;
service
.get_node_info(
&device_filter.iter().copied().map(String::from).collect::<Vec<_>>(),
iterator_server,
exact_match,
)
.context("FIDL call to get device info failed")?;
Ok(iterator)
}
async fn get_device_info(
service: &fdd::ManagerProxy,
device_filter: &[&str],
exact_match: bool,
) -> Result<Vec<fdd::NodeInfo>> {
let iterator = send_get_device_info_request(service, device_filter, exact_match)?;
let mut device_infos = Vec::new();
loop {
let mut device_info =
iterator.get_next().await.context("FIDL call to get device info failed")?;
if device_info.len() == 0 {
break;
}
device_infos.append(&mut device_info);
}
Ok(device_infos)
}
fn send_get_driver_info_request(
service: &fdd::ManagerProxy,
driver_filter: &[&str],
) -> Result<fdd::DriverInfoIteratorProxy> {
let (iterator, iterator_server) =
fidl::endpoints::create_proxy::<fdd::DriverInfoIteratorMarker>()?;
service
.get_driver_info(
&driver_filter.iter().copied().map(String::from).collect::<Vec<_>>(),
iterator_server,
)
.context("FIDL call to get driver info failed")?;
Ok(iterator)
}
async fn get_driver_info(
service: &fdd::ManagerProxy,
driver_filter: &[&str],
) -> Result<Vec<fdf::DriverInfo>> {
let iterator = send_get_driver_info_request(service, driver_filter)?;
let mut driver_infos = Vec::new();
loop {
let mut driver_info =
iterator.get_next().await.context("FIDL call to get driver info failed")?;
if driver_info.len() == 0 {
break;
}
driver_infos.append(&mut driver_info)
}
Ok(driver_infos)
}
async fn set_up_test_driver_realm() -> Result<(RealmInstance, fdd::ManagerProxy)> {
const ROOT_DRIVER_URL: &str = PARENT_DRIVER_URL;
let builder = RealmBuilder::new().await?;
builder.driver_test_realm_setup().await?;
let instance = builder.build().await?;
let mut realm_args = fdt::RealmArgs::default();
// DriverTestRealm attempts to bind the .cm of test-parent-sys if not explicitly requested otherwise.
realm_args.root_driver = Some(ROOT_DRIVER_URL.to_owned());
instance.driver_test_realm_start(realm_args).await?;
let driver_dev = instance.root.connect_to_protocol_at_exposed_dir::<fdd::ManagerMarker>()?;
// Make sure we wait until all the drivers are bound before returning.
let dev = instance.driver_test_realm_connect_to_dev()?;
device_watcher::recursive_wait(&dev, "sys/test/sample_driver")
.await
.expect("recursive wait failed");
Ok((instance, driver_dev))
}
fn assert_contains_driver_url(driver_infos: &Vec<fdf::DriverInfo>, expected_driver_url: &str) {
assert!(driver_infos
.iter()
.find(|driver_info| driver_info.url.as_ref().expect("Missing device URL")
== expected_driver_url)
.is_some());
}
// GetDriverInfo tests
// DFv1
#[fasync::run_singlethreaded(test)]
async fn test_get_driver_info_no_filter_dfv1() -> Result<()> {
let (_instance, driver_dev) = set_up_test_driver_realm().await?;
let driver_infos = get_driver_info(&driver_dev, &[]).await?;
assert_eq!(driver_infos.len(), 3);
assert_contains_driver_url(&driver_infos, SAMPLE_DRIVER_URL);
assert_contains_driver_url(&driver_infos, PARENT_DRIVER_URL);
assert_contains_driver_url(&driver_infos, FAKE_DRIVER_URL);
Ok(())
}
#[fasync::run_singlethreaded(test)]
async fn test_get_driver_info_with_filter_dfv1() -> Result<()> {
const DRIVER_FILTER: [&str; 1] = [SAMPLE_DRIVER_URL];
let (_instance, driver_dev) = set_up_test_driver_realm().await?;
let driver_infos = get_driver_info(&driver_dev, &DRIVER_FILTER).await?;
assert_eq!(driver_infos.len(), 1);
assert_contains_driver_url(&driver_infos, SAMPLE_DRIVER_URL);
Ok(())
}
#[fasync::run_singlethreaded(test)]
async fn test_get_driver_info_with_mixed_filter_dfv1() -> Result<()> {
const DRIVER_FILTER: [&str; 2] = ["fuchsia-boot:///dtr#driver/sample-driver.so", "foo"];
let (_instance, driver_dev) = set_up_test_driver_realm().await?;
let iterator = send_get_driver_info_request(&driver_dev, &DRIVER_FILTER)?;
let res = iterator.get_next().await.expect_err("A driver should not be returned");
assert_not_found_error(res);
Ok(())
}
#[fasync::run_singlethreaded(test)]
async fn test_get_driver_info_with_incomplete_filter_dfv1() -> Result<()> {
const DRIVER_FILTER: [&str; 1] = ["fuchsia-boot:///dtr#driver/sample-driver"];
let (_instance, driver_dev) = set_up_test_driver_realm().await?;
let iterator = send_get_driver_info_request(&driver_dev, &DRIVER_FILTER)?;
let res = iterator.get_next().await.expect_err("A driver should not be returned");
assert_not_found_error(res);
Ok(())
}
#[fasync::run_singlethreaded(test)]
async fn test_get_driver_info_not_found_filter_dfv1() -> Result<()> {
const DRIVER_FILTER: [&str; 1] = ["foo"];
let (_instance, driver_dev) = set_up_test_driver_realm().await?;
let iterator = send_get_driver_info_request(&driver_dev, &DRIVER_FILTER)?;
let res = iterator.get_next().await.expect_err("A driver should not be returned");
assert_not_found_error(res);
Ok(())
}
#[fasync::run_singlethreaded(test)]
async fn test_get_driver_info_no_filter() -> Result<()> {
let (_instance, driver_dev) = set_up_test_driver_realm().await?;
let driver_infos = get_driver_info(&driver_dev, &[]).await?;
assert_eq!(driver_infos.len(), 3);
assert_contains_driver_url(&driver_infos, SAMPLE_DRIVER_URL);
assert_contains_driver_url(&driver_infos, PARENT_DRIVER_URL);
assert_contains_driver_url(&driver_infos, FAKE_DRIVER_URL);
Ok(())
}
#[fasync::run_singlethreaded(test)]
async fn test_get_driver_info_with_filter() -> Result<()> {
const DRIVER_FILTER: [&str; 1] = [SAMPLE_DRIVER_URL];
let (_instance, driver_dev) = set_up_test_driver_realm().await?;
let driver_infos = get_driver_info(&driver_dev, &DRIVER_FILTER).await?;
assert_eq!(driver_infos.len(), 1);
assert_contains_driver_url(&driver_infos, SAMPLE_DRIVER_URL);
Ok(())
}
#[fasync::run_singlethreaded(test)]
async fn test_get_driver_info_with_duplicate_filter() -> Result<()> {
const DRIVER_FILTER: [&str; 2] = [SAMPLE_DRIVER_URL, SAMPLE_DRIVER_URL];
let (_instance, driver_dev) = set_up_test_driver_realm().await?;
let driver_infos = get_driver_info(&driver_dev, &DRIVER_FILTER).await?;
assert_eq!(driver_infos.len(), 1);
assert_contains_driver_url(&driver_infos, SAMPLE_DRIVER_URL);
Ok(())
}
#[fasync::run_singlethreaded(test)]
async fn test_get_driver_info_with_mixed_filter() -> Result<()> {
const DRIVER_FILTER: [&str; 2] = [SAMPLE_DRIVER_URL, "foo"];
let (_instance, driver_dev) = set_up_test_driver_realm().await?;
let driver_infos = get_driver_info(&driver_dev, &DRIVER_FILTER).await?;
assert_eq!(driver_infos.len(), 1);
assert_contains_driver_url(&driver_infos, SAMPLE_DRIVER_URL);
Ok(())
}
#[fasync::run_singlethreaded(test)]
async fn test_get_driver_info_with_incomplete_filter() -> Result<()> {
const DRIVER_FILTER: [&str; 1] = ["fuchsia-boot:///dtr#meta/sample-driver"];
let (_instance, driver_dev) = set_up_test_driver_realm().await?;
let iterator = send_get_driver_info_request(&driver_dev, &DRIVER_FILTER)?;
let res = iterator.get_next().await.expect_err("A driver should not be returned");
assert_not_found_error(res);
Ok(())
}
#[fasync::run_singlethreaded(test)]
async fn test_get_driver_info_not_found_filter() -> Result<()> {
const DRIVER_FILTER: [&str; 1] = ["foo"];
let (_instance, driver_dev) = set_up_test_driver_realm().await?;
let iterator = send_get_driver_info_request(&driver_dev, &DRIVER_FILTER)?;
let res = iterator.get_next().await.expect_err("A driver should not be returned");
assert_not_found_error(res);
Ok(())
}
#[fasync::run_singlethreaded(test)]
async fn test_get_device_info_fuzzy_filter() -> Result<()> {
const DEVICE_FILTER: [&str; 1] = ["sample"];
let (_instance, driver_dev) = set_up_test_driver_realm().await?;
let device_infos =
get_device_info(&driver_dev, &DEVICE_FILTER, /* exact_match= */ false).await?;
assert_eq!(device_infos.len(), 1);
let device_nodes = test_utils::create_device_topology(device_infos);
assert_eq!(device_nodes.len(), 1);
let matched_node = &device_nodes[0];
assert_eq!(
matched_node.info.moniker.as_ref().expect("device missing monier"),
"dev.sys.test.sample_driver"
);
Ok(())
}
#[fasync::run_singlethreaded(test)]
async fn test_get_device_info_no_filter() -> Result<()> {
let (_instance, driver_dev) = set_up_test_driver_realm().await?;
let device_infos = get_device_info(&driver_dev, &[], /* exact_match= */ true).await?;
let device_nodes = test_utils::create_device_topology(device_infos);
assert_eq!(device_nodes.len(), 1);
let root = &device_nodes[0];
assert_eq!(root.info.moniker.as_ref().expect("node missing moniker"), "dev");
assert_eq!(
root.info.bound_driver_url.as_ref().expect("node missing driver URL"),
PARENT_DRIVER_URL
);
assert!(root.info.node_property_list.is_none());
assert_eq!(root.num_children, 1);
assert_eq!(root.child_nodes.len(), 1);
let sys = &root.child_nodes[0];
assert_eq!(sys.info.moniker.as_ref().expect("node missing moniker"), "dev.sys");
assert_eq!(sys.info.bound_driver_url.as_ref().expect("node missing driver URL"), "unbound");
assert_eq!(
sys.info.node_property_list.as_ref().map(|x| x.as_slice()),
get_no_protocol_property_list().as_ref().map(|x| x.as_slice())
);
assert_eq!(sys.num_children, 1);
assert_eq!(sys.child_nodes.len(), 1);
let test = &sys.child_nodes[0];
assert_eq!(test.info.moniker.as_ref().expect("node missing moniker"), "dev.sys.test");
assert_eq!(
test.info.bound_driver_url.as_ref().expect("node missing driver URL"),
SAMPLE_DRIVER_URL
);
assert_eq!(
test.info.node_property_list.as_ref().map(|x| x.as_slice()),
get_test_parent_property_list().as_ref().map(|x| x.as_slice())
);
Ok(())
}
#[fasync::run_singlethreaded(test)]
async fn test_get_device_info_with_filter() -> Result<()> {
const DEVICE_FILTER: [&str; 1] = ["dev.sys.test"];
let (_instance, driver_dev) = set_up_test_driver_realm().await?;
let device_infos =
get_device_info(&driver_dev, &DEVICE_FILTER, /* exact_match= */ true).await?;
let device_nodes = test_utils::create_device_topology(device_infos);
assert_eq!(device_nodes.len(), 1);
let root_sys_test = &device_nodes[0];
assert_eq!(root_sys_test.info.moniker.as_ref().expect("node missing moniker"), "dev.sys.test");
assert_eq!(
root_sys_test.info.bound_driver_url.as_ref().expect("node missing driver URL"),
SAMPLE_DRIVER_URL
);
assert_eq!(
root_sys_test.info.node_property_list.as_ref().map(|x| x.as_slice()),
get_test_parent_property_list().as_ref().map(|x| x.as_slice())
);
assert!(root_sys_test.child_nodes.is_empty());
Ok(())
}
#[fasync::run_singlethreaded(test)]
async fn test_get_device_info_with_duplicate_filter() -> Result<()> {
const DEVICE_FILTER: [&str; 2] = ["dev.sys.test", "dev.sys.test"];
let (_instance, driver_dev) = set_up_test_driver_realm().await?;
let device_infos =
get_device_info(&driver_dev, &DEVICE_FILTER, /* exact_match= */ true).await?;
let device_nodes = test_utils::create_device_topology(device_infos);
assert_eq!(device_nodes.len(), 1);
let root_sys_test = &device_nodes[0];
assert_eq!(root_sys_test.info.moniker.as_ref().expect("node missing moniker"), "dev.sys.test");
assert_eq!(
root_sys_test.info.bound_driver_url.as_ref().expect("node missing driver URL"),
SAMPLE_DRIVER_URL
);
assert_eq!(
root_sys_test.info.node_property_list.as_ref().map(|x| x.as_slice()),
get_test_parent_property_list().as_ref().map(|x| x.as_slice())
);
assert!(root_sys_test.child_nodes.is_empty());
Ok(())
}
#[fasync::run_singlethreaded(test)]
async fn test_get_device_info_with_incomplete_filter() -> Result<()> {
const DEVICE_FILTER: [&str; 1] = ["dev.sys.te"];
let (_instance, driver_dev) = set_up_test_driver_realm().await?;
let device_infos =
get_device_info(&driver_dev, &DEVICE_FILTER, /* exact_match= */ true).await?;
assert!(device_infos.is_empty());
Ok(())
}
#[fasync::run_singlethreaded(test)]
async fn test_get_device_info_not_found_filter() -> Result<()> {
const DEVICE_FILTER: [&str; 1] = ["foo"];
let (_instance, driver_dev) = set_up_test_driver_realm().await?;
let device_infos =
get_device_info(&driver_dev, &DEVICE_FILTER, /* exact_match= */ true).await?;
assert!(device_infos.is_empty());
Ok(())
}
#[fasync::run_singlethreaded(test)]
async fn test_add_test_node() -> Result<()> {
let (instance, driver_dev) = set_up_test_driver_realm().await?;
driver_dev
.add_test_node(&fdd::TestNodeAddArgs {
name: Some("test_sample".to_string()),
properties: Some(vec![fdf::NodeProperty {
key: fdf::NodePropertyKey::IntValue(bind::ddk_bind_constants::BIND_PROTOCOL),
value: fdf::NodePropertyValue::IntValue(bind_fuchsia_test::BIND_PROTOCOL_PARENT),
}]),
..Default::default()
})
.await?
.unwrap();
let dev = instance.driver_test_realm_connect_to_dev()?;
device_watcher::recursive_wait(&dev, "test_sample/sample_driver").await?;
driver_dev.remove_test_node("test_sample").await?.unwrap();
assert_eq!(
Err(zx_status::Status::NOT_FOUND.into_raw()),
driver_dev.remove_test_node("test_sample").await?
);
Ok(())
}