blob: 666fd79f71612095a30daa9fec1b06f6a7380e3e [file] [log] [blame]
// Copyright 2020 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 {
crate::target_formatter::TargetFormatter,
anyhow::{anyhow, Result},
ffx_core::ffx_plugin,
ffx_list_args::ListCommand,
fidl_fuchsia_developer_bridge as bridge,
std::convert::TryFrom,
std::io::{stdout, Write},
};
mod target_formatter;
#[ffx_plugin()]
pub async fn list_targets(daemon_proxy: bridge::DaemonProxy, cmd: ListCommand) -> Result<()> {
list_impl(daemon_proxy, cmd, Box::new(stdout())).await
}
async fn list_impl<W: Write>(
daemon_proxy: bridge::DaemonProxy,
cmd: ListCommand,
mut writer: W,
) -> Result<()> {
match daemon_proxy
.list_targets(match cmd.nodename {
Some(ref t) => t,
None => "",
})
.await
{
Ok(r) => {
match r.len() {
0 => {
writeln!(writer, "No devices found.")?;
}
_ => {
let formatter = TargetFormatter::try_from(r)
.map_err(|e| anyhow!("target malformed: {:?}", e))?;
let v: Option<ffx_config::Value> = ffx_config::get("target.default").await.ok();
let default = v.as_ref().map(|s| s.as_str()).flatten();
writeln!(writer, "{}", formatter.lines(default).join("\n"))?;
}
};
Ok(())
}
Err(e) => {
eprintln!("ERROR: {:?}", e);
Err(anyhow!("Error listing targets: {:?}", e))
}
}
}
///////////////////////////////////////////////////////////////////////////////
// tests
#[cfg(test)]
mod test {
use {
super::*,
ffx_daemon::target::TargetAddr,
fidl_fuchsia_developer_bridge::{
DaemonRequest, RemoteControlState, Target as FidlTarget, TargetState, TargetType,
},
regex::Regex,
std::io::BufWriter,
std::net::IpAddr,
};
fn to_fidl_target(nodename: String) -> FidlTarget {
let addr: TargetAddr =
(IpAddr::from([0xfe80, 0x0, 0x0, 0x0, 0xdead, 0xbeef, 0xbeef, 0xbeef]), 3).into();
FidlTarget {
nodename: Some(nodename),
addresses: Some(vec![addr.into()]),
age_ms: Some(101),
rcs_state: Some(RemoteControlState::Up),
target_type: Some(TargetType::Unknown),
target_state: Some(TargetState::Unknown),
}
}
fn setup_fake_daemon_server(num_tests: usize) -> bridge::DaemonProxy {
setup_fake_daemon_proxy(move |req| match req {
DaemonRequest::ListTargets { value, responder } => {
let fidl_values: Vec<FidlTarget> = if value.is_empty() {
(0..num_tests)
.map(|i| format!("Test {}", i))
.map(|name| to_fidl_target(name))
.collect()
} else {
(0..num_tests)
.map(|i| format!("Test {}", i))
.filter(|t| *t == value)
.map(|name| to_fidl_target(name))
.collect()
};
responder.send(&mut fidl_values.into_iter().by_ref().take(512)).unwrap();
}
_ => assert!(false),
})
}
async fn run_list_test(num_tests: usize, cmd: ListCommand) -> String {
let mut output = String::new();
let writer = unsafe { BufWriter::new(output.as_mut_vec()) };
let proxy = setup_fake_daemon_server(num_tests);
let result = list_impl(proxy, cmd, writer).await.unwrap();
assert_eq!(result, ());
output
}
#[fuchsia_async::run_singlethreaded(test)]
async fn test_list_with_no_devices_and_no_nodename() -> Result<()> {
let output = run_list_test(0, ListCommand { nodename: None }).await;
assert_eq!("No devices found.\n".to_string(), output);
Ok(())
}
#[fuchsia_async::run_singlethreaded(test)]
async fn test_list_with_one_device_and_no_nodename() -> Result<()> {
let output = run_list_test(1, ListCommand { nodename: None }).await;
let value = format!("Test {}", 0);
let node_listing = Regex::new(&value).expect("test regex");
assert_eq!(
1,
node_listing.find_iter(&output).count(),
"could not find \"{}\" nodename in output:\n{}",
value,
output
);
Ok(())
}
#[fuchsia_async::run_singlethreaded(test)]
async fn test_list_with_multiple_devices_and_no_nodename() -> Result<()> {
let num_tests = 10;
let output = run_list_test(num_tests, ListCommand { nodename: None }).await;
for x in 0..num_tests {
let value = format!("Test {}", x);
let node_listing = Regex::new(&value).expect("test regex");
assert_eq!(
1,
node_listing.find_iter(&output).count(),
"could not find \"{}\" nodename in output:\n{}",
value,
output
);
}
Ok(())
}
#[fuchsia_async::run_singlethreaded(test)]
async fn test_list_with_one_device_and_matching_nodename() -> Result<()> {
let output = run_list_test(1, ListCommand { nodename: Some("Test 0".to_string()) }).await;
let value = format!("Test {}", 0);
let node_listing = Regex::new(&value).expect("test regex");
assert_eq!(
1,
node_listing.find_iter(&output).count(),
"could not find \"{}\" nodename in output:\n{}",
value,
output
);
Ok(())
}
#[fuchsia_async::run_singlethreaded(test)]
async fn test_list_with_one_device_and_not_matching_nodename() -> Result<()> {
let output = run_list_test(1, ListCommand { nodename: Some("blarg".to_string()) }).await;
let value = format!("Test {}", 0);
let node_listing = Regex::new(&value).expect("test regex");
assert_eq!(0, node_listing.find_iter(&output).count());
Ok(())
}
#[fuchsia_async::run_singlethreaded(test)]
async fn test_list_with_multiple_devices_and_not_matching_nodename() -> Result<()> {
let num_tests = 25;
let output =
run_list_test(num_tests, ListCommand { nodename: Some("blarg".to_string()) }).await;
for x in 0..num_tests {
let value = format!("Test {}", x);
let node_listing = Regex::new(&value).expect("test regex");
assert_eq!(0, node_listing.find_iter(&output).count());
}
Ok(())
}
#[fuchsia_async::run_singlethreaded(test)]
async fn test_list_with_multiple_devices_and_matching_nodename() -> Result<()> {
let output = run_list_test(25, ListCommand { nodename: Some("Test 19".to_string()) }).await;
let value = format!("Test {}", 0);
let node_listing = Regex::new(&value).expect("test regex");
assert_eq!(0, node_listing.find_iter(&output).count());
let value = format!("Test {}", 19);
let node_listing = Regex::new(&value).expect("test regex");
assert_eq!(1, node_listing.find_iter(&output).count());
Ok(())
}
}