blob: 52344dc0f26ef13d12a8df4dfbd4ea2f57c15efd [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.
use {
crate::test_suite::handle_suite_requests,
anyhow::{anyhow, Error},
fidl::endpoints::ServerEnd,
fidl_fuchsia_component_runner as fcrunner, fidl_fuchsia_data as fdata,
fidl_fuchsia_test as ftest, fuchsia_async as fasync,
fuchsia_component::server::ServiceFs,
fuchsia_zircon as zx,
futures::{StreamExt, TryStreamExt},
runner::component::ComponentNamespace,
std::convert::TryFrom,
};
/// The services exposed in the outgoing directory for the test component.
enum TestComponentExposedServices {
Suite(ftest::SuiteRequestStream),
}
/// Handles a `fcrunner::ComponentRunnerRequestStream`.
///
/// When a run request arrives, the test suite protocol is served in the test component's outgoing
/// namespace, and then the component is run in response to `ftest::SuiteRequest::Run` requests.
///
/// See `test_suite` for more on how the test suite requests are handled.
pub async fn handle_runner_requests(
mut request_stream: fcrunner::ComponentRunnerRequestStream,
) -> Result<(), Error> {
while let Some(event) = request_stream.try_next().await? {
match event {
fcrunner::ComponentRunnerRequest::Start { start_info, controller, .. } => {
let outgoing_dir_channel = start_info
.outgoing_dir
.ok_or(anyhow!("Missing outgoing directory."))?
.into_channel();
let component_url =
start_info.resolved_url.ok_or(anyhow!("Missing resolved URL."))?;
let program = start_info.program.ok_or(anyhow!("Missing program information."))?;
let namespace = ComponentNamespace::try_from(
start_info.ns.ok_or(anyhow!("Missing namespace."))?,
)?;
fasync::Task::local(async move {
match serve_test_suite(
&component_url,
program,
outgoing_dir_channel,
namespace,
controller,
)
.await
{
Ok(_) => tracing::info!("Finished serving test suite for component."),
Err(e) => tracing::error!("Error serving test suite: {:?}", e),
}
})
.detach();
}
}
}
Ok(())
}
/// Serves a `ftest::SuiteRequestStream` from `directory_channel`.
///
/// This function is used to serve a `ftest::SuiteRequestStream` in the outgoing directory of a test
/// component. This is what the test framework connects to to run test cases.
///
/// When the returned future completes, the outgoing directory has finished serving requests.
///
/// # Parameters
/// - `test_url`: The URL of the test component.
/// - `program`: The program data associated with the run request for the test component.
/// - `outgoing_dir_channel`: The channel for the directory to serve the test suite protocol from.
/// - `namespace`: The incoming namespace to provide to the test component.
/// - `controller`: The component controller associated with the test component.
async fn serve_test_suite(
test_url: &str,
program: fdata::Dictionary,
outgoing_dir_channel: fidl::Channel,
namespace: ComponentNamespace,
controller: ServerEnd<fcrunner::ComponentControllerMarker>,
) -> Result<(), Error> {
let test_url = test_url.to_string();
let mut outgoing_dir = ServiceFs::new_local();
outgoing_dir.dir("svc").add_fidl_service(TestComponentExposedServices::Suite);
outgoing_dir.serve_connection(outgoing_dir_channel)?;
if let Some(service_request) = outgoing_dir.next().await {
match service_request {
TestComponentExposedServices::Suite(stream) => {
let test_url = test_url.to_string();
let program = program.clone();
match handle_suite_requests(&test_url, Some(program), namespace.clone(), stream)
.await
{
Ok(_) => tracing::info!("Finished serving test suite requests."),
Err(e) => {
tracing::error!("Error serving test suite requests: {:?}", e)
}
}
let _ = controller.close_with_epitaph(zx::Status::OK);
}
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use {
super::*,
fidl::endpoints::{create_proxy, DiscoverableProtocolMarker},
fidl_fuchsia_component_runner::ComponentControllerMarker,
fidl_fuchsia_io as fio, fidl_fuchsia_test as ftest, fuchsia_async as fasync,
};
async fn list_directory(root_proxy: &fio::DirectoryProxy) -> Vec<String> {
let entries = fuchsia_fs::directory::readdir(&root_proxy)
.await
.expect("Couldn't read listed directory.");
let items = entries.into_iter().map(|entry| entry.name).collect::<Vec<String>>();
items
}
#[fasync::run_singlethreaded(test)]
async fn test_serving_test_suite_in_svc() {
let (directory_proxy, directory_server) =
create_proxy::<fio::DirectoryMarker>().expect("Couldn't create case iterator.");
let (_, controller) = create_proxy::<ComponentControllerMarker>().unwrap();
fasync::Task::local(async move {
let _ = serve_test_suite(
"test",
fdata::Dictionary { entries: None, ..fdata::Dictionary::EMPTY },
directory_server.into_channel(),
ComponentNamespace::try_from(vec![]).unwrap(),
controller,
)
.await;
})
.detach();
let svc_path = std::path::Path::new("svc");
let svc_dir =
fuchsia_fs::open_directory(&directory_proxy, svc_path, fio::OpenFlags::RIGHT_READABLE)
.expect("Couldn't open svc.");
let svc_contents = list_directory(&svc_dir).await;
assert_eq!(svc_contents, vec![ftest::SuiteMarker::PROTOCOL_NAME]);
}
}