blob: 18d8483142fdb9d901e42a0fb39aa94f0708e84c [file] [log] [blame] [edit]
// Copyright 2019 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::{format_err, Error},
argh::FromArgs,
fidl::encoding::Decodable,
fidl_fuchsia_session::{ElementManagerMarker, ElementManagerProxy, ElementSpec},
fidl_fuchsia_session::{
LaunchConfiguration, LauncherMarker, LauncherProxy, RestarterMarker, RestarterProxy,
},
fuchsia_async as fasync,
fuchsia_component::client::connect_to_service,
};
#[derive(FromArgs, Debug, PartialEq)]
/// Various operations to control sessions.
pub struct Args {
#[argh(subcommand)]
pub command: Command,
}
#[derive(FromArgs, Debug, PartialEq)]
#[argh(subcommand)]
pub enum Command {
Launch(LaunchCommand),
Restart(RestartCommand),
Add(AddCommand),
}
#[derive(FromArgs, Debug, PartialEq)]
#[argh(subcommand, name = "launch")]
/// Launch a new session.
pub struct LaunchCommand {
/// the URL for the session to launch.
#[argh(positional)]
pub session_url: String,
}
#[derive(FromArgs, Debug, PartialEq)]
#[argh(subcommand, name = "restart")]
/// Restart the current session.
pub struct RestartCommand {}
#[derive(FromArgs, Debug, PartialEq)]
#[argh(subcommand, name = "add")]
/// Add an element to the current session.
pub struct AddCommand {
/// the URL for the element to add.
#[argh(positional)]
pub element_url: String,
}
#[fasync::run_singlethreaded]
async fn main() -> Result<(), Error> {
let Args { command } = argh::from_env();
match command {
Command::Launch(LaunchCommand { session_url }) => {
let launcher = connect_to_service::<LauncherMarker>()?;
match launch_session(&session_url, launcher).await {
Ok(_) => {
println!("Launched session: {:?}", session_url);
Ok(())
}
Err(err) => {
println!("Failed to launch session: {:?}, {:?}", session_url, err);
Err(err)
}
}
}
Command::Restart(RestartCommand {}) => {
let restarter = connect_to_service::<RestarterMarker>()?;
match restart_session(restarter).await {
Ok(_) => {
println!("Restarted the session.");
Ok(())
}
Err(err) => {
println!("Failed to restart session: {:?}", err);
Err(err)
}
}
}
Command::Add(AddCommand { element_url }) => {
let element_manager = connect_to_service::<ElementManagerMarker>()?;
match add_element(&element_url, element_manager).await {
Ok(_) => {
println!("Added element: {:?}", element_url);
Ok(())
}
Err(err) => {
println!("Failed to add element: {:?}, {:?}", element_url, err);
Err(err)
}
}
}
}
}
/// Launches a session.
///
/// # Parameters
/// - `session_url`: The URL of the session to launch.
/// - `launcher`: The launcher proxy to use to launch the session.
///
/// # Errors
/// Returns an error if there is an issue launching the session.
async fn launch_session(session_url: &str, launcher: LauncherProxy) -> Result<(), Error> {
let result = launcher
.launch(LaunchConfiguration {
session_url: Some(session_url.to_string()),
..LaunchConfiguration::EMPTY
})
.await?;
result.map_err(|err: fidl_fuchsia_session::LaunchError| format_err!("{:?}", err))?;
Ok(())
}
/// Restarts a session.
///
/// # Parameters
/// - `restarter`: The Restarter proxy to use to restart the session.
///
/// # Errors
/// Returns an error if there is either an issue launching the session or
/// there isn't a previous session to be restarted.
async fn restart_session(restarter: RestarterProxy) -> Result<(), Error> {
let result = restarter.restart().await?;
result.map_err(|err: fidl_fuchsia_session::RestartError| format_err!("{:?}", err))?;
Ok(())
}
/// Adds an element to the current session.
///
/// # Parameters
/// - `element_url`: The URL of the element to add.
/// - `element_manager`: The ElementManager to use when proposing the element.
///
/// # Errors
/// Returns an error if there is an issue adding the element.
async fn add_element(element_url: &str, element_manager: ElementManagerProxy) -> Result<(), Error> {
let spec =
ElementSpec { component_url: Some(element_url.to_string()), ..ElementSpec::new_empty() };
let result = element_manager.propose_element(spec, None).await?;
result.map_err(|err: fidl_fuchsia_session::ProposeElementError| format_err!("{:?}", err))?;
Ok(())
}
#[cfg(test)]
mod tests {
use {
super::*,
fidl::endpoints::create_proxy_and_stream,
fidl_fuchsia_session::{
ElementManagerRequest, LaunchError, LauncherMarker, ProposeElementError, RestartError,
RestarterMarker,
},
// fidl_fuchsia.sys2::EventType, TODO(fxbug.dev/47730): re-enable the tests.
fuchsia_async as fasync,
futures::TryStreamExt,
// test_utils_lib::events::{EventSource, Ordering, RecordedEvent},
};
/// Tests that the session_control tool successfully handles a call to LaunchSession with the provided URL.
#[fasync::run_singlethreaded(test)]
async fn test_launch_session() {
let (launcher, mut launcher_server) =
create_proxy_and_stream::<LauncherMarker>().expect("Failed to create Launcher FIDL.");
let session_url = "test_session";
let _server_task = fasync::Task::spawn(async move {
let launch_request =
launcher_server.try_next().await.expect("FIDL Error").expect("Stream terminated");
let (configuration, responder) =
launch_request.into_launch().expect("Failed to unwrap launch request");
assert_eq!(configuration.session_url, Some(session_url.to_string()));
let _ = responder.send(&mut Ok(()));
});
assert!(launch_session(&session_url, launcher).await.is_ok());
}
/// Tests that the session_control tool returns an error on a call to LaunchSession when there
/// is no LaunchConfiguration provided.
#[fasync::run_singlethreaded(test)]
async fn test_launch_session_error() {
let (launcher, mut launcher_server) =
create_proxy_and_stream::<LauncherMarker>().expect("Failed to create Launcher FIDL.");
let session_url = "test_session";
let _server_task = fasync::Task::spawn(async move {
let launch_request =
launcher_server.try_next().await.expect("FIDL Error").expect("Stream terminated");
let (_, responder) =
launch_request.into_launch().expect("Failed to unwrap launch request");
let _ = responder.send(&mut Err(LaunchError::NotFound));
});
assert!(launch_session(&session_url, launcher).await.is_err());
}
/// Tests that restart_session makes the appropriate calls to fuchsia.session.Launcher.
#[fasync::run_singlethreaded(test)]
async fn test_restart_session() {
let (restarter, mut restarter_server) =
create_proxy_and_stream::<RestarterMarker>().expect("Failed to create Restarter FIDL.");
let _server_task = fasync::Task::spawn(async move {
let restarter_request =
restarter_server.try_next().await.expect("FIDL Error").expect("Stream terminated");
let responder =
restarter_request.into_restart().expect("Failed to unwrap restarter request");
let _ = responder.send(&mut Ok(()));
});
assert!(restart_session(restarter).await.is_ok());
}
/// Tests that restart_session returns an error when an error is returned from fuchsia.session.Restarter.
#[fasync::run_singlethreaded(test)]
async fn test_restart_session_error() {
let (restarter, mut restarter_server) =
create_proxy_and_stream::<RestarterMarker>().expect("Failed to create Restarter FIDL.");
let _server_task = fasync::Task::spawn(async move {
let restarter_request =
restarter_server.try_next().await.expect("FIDL Error").expect("Stream terminated");
let responder =
restarter_request.into_restart().expect("Failed to unwrap restarter request");
let _ = responder.send(&mut Err(RestartError::NotFound));
});
assert!(restart_session(restarter).await.is_err());
}
/// Tests that an element is added to the session
#[fasync::run_singlethreaded(test)]
async fn test_add_element() {
let (proxy, mut server) = create_proxy_and_stream::<ElementManagerMarker>()
.expect("Failed to create ElementManager FIDL.");
let element_url = "test_element";
let _server_task = fasync::Task::spawn(async move {
let propose_request =
server.try_next().await.expect("FIDL Error").expect("Stream terminated");
let ElementManagerRequest::ProposeElement { spec, responder, .. } = propose_request;
assert_eq!(spec.component_url, Some(element_url.to_string()));
let _ = responder.send(&mut Ok(()));
});
assert!(add_element(element_url, proxy).await.is_ok());
}
/// Tests that an element is added to the session
#[fasync::run_singlethreaded(test)]
async fn test_add_element_error() {
let (proxy, mut server) = create_proxy_and_stream::<ElementManagerMarker>()
.expect("Failed to create ElementManager FIDL.");
let element_url = "test_element";
let _server_task = fasync::Task::spawn(async move {
let propose_request =
server.try_next().await.expect("FIDL Error").expect("Stream terminated");
let ElementManagerRequest::ProposeElement { responder, .. } = propose_request;
let _ = responder.send(&mut Err(ProposeElementError::Rejected));
});
assert!(add_element(element_url, proxy).await.is_err());
}
// TODO(fxbug.dev/47730): re-enable these tests.
// /// Verifies that session control is routed the expected capabilities.
// #[fasync::run_singlethreaded(test)]
// async fn test_capability_routing() {
// let event_source = EventSource::new().unwrap();
// event_source.start_component_tree().await.unwrap();
// let launcher = connect_to_service::<LauncherMarker>().expect("launcher service?");
// let expected_events = vec![
// RecordedEvent {
// event_type: EventType::CapabilityRouted,
// target_moniker: "./session:session:*".to_string(),
// capability_id: Some("elf".to_string()),
// },
// RecordedEvent {
// event_type: EventType::CapabilityRouted,
// target_moniker: "./session:session:*".to_string(),
// capability_id: Some("/svc/fuchsia.session.Launcher".to_string()),
// },
// ];
// let expectation =
// event_source.expect_events(Ordering::Unordered, expected_events).await.unwrap();
// let session_url = "fuchsia-pkg://fuchsia.com/session_control#meta/session_control.cm";
// match launch_session(&session_url, launcher).await {
// Ok(_) => fx_log_info!("Launched session: {:?}", session_url),
// Err(err) => fx_log_info!("Failed to launch session: {:?}, {:?}", session_url, err),
// };
// expectation.await.unwrap();
// }
// /// Verifies that the session is correctly resolved and launched without errors.
// #[fasync::run_singlethreaded(test)]
// async fn test_session_lifecycle() {
// let event_source = EventSource::new().unwrap();
// event_source.start_component_tree().await.unwrap();
// let launcher = connect_to_service::<LauncherMarker>().expect("launcher service?");
// let expected_events = vec![
// RecordedEvent {
// event_type: EventType::Resolved,
// target_moniker: "./session:session:*".to_string(),
// capability_id: None,
// },
// RecordedEvent {
// event_type: EventType::Started,
// target_moniker: "./session:session:*".to_string(),
// capability_id: None,
// },
// ];
// let expectation =
// event_source.expect_events(Ordering::Ordered, expected_events).await.unwrap();
// let session_url = "fuchsia-pkg://fuchsia.com/session_control#meta/session_control.cm";
// match launch_session(&session_url, launcher).await {
// Ok(_) => println!("Launched session: {:?}", session_url),
// Err(err) => println!("Failed to launch session: {:?}, {:?}", session_url, err),
// };
// expectation.await.unwrap();
// }
}