blob: 1e49c1dd3cc813667e9c3a4a7601fc5e28ae8c81 [file] [log] [blame] [edit]
// Copyright 2022 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, Result},
direct_mode_vmm::{start, take_direct_vdso},
fidl::endpoints::{create_proxy, ClientEnd, ServerEnd},
fidl_fuchsia_test as ftest, fuchsia_async as fasync,
fuchsia_component::server::ServiceFs,
fuchsia_runtime::{HandleInfo, HandleType},
fuchsia_zircon::{HandleBased, Rights, Socket, SocketOpts, Vmo},
futures::prelude::*,
process_builder::StartupHandle,
std::ffi::CString,
tracing::error,
};
/// Trivial service host that just launches a restricted and unrestricted
/// protocol that both return a trivial string.
#[fasync::run_singlethreaded]
async fn main() {
let mut fs = ServiceFs::new_local();
fs.dir("svc").add_fidl_service(move |stream| {
fasync::Task::local(
serve_suite(stream).unwrap_or_else(|_| error!("Failed to serve suite")),
)
.detach();
});
fs.take_and_serve_directory_handle().expect("Failed to serve outgoing dir");
fs.collect::<()>().await;
}
/// Trivial service that just returns the value "restricted"
async fn serve_suite(mut stream: ftest::SuiteRequestStream) -> Result<()> {
while let Some(request) = stream.try_next().await? {
match request {
ftest::SuiteRequest::GetTests { iterator, .. } => fasync::Task::spawn(
serve_case_iterator(iterator)
.unwrap_or_else(|_| error!("Failed to serve case iterator")),
)
.detach(),
ftest::SuiteRequest::Run { tests, listener, .. } => run_tests(tests, listener).await?,
}
}
Ok(())
}
async fn serve_case_iterator(iterator: ServerEnd<ftest::CaseIteratorMarker>) -> Result<()> {
let mut stream = iterator.into_stream()?;
let mut cases = vec![
ftest::Case {
name: Some("DirectMode.Kernel".to_string()),
enabled: Some(true),
..ftest::Case::EMPTY
},
ftest::Case {
name: Some("DirectMode.User".to_string()),
enabled: Some(true),
..ftest::Case::EMPTY
},
]
.into_iter();
while let Some(event) = stream.try_next().await? {
match event {
ftest::CaseIteratorRequest::GetNext { responder } => responder.send(&mut cases)?,
}
}
Ok(())
}
async fn run_tests(
tests: Vec<ftest::Invocation>,
listener: ClientEnd<ftest::RunListenerMarker>,
) -> Result<()> {
let vdso_vmo = take_direct_vdso();
let listener = listener.into_proxy()?;
for test in tests {
let (case_listener, case_listener_server) = create_proxy::<ftest::CaseListenerMarker>()?;
let (stderr, stderr_server) = Socket::create(SocketOpts::STREAM)?;
let std_handles =
ftest::StdHandles { err: Some(stderr_server), ..ftest::StdHandles::EMPTY };
let name = test.name.clone().unwrap_or_default();
listener.on_test_case_started(test, std_handles, case_listener_server)?;
let vdso_vmo = vdso_vmo.duplicate_handle(Rights::SAME_RIGHTS)?;
let handles = vec![StartupHandle {
handle: stderr.duplicate_handle(Rights::SAME_RIGHTS)?.into_handle(),
info: HandleInfo::new(HandleType::FileDescriptor, 2),
}];
let result = match name.as_str() {
"DirectMode.Kernel" => kernel_test(vdso_vmo).await,
"DirectMode.User" => user_test(vdso_vmo, handles).await,
_ => Err(anyhow!("Unknown test")),
};
let status = if let Err(e) = result {
stderr.write(e.to_string().as_bytes())?;
ftest::Status::Failed
} else {
ftest::Status::Passed
};
let result = ftest::Result_ { status: Some(status), ..ftest::Result_::EMPTY };
case_listener.finished(result)?;
}
listener.on_finished()?;
Ok(())
}
// Test the behaviour of direct mode binary that acts as a kernel.
async fn kernel_test(vdso_vmo: Vmo) -> Result<()> {
let args = vec![CString::new("/pkg/bin/kernel")?];
start(vdso_vmo, args, vec![], vec![], vec![])
.await
.map_or(Ok(()), |_| Err(anyhow!("Expected failure")))
}
// Test the behaviour of direct mode binary that acts as an application.
async fn user_test(vdso_vmo: Vmo, handles: Vec<StartupHandle>) -> Result<()> {
let args = vec![CString::new("/pkg/bin/user")?];
start(vdso_vmo, args, vec![], vec![], handles).await
}