blob: 92c9f5b9dd291be2fcaf7d0ea2534904d73a47e9 [file] [log] [blame]
// Copyright 2024 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.
use anyhow::{anyhow, Context, Result};
use fuchsia_component_test::RealmBuilder;
use fuchsia_driver_test::{DriverTestRealmBuilder, DriverTestRealmInstance};
use {
fidl_fuchsia_driver_development as fdd, fidl_fuchsia_driver_registrar as fdr,
fidl_fuchsia_driver_test as fdt, fuchsia_async as fasync,
};
fn send_get_device_info_request(
service: &fdd::ManagerProxy,
device_filter: &[String],
exact_match: bool,
) -> Result<fdd::NodeInfoIteratorProxy> {
let (iterator, iterator_server) =
fidl::endpoints::create_proxy::<fdd::NodeInfoIteratorMarker>();
service
.get_node_info(device_filter, iterator_server, exact_match)
.context("FIDL call to get device info failed")?;
Ok(iterator)
}
async fn get_device_info(
service: &fdd::ManagerProxy,
device_filter: &[String],
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)
}
#[fasync::run_singlethreaded(test)]
async fn test_replace_failed_driver() -> Result<()> {
let node_filter = ["dev.sys.test".to_string()];
let failing_url = "fuchsia-boot:///dtr#meta/fail-to-start.cm";
let replacemnt_url =
"fuchsia-pkg://fuchsia.com/fail-to-start-replacement#meta/fail-to-start-replacement.cm";
// Create the RealmBuilder.
let builder = RealmBuilder::new().await?;
builder.driver_test_realm_setup().await?;
// Build the Realm.
let instance = builder.build().await?;
// Start the DriverTestRealm.
// Keep the replacement disabled until we disable the original.
let args = fdt::RealmArgs {
driver_disable: Some(vec![
"fuchsia-boot:///dtr#meta/fail-to-start-replacement.cm".to_string()
]),
..Default::default()
};
instance.driver_test_realm_start(args).await?;
let driver_dev = instance.root.connect_to_protocol_at_exposed_dir()?;
let driver_registrar: fdr::DriverRegistrarProxy =
instance.root.connect_to_protocol_at_exposed_dir()?;
// Check that it's quarantined.
let device_infos = get_device_info(&driver_dev, &node_filter, /* exact_match= */ true).await?;
assert_eq!(Some(true), device_infos.first().expect("one node entry").quarantined);
assert_eq!(
Some(failing_url.to_string()),
device_infos.first().expect("one node entry").bound_driver_url
);
// Let's disable the failed driver.
let disable_result = driver_dev.disable_driver(&failing_url, None).await;
if disable_result.is_err() {
return Err(anyhow!("Failed to disable failing_url."));
}
// Now we can restart the first target driver with the rematch flag.
let restart_result =
driver_dev.restart_driver_hosts(failing_url, fdd::RestartRematchFlags::REQUESTED).await?;
if restart_result.is_err() {
return Err(anyhow!("Failed to restart target_1."));
}
// Wait until the node is unbound.
loop {
let device_infos =
get_device_info(&driver_dev, &node_filter, /* exact_match= */ true).await?;
match device_infos.first().expect("one node entry").bound_driver_url.as_deref() {
Some("unbound") => break,
_ => {}
}
}
// Now we can register our replacement.
let register_result = driver_registrar.register(replacemnt_url).await;
match register_result {
Ok(Ok(())) => {}
Ok(Err(err)) => {
return Err(anyhow!("Failed to register replacement: {}.", err));
}
Err(err) => {
return Err(anyhow!("Failed to register replacement: {}.", err));
}
};
// And now that we have registered the replacement we call to bind all available nodes.
let bind_result = driver_dev.bind_all_unbound_nodes2().await;
match bind_result {
Ok(Ok(res)) => {
let binding = res.first().expect("one binding event");
assert_eq!(Some("dev.sys.test".to_string()), binding.node_name);
assert_eq!(Some(replacemnt_url.to_string()), binding.driver_url);
}
Ok(Err(err)) => {
return Err(anyhow!("Failed to bind_all_unbound_nodes: {}.", err));
}
Err(err) => {
return Err(anyhow!("Failed to bind_all_unbound_nodes: {}.", err));
}
};
// Wait until the host koid is not None, which means the replacement is up.
loop {
let device_infos =
get_device_info(&driver_dev, &node_filter, /* exact_match= */ true).await?;
let node = device_infos.first().expect("one node entry");
if node.driver_host_koid.is_some() {
// Ensure its no longer quarantined.
assert_eq!(Some(false), node.quarantined);
assert_eq!(Some(replacemnt_url.to_string()), node.bound_driver_url);
break;
}
}
Ok(())
}