blob: e2e419d56f49306fff121e8eaef80288ed0d8fbc [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;
use anyhow::*;
use fidl::endpoints::{create_proxy, ServerEnd};
use fuchsia_component::server::ServiceFs;
use futures::prelude::*;
use test::StressTest;
use tracing::{error, info};
use {
fidl_fuchsia_component_runner as fcrunner, fidl_fuchsia_io as fio, fidl_fuchsia_test as ftest,
fuchsia_async as fasync, fuchsia_zircon as zx,
};
#[fuchsia::main]
async fn main() -> Result<()> {
let mut fs = ServiceFs::new();
fs.dir("svc").add_fidl_service(move |stream| {
fasync::Task::spawn(serve_runner(stream).map(|r| info!("Serving runner: {:?}", r)))
.detach();
});
fs.take_and_serve_directory_handle()?;
fs.collect::<()>().await;
Ok(())
}
async fn serve_runner(mut stream: fcrunner::ComponentRunnerRequestStream) -> Result<()> {
while let Some(fcrunner::ComponentRunnerRequest::Start { start_info, controller, .. }) =
stream.try_next().await?
{
let controller_stream = controller.into_stream()?;
fasync::Task::spawn(
serve_controller(controller_stream).map(|r| info!("Serving controller: {:?}", r)),
)
.detach();
let dictionary = start_info.program.ok_or(anyhow!("No program dictionary"))?;
let dictionary = dictionary.entries.ok_or(anyhow!("No entries in program dictionary"))?;
let namespace = start_info.ns.ok_or(anyhow!("No incoming namespace"))?;
let test = StressTest::new(dictionary, namespace)?;
let out_dir = start_info.outgoing_dir.ok_or(anyhow!("No outgoing directory"))?;
fasync::Task::spawn(
serve_out_dir(out_dir, test).map(|r| info!("Serving out dir: {:?}", r)),
)
.detach();
}
Ok(())
}
/// TODO(xbhatnag): All futures should be aborted when a Stop/Kill request is received.
async fn serve_controller(mut stream: fcrunner::ComponentControllerRequestStream) -> Result<()> {
if let Some(request) = stream.try_next().await? {
info!("Received controller request: {:?}", request);
}
Ok(())
}
async fn serve_out_dir(out_dir: ServerEnd<fio::DirectoryMarker>, test: StressTest) -> Result<()> {
let mut fs = ServiceFs::new();
fs.dir("svc").add_fidl_service(move |stream| {
fasync::Task::spawn(
serve_test_suite(stream, test.clone()).map(|r| info!("Serving test suite: {:?}", r)),
)
.detach();
});
fs.serve_connection(out_dir)?;
fs.collect::<()>().await;
Ok(())
}
/// Implements `fuchsia.test.Suite` service and runs test.
async fn serve_test_suite(mut stream: ftest::SuiteRequestStream, test: StressTest) -> Result<()> {
while let Some(event) = stream.try_next().await? {
match event {
ftest::SuiteRequest::GetTests { iterator, control_handle: _ } => {
fasync::Task::spawn(
serve_case_iterator(iterator).map(|r| info!("Serving case iterator: {:?}", r)),
)
.detach();
}
ftest::SuiteRequest::Run { tests, listener, .. } => {
let listener = listener.into_proxy()?;
for invocation in tests {
let (case_listener, server_end) = create_proxy::<ftest::CaseListenerMarker>()?;
// TODO(84852): Use stderr to print status of actors
let (stderr_client, stderr_server) = zx::Socket::create_datagram();
let std_handles = ftest::StdHandles {
out: None,
err: Some(stderr_server),
..Default::default()
};
listener.on_test_case_started(&invocation, std_handles, server_end)?;
let result = if let Err(e) = test.clone().start().await {
if let Err(status) = stderr_client.write(e.to_string().as_bytes()) {
error!(
"Failed to write error to stderr socket [write status={}][error={:?}]",
status, e
)
}
ftest::Result_ { status: Some(ftest::Status::Failed), ..Default::default() }
} else {
ftest::Result_ { status: Some(ftest::Status::Passed), ..Default::default() }
};
case_listener.finished(&result)?;
}
listener.on_finished()?;
}
}
}
Ok(())
}
async fn serve_case_iterator(iterator: ServerEnd<ftest::CaseIteratorMarker>) -> Result<()> {
let mut stream = iterator.into_stream()?;
let cases = &[ftest::Case {
name: Some("stress_test".to_string()),
enabled: Some(true),
..Default::default()
}];
// Send the `stress_test` case in the first call
if let Some(ftest::CaseIteratorRequest::GetNext { responder }) = stream.try_next().await? {
responder.send(cases)?;
}
// Send an empty response in the second call. This instructs the test_manager that there are
// no more cases in this test suite.
if let Some(ftest::CaseIteratorRequest::GetNext { responder }) = stream.try_next().await? {
responder.send(&[])?;
}
Ok(())
}