blob: 413d35ad44521ede14fa1e4bd752665719e4088d [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::Error,
fidl_fuchsia_sys as fsys, fuchsia_async as fasync,
fuchsia_component::server::ServiceFs,
fuchsia_component_test::LocalComponentHandles,
futures::{lock::Mutex, prelude::*},
std::collections::HashMap,
std::sync::Arc,
tracing::*,
};
/// A function that instantiates a v1 component, given its `LaunchInfo`.
///
/// This should serve the component's outgoing directory at `LaunchInfo.directory_request`.
pub type CreateComponentFn = Box<dyn FnMut(fsys::LaunchInfo) + Sync + Send + 'static>;
/// A local implementation of a component that serves the `fuchsia.sys.Environment` and
/// `fuchsia.sys.Launcher` protocols used to manage v1 components.
///
/// This is a testing fake for the production appmgr component, used in tests to mock
/// v1 components.
pub struct FakeAppmgr {
mock_components: Mutex<HashMap<String, CreateComponentFn>>,
}
impl FakeAppmgr {
pub fn new(mock_components: HashMap<String, CreateComponentFn>) -> Arc<Self> {
Arc::new(FakeAppmgr { mock_components: Mutex::new(mock_components) })
}
/// Serves a local child component that exposes the `fuchsia.sys.Environment`
/// and `fuchsia.sys.Launcher` protocols.
pub async fn serve_local_child(
self: Arc<Self>,
handles: LocalComponentHandles,
) -> Result<(), Error> {
let mut fs = ServiceFs::new();
let self_ = self.clone();
fs.dir("svc").add_fidl_service(move |stream: fsys::EnvironmentRequestStream| {
let self_ = self_.clone();
fasync::Task::local(async move { self_.serve_environment(stream).await }).detach();
});
let self_ = self.clone();
fs.dir("svc").add_fidl_service(move |stream: fsys::LauncherRequestStream| {
let self_ = self_.clone();
fasync::Task::local(async move { self_.serve_launcher(stream).await }).detach();
});
fs.serve_connection(handles.outgoing_dir)?;
fs.collect::<()>().await;
Ok(())
}
async fn serve_environment(self: Arc<Self>, mut stream: fsys::EnvironmentRequestStream) {
match stream.try_next().await.expect("failed to serve Environment") {
Some(fsys::EnvironmentRequest::CreateNestedEnvironment {
control_handle: _,
environment,
controller,
..
}) => {
let self_ = self.clone();
fasync::Task::local(async move {
let stream =
environment.into_stream().expect("failed to create stream for Environment");
self_.serve_environment(stream).await
})
.detach();
let self_ = self.clone();
fasync::Task::local(async move {
let (stream, ctrl) = controller
.into_stream_and_control_handle()
.expect("failed to create stream for EnvironmentController");
ctrl.send_on_created().expect("failed to send OnCreated");
self_.serve_environment_controller(stream).await
})
.detach();
}
Some(fsys::EnvironmentRequest::GetLauncher { launcher, .. }) => {
let self_ = self.clone();
fasync::Task::local(async move {
let stream =
launcher.into_stream().expect("failed to create stream for Launcher");
self_.serve_launcher(stream).await
})
.detach();
}
None => {}
_ => panic!("unexpected Environment request"),
}
}
async fn serve_environment_controller(
self: Arc<Self>,
mut stream: fsys::EnvironmentControllerRequestStream,
) {
match stream.try_next().await.expect("failed to serve EnvironmentController") {
None => {}
_ => panic!("unexpected EnvironmentController request"),
}
}
async fn serve_launcher(self: Arc<Self>, mut stream: fsys::LauncherRequestStream) {
while let Some(fsys::LauncherRequest::CreateComponent {
launch_info,
controller,
control_handle: _,
}) = stream.try_next().await.expect("failed to serve Launcher")
{
info!("Launching mock v1 component: {}", launch_info.url);
match self.mock_components.lock().await.get_mut(&launch_info.url) {
Some(create_component_fn) => create_component_fn(launch_info),
None => panic!("unexpected v1 component launched: {}", launch_info.url),
}
let (mut controller_stream, ctrl) = controller
.unwrap()
.into_stream_and_control_handle()
.expect("failed to create stream of ComponentController requests");
fasync::Task::spawn(async move {
if let Some(request) = controller_stream.try_next().await.unwrap() {
panic!("Unexpected ComponentController request: {:?}", request);
}
})
.detach();
ctrl.send_on_directory_ready().expect("failed to send OnDirectoryReady");
}
}
}