blob: 67b6a99e17f662a2692d65566a6b2c23ba05760d [file] [log] [blame]
// Copyright 2018 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.
#![feature(async_await, await_macro, futures_api)]
use {
failure::{format_err, Error, ResultExt},
fidl_fuchsia_netemul_bus::{BusManagerMarker, BusMarker, BusProxy, Event},
fidl_fuchsia_netemul_network::{
EndpointManagerMarker, NetworkContextMarker, NetworkManagerMarker,
},
fuchsia_app::client,
fuchsia_async::{self as fasync, TimeoutExt},
fuchsia_zircon::DurationNum,
futures::TryStreamExt,
std::fs,
std::path::Path,
structopt::StructOpt,
};
#[derive(StructOpt, Debug)]
struct Opt {
#[structopt(short = "t")]
test: Option<i32>,
#[structopt(short = "n", default_value = "root")]
name: String,
}
const BUS_NAME: &str = "test-bus";
const NETWORK_NAME: &str = "test-net";
const EP0_NAME: &str = "ep0";
const EP1_NAME: &str = "ep1";
const EVENT_CODE: i32 = 1;
const TIMEOUT_SECS: i64 = 2;
const SETUP_FILE: &str = "/vdata/test-setup";
const SETUP_FILE_DATA: &str = "Hello World";
pub struct BusConnection {
bus: BusProxy,
}
impl BusConnection {
pub fn new(client: &str) -> Result<BusConnection, Error> {
let busm =
client::connect_to_service::<BusManagerMarker>().context("BusManager not available")?;
let (bus, busch) = fidl::endpoints::create_proxy::<BusMarker>()?;
busm.subscribe(BUS_NAME, client, busch)?;
Ok(BusConnection { bus })
}
pub fn publish_code(&self, code: i32) -> Result<(), Error> {
self.bus.publish(&mut Event {
code: code,
message: None,
arguments: None,
})?;
Ok(())
}
pub async fn wait_for_client(&mut self, expect: &'static str) -> Result<(), Error> {
let clients = await!(self.bus.get_clients())?;
if clients.contains(&String::from(expect)) {
return Ok(());
}
let mut stream = self
.bus
.take_event_stream()
.try_filter_map(|event| match event {
fidl_fuchsia_netemul_bus::BusEvent::OnClientAttached { client } => {
if client == expect {
futures::future::ok(Some(()))
} else {
futures::future::ok(None)
}
}
_ => futures::future::ok(None),
});
await!(stream.try_next())?;
Ok(())
}
pub async fn wait_for_event(&mut self, code: i32) -> Result<(), Error> {
let mut stream = self
.bus
.take_event_stream()
.try_filter_map(|event| match event {
fidl_fuchsia_netemul_bus::BusEvent::OnBusData { data } => {
if data.code == code {
futures::future::ok(Some(()))
} else {
futures::future::ok(None)
}
}
_ => futures::future::ok(None),
});
await!(stream.try_next())?;
Ok(())
}
}
fn service_path(name: &str) -> String {
format!("/svc/{}", name)
}
fn device_path(name: &str) -> String {
format!("/vdev/{}", name)
}
fn check_path_present(path: &str) -> Result<(), Error> {
if Path::new(path).exists() {
Ok(())
} else {
Err(format_err!(
"Path {} not present, expected it to be there",
path
))
}
}
fn check_path_absent(path: &str) -> Result<(), Error> {
if Path::new(path).exists() {
Err(format_err!(
"Path {} present, expected it to be absent.",
path
))
} else {
Ok(())
}
}
fn check_netemul_environment() -> Result<(), Error> {
let () = check_path_present(&service_path("fuchsia.netemul.bus.BusManager"))?;
let () = check_path_present(&service_path(
"fuchsia.netemul.environment.ManagedEnvironment",
))?;
let () = check_path_present(&service_path("fuchsia.netemul.network.NetworkContext"))?;
Ok(())
}
async fn check_network() -> Result<(), Error> {
let netctx = client::connect_to_service::<NetworkContextMarker>()?;
let (epm, epmch) = fidl::endpoints::create_proxy::<EndpointManagerMarker>()?;
let () = netctx.get_endpoint_manager(epmch)?;
let (netm, netmch) = fidl::endpoints::create_proxy::<NetworkManagerMarker>()?;
let () = netctx.get_network_manager(netmch)?;
let net = await!(netm.get_network(NETWORK_NAME))?;
if net == None {
return Err(format_err!("Could not retrieve network {}.", NETWORK_NAME));
}
let ep0 = await!(epm.get_endpoint(EP0_NAME))?;
if ep0 == None {
return Err(format_err!("Could not retrieve endpoint {}", EP0_NAME));
}
let ep1 = await!(epm.get_endpoint(EP1_NAME))?;
if ep1 == None {
return Err(format_err!("Could not retrieve endpoint {}", EP1_NAME));
}
Ok(())
}
async fn root_wait_for_children(mut bus: BusConnection) -> Result<(), Error> {
// wait for three hits on the bus, representing each child test
for i in 0..3 {
let () = await!(bus
.wait_for_event(EVENT_CODE)
.on_timeout(TIMEOUT_SECS.seconds().after_now(), || Err(format_err!(
"timed out waiting for children procs"
))))?;
println!("Got ping from child {}", i);
}
Ok(())
}
async fn child_publish_on_bus(mut bus: BusConnection) -> Result<(), Error> {
// wait for root to show up on the bus...
let () = await!(bus
.wait_for_client("root")
.on_timeout(TIMEOUT_SECS.seconds().after_now(), || Err(format_err!(
"Timed out waiting for root test"
))))?;
// ... then publish an event so root knows we were spawned
let () = bus.publish_code(EVENT_CODE)?;
Ok(())
}
fn run_root(opt: &Opt) -> Result<(), Error> {
println!("Running main test: {}", opt.name);
let () = check_netemul_environment()?;
let () = check_path_present(&service_path("fuchsia.netstack.Netstack"))?;
let () = check_path_present(&service_path("fuchsia.net.SocketProvider"))?;
let () = check_path_present(&device_path("class/ethernet/ep0"))?;
let () = check_path_present(&device_path("class/ethernet/ep1"))?;
let mut executor = fasync::Executor::new().context("Error creating executor")?;
// check that network was created according to spec
let () = executor.run_singlethreaded(check_network())?;
// check that the setup process ran:
let setup_data = fs::read_to_string(SETUP_FILE).context("Can't open setup file.")?;
if setup_data != SETUP_FILE_DATA {
return Err(format_err!(
"Setup file contents mismatch, got {}",
setup_data
));
}
// wait for children on bus
let bus = BusConnection::new(&opt.name)?;
executor.run_singlethreaded(root_wait_for_children(bus))
}
// environment 1 inherits from the root environment
fn run_test_1(opt: &Opt) -> Result<(), Error> {
println!("Running test 1: {}", opt.name);
let () = check_netemul_environment()?;
let () = check_path_present(&service_path("fuchsia.netstack.Netstack"))?;
let () = check_path_present(&service_path("fuchsia.net.SocketProvider"))?;
let () = check_path_absent(&device_path("class/ethernet/ep0"))?;
let () = check_path_absent(&device_path("class/ethernet/ep1"))?;
let mut executor = fasync::Executor::new().context("Error creating executor")?;
let bus = BusConnection::new(&opt.name)?;
executor.run_singlethreaded(child_publish_on_bus(bus))
}
// environment 2 does NOT inherit from the root environment
fn run_test_2(opt: &Opt) -> Result<(), Error> {
println!("Running test 2: {}", opt.name);
let () = check_netemul_environment()?;
let () = check_path_absent(&service_path("fuchsia.netstack.Netstack"))?;
let () = check_path_absent(&service_path("fuchsia.net.SocketProvider"))?;
let () = check_path_absent(&device_path("class/ethernet/ep0"))?;
let () = check_path_absent(&device_path("class/ethernet/ep1"))?;
let mut executor = fasync::Executor::new().context("Error creating executor")?;
let bus = BusConnection::new(&opt.name)?;
executor.run_singlethreaded(child_publish_on_bus(bus))
}
fn run_setup_test(opt: &Opt) -> Result<(), Error> {
println!("Running setup test: {}", opt.name);
// create a file in vdata, that will be verified by root test
let () = fs::write(SETUP_FILE, SETUP_FILE_DATA).context("setup can't write file")?;
Ok(())
}
fn main() -> Result<(), Error> {
let opt = Opt::from_args();
match opt.test {
None => run_root(&opt),
Some(1) => run_test_1(&opt),
Some(2) => run_test_2(&opt),
Some(3) => run_setup_test(&opt),
_ => Err(format_err!("Unrecognized test option")),
}
}