blob: 836077d9f9c0d8217183139d98477267d9604b8d [file] [log] [blame]
// Copyright 2023 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::{Context, Result};
use fidl::endpoints::ServiceMarker;
use fuchsia_async::{self as fasync, DurationExt, Timer};
use fuchsia_component::client;
use fuchsia_component_test::RealmBuilder;
use fuchsia_driver_test::{DriverTestRealmBuilder, DriverTestRealmInstance};
use fuchsia_zircon::DurationNum;
use {
fidl_fuchsia_crashdriver_test as fcdt, fidl_fuchsia_driver_development as fdd,
fidl_fuchsia_driver_test as fdt,
};
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)
}
async fn wait_for_instance(realm: &fuchsia_component_test::RealmInstance) -> Result<String> {
let instance;
let service = client::open_service_at_dir::<fcdt::DeviceMarker>(realm.root.get_exposed_dir())
.context("Failed to open service")?;
loop {
// TODO(https://fxbug.dev/42124541): Once component manager supports watching for
// service instances, this loop shousld be replaced by a watcher.
let entries = fuchsia_fs::directory::readdir(&service)
.await
.context("Failed to read service instances")?;
if let Some(entry) = entries.iter().next() {
instance = entry.name.clone();
break;
}
Timer::new(100.millis().after_now()).await;
}
return Ok(instance);
}
#[fasync::run_singlethreaded(test)]
async fn test_restart_on_crash() -> Result<()> {
// Create the RealmBuilder.
let builder = RealmBuilder::new().await?;
builder.driver_test_realm_setup().await?;
builder.driver_test_realm_add_expose::<fcdt::DeviceMarker>().await?;
// Build the Realm.
let realm = builder.build().await?;
let exposes = vec![fdt::Expose {
service_name: fcdt::DeviceMarker::SERVICE_NAME.to_string(),
collection: fdt::Collection::PackageDrivers,
}];
// Start the DriverTestRealm.
let args = fdt::RealmArgs {
root_driver: Some("#meta/crasher.cm".to_string()),
exposes: Some(exposes),
..Default::default()
};
realm.driver_test_realm_start(args).await?;
// Find an instance of the `Device` service.
let instance = wait_for_instance(&realm).await?;
let driver_dev = realm.root.connect_to_protocol_at_exposed_dir::<fdd::ManagerMarker>()?;
let device_infos = get_device_info(&driver_dev, &[], /* exact_match= */ true).await?;
assert_eq!(1, device_infos.len());
let driver_host_koid_1 = device_infos[0].driver_host_koid;
// Connect to the `Device` service.
let device = client::connect_to_service_instance_at_dir::<fcdt::DeviceMarker>(
realm.root.get_exposed_dir(),
&instance,
)
.context("Failed to open service")?;
let crasher = device.connect_to_crasher()?;
let pong_1 = crasher.ping().await?;
// CRASH
crasher.crash()?;
// Wait until the node comes back with a new host.
let mut driver_host_koid_2: Option<u64>;
loop {
let device_infos = get_device_info(&driver_dev, &[], /* exact_match= */ true).await?;
assert_eq!(1, device_infos.len());
driver_host_koid_2 = device_infos[0].driver_host_koid;
if driver_host_koid_2.is_some() && driver_host_koid_2 != driver_host_koid_1 {
break;
}
Timer::new(100.millis().after_now()).await;
}
assert_ne!(driver_host_koid_1, driver_host_koid_2);
// Connect to the new one.
let instance = wait_for_instance(&realm).await?;
let device = client::connect_to_service_instance_at_dir::<fcdt::DeviceMarker>(
realm.root.get_exposed_dir(),
&instance,
)
.context("Failed to open service")?;
let crasher = device.connect_to_crasher()?;
// Check that it is able to communicate with the new one.
let pong_2 = crasher.ping().await?;
assert_ne!(pong_1, pong_2);
Ok(())
}