blob: 326f05189e9082edea23a2beab0b2d07b80e830b [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.
use {
anyhow::{format_err, Context as _, Error},
fidl_fuchsia_netemul_network::{
EndpointManagerMarker, NetworkContextMarker, NetworkManagerMarker,
},
fidl_fuchsia_netemul_sync::{BusMarker, BusProxy, Event, SyncManagerMarker},
fuchsia_async as fasync,
fuchsia_component::client,
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 SETUP_FILE: &str = "/data/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::<SyncManagerMarker>()
.context("SyncManager not available")?;
let (bus, busch) = fidl::endpoints::create_proxy::<BusMarker>()?;
busm.bus_subscribe(BUS_NAME, client, busch)?;
Ok(BusConnection { bus })
}
pub fn publish_code(&self, code: i32) -> Result<(), Error> {
self.bus.publish(Event {
code: Some(code),
message: None,
arguments: None,
..Event::EMPTY
})?;
Ok(())
}
pub async fn wait_for_client(&mut self, expect: &'static str) -> Result<(), Error> {
let _ = self.bus.wait_for_clients(&mut vec![expect].drain(..), 0).await?;
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_sync::BusEvent::OnBusData { data } => match data.code {
Some(rcv_code) => {
if rcv_code == code {
futures::future::ok(Some(()))
} else {
futures::future::ok(None)
}
}
None => futures::future::ok(None),
},
_ => futures::future::ok(None),
});
stream.try_next().await?;
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.sync.SyncManager"))?;
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 = netm.get_network(NETWORK_NAME).await?;
if net == None {
return Err(format_err!("Could not retrieve network {}.", NETWORK_NAME));
}
let ep0 = epm.get_endpoint(EP0_NAME).await?;
if ep0 == None {
return Err(format_err!("Could not retrieve endpoint {}", EP0_NAME));
}
let ep1 = epm.get_endpoint(EP1_NAME).await?;
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 () = bus.wait_for_event(EVENT_CODE).await?;
log::info!("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 () = bus.wait_for_client("root").await?;
// ... then publish an event so root knows we were spawned
let () = bus.publish_code(EVENT_CODE)?;
Ok(())
}
fn run_root(opt: &Opt) -> Result<(), Error> {
log::info!("Running main test: {}", opt.name);
let () = check_netemul_environment()?;
let () = check_path_present(&service_path("fuchsia.netstack.Netstack"))?;
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> {
log::info!("Running test 1: {}", opt.name);
let () = check_netemul_environment()?;
let () = check_path_present(&service_path("fuchsia.netstack.Netstack"))?;
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> {
log::info!("Running test 2: {}", opt.name);
let () = check_netemul_environment()?;
let () = check_path_absent(&service_path("fuchsia.netstack.Netstack"))?;
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> {
log::info!("Running setup test: {}", opt.name);
// create a file in /data, 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 () = fuchsia_syslog::init().context("cannot init logger")?;
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")),
}
}