blob: c729d95a5ddb3fe8d5aff3ce0018df7af4ac59fc [file] [log] [blame]
// 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::{Context, Result},
ffx_component::SELECTOR_FORMAT_HELP,
ffx_core::{ffx_error, ffx_plugin},
ffx_knock_args::KnockCommand,
fidl::handle::Channel,
fidl_fuchsia_developer_remotecontrol as rc, fuchsia_zircon_status as zx_status, selectors,
std::io::{stdout, Write},
};
#[ffx_plugin()]
pub async fn knock_cmd(remote_proxy: rc::RemoteControlProxy, cmd: KnockCommand) -> Result<()> {
let writer = Box::new(stdout());
knock(remote_proxy, writer, &cmd.selector).await
}
async fn knock<W: Write>(
remote_proxy: rc::RemoteControlProxy,
mut write: W,
selector: &str,
) -> Result<()> {
let writer = &mut write;
let selector = selectors::parse_selector(selector).map_err(|e| {
ffx_error!("Invalid selector '{}': {}\n{}", selector, e, SELECTOR_FORMAT_HELP)
})?;
let (client, server) = Channel::create()?;
match remote_proxy.connect(selector, server).await.context("awaiting connect call")? {
Ok(m) => {
match client.read_split(&mut vec![], &mut vec![]) {
Err(zx_status::Status::PEER_CLOSED) => writeln!(
writer,
"Failure: service is not up. Connection to '{}:{}:{}' reported PEER_CLOSED.",
m.moniker.join("/"),
m.subdir,
m.service
)?,
Err(zx_status::Status::SHOULD_WAIT) => writeln!(
writer,
"Success: service is up. Connected to '{}:{}:{}'.",
m.moniker.join("/"),
m.subdir,
m.service
)?,
Err(e) => writeln!(
writer,
"Unknown: opened connection to '{}:{}:{}', but channel read reported {:?}.",
m.moniker.join("/"),
m.subdir,
m.service,
e
)?,
_ => writeln!(
writer,
"Success: service is up. Connected to '{}:{}:{}'.",
m.moniker.join("/"),
m.subdir,
m.service
)?,
};
Ok(())
}
Err(e) => {
writeln!(writer, "Failed to connect to service: {:?}", e)?;
Ok(())
}
}
}
////////////////////////////////////////////////////////////////////////////////
// tests
#[cfg(test)]
mod test {
use {
super::*,
fidl::endpoints::RequestStream,
fidl::handle::AsyncChannel,
fidl_fuchsia_developer_bridge::{DaemonRequest, DaemonRequestStream},
futures::TryStreamExt,
std::io::BufWriter,
};
fn setup_fake_daemon_service(mut stream: DaemonRequestStream) {
fuchsia_async::Task::spawn(async move {
while let Ok(Some(req)) = stream.try_next().await {
match req {
DaemonRequest::EchoString { value, responder } => {
responder.send(&value).unwrap();
}
_ => assert!(false),
}
// We should only get one request per stream. We want subsequent calls to fail if more are
// made.
break;
}
})
.detach();
}
fn setup_fake_remote_server(connect_chan: bool) -> rc::RemoteControlProxy {
setup_fake_remote_proxy(move |req| match req {
rc::RemoteControlRequest::Connect { selector: _, service_chan, responder } => {
if connect_chan {
setup_fake_daemon_service(DaemonRequestStream::from_channel(
AsyncChannel::from_channel(service_chan).unwrap(),
));
}
let _ = responder
.send(&mut Ok(rc::ServiceMatch {
moniker: vec![String::from("core"), String::from("test")],
subdir: String::from("out"),
service: String::from("fuchsia.myservice"),
}))
.unwrap();
}
_ => assert!(false, "got unexpected {:?}", req),
})
}
#[fuchsia_async::run_singlethreaded(test)]
async fn test_knock_invalid_selector() -> Result<()> {
let mut output = String::new();
let writer = unsafe { BufWriter::new(output.as_mut_vec()) };
let remote_proxy = setup_fake_remote_server(false);
let response = knock(remote_proxy, writer, "a:b:").await;
let e = response.unwrap_err();
assert!(e.to_string().contains(SELECTOR_FORMAT_HELP));
Ok(())
}
#[fuchsia_async::run_singlethreaded(test)]
async fn test_knock_working_service() -> Result<()> {
let mut output = String::new();
let writer = unsafe { BufWriter::new(output.as_mut_vec()) };
let remote_proxy = setup_fake_remote_server(true);
let _response = knock(remote_proxy, writer, "*:*:*").await.expect("knock should not fail");
assert!(output.contains("Success"));
assert!(output.contains("core/test:out:fuchsia.myservice"));
Ok(())
}
#[fuchsia_async::run_singlethreaded(test)]
async fn test_knock_no_service_connected() -> Result<()> {
let mut output = String::new();
let writer = unsafe { BufWriter::new(output.as_mut_vec()) };
let remote_proxy = setup_fake_remote_server(false);
let _response = knock(remote_proxy, writer, "*:*:*").await.expect("knock should not fail");
assert!(!output.contains("Success"));
assert!(output.contains("Failure"));
assert!(output.contains("core/test:out:fuchsia.myservice"));
Ok(())
}
}