blob: fe81cac8d511915f09929f620e31b4a0eeef17cc [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)]
#![deny(warnings)]
// Explicitly added due to conflict using custom_attribute and async_await above.
#[macro_use]
extern crate serde_derive;
mod opts;
use crate::opts::Opt;
use {
connectivity_testing::wlan_service_util,
failure::{bail, Error, ResultExt},
fidl_fuchsia_wlan_device_service::{DeviceServiceMarker, DeviceServiceProxy},
fidl_fuchsia_wlan_sme as fidl_sme,
fuchsia_app::client::connect_to_service,
fuchsia_async as fasync,
fuchsia_syslog::{self as syslog, fx_log_info},
std::collections::HashMap,
std::process,
std::thread::sleep,
std::time::{Duration, Instant},
structopt::StructOpt,
};
#[allow(dead_code)]
type WlanService = DeviceServiceProxy;
fn main() -> Result<(), Error> {
syslog::init_with_tags(&["wlan-stress-test"]).expect("Should not fail");
let opt = Opt::from_args();
fx_log_info!("{:?}", opt);
if opt.connect_test_enabled && opt.target_ssid.is_empty() {
bail!("A target_ssid must be provided for connect tests");
}
if !opt.scan_test_enabled && !opt.connect_test_enabled && !opt.disconnect_test_enabled {
bail!("At least one api must be selected for testing");
}
// create objects to hold test objects and results
let mut test_results: TestResults = Default::default();
test_results.num_repetitions = opt.repetitions;
test_results.scan_test_enabled = opt.scan_test_enabled;
test_results.connect_test_enabled = opt.connect_test_enabled;
test_results.disconnect_test_enabled = opt.disconnect_test_enabled;
let mut test_pass = true;
if let Err(e) = run_test(opt, &mut test_results) {
test_pass = false;
test_results.error_message = e.to_string();
}
report_results(&mut test_results);
if !test_pass {
process::exit(1);
}
Ok(())
}
fn duration_to_ms(duration: Duration) -> u128 {
(duration.as_secs() * 1000 + duration.subsec_millis() as u64) as u128
}
fn run_test(opt: Opt, test_results: &mut TestResults) -> Result<(), Error> {
let mut scan_test_pass = true;
let mut connect_test_pass = true;
let mut disconnect_test_pass = true;
let mut exec = fasync::Executor::new().context("Error creating event loop")?;
let wlan_svc =
connect_to_service::<DeviceServiceMarker>().context("Failed to connect to wlan_service")?;
let fut = async {
let wlan_iface_ids = await!(wlan_service_util::get_iface_list(&wlan_svc))
.context("Failed to query wlan_service iface list")?;
if wlan_iface_ids.is_empty() {
bail!("Did not find wlan interfaces");
};
for iface in wlan_iface_ids {
let sme_proxy = await!(wlan_service_util::get_iface_sme_proxy(&wlan_svc, iface))?;
match await!(sme_proxy.status()) {
Ok(status) => status,
Err(_) => continue,
};
let result = TestReport::new();
let iface_object = WlanIface::new(sme_proxy, result);
test_results.iface_objects.insert(iface, iface_object);
}
// now that we have interfaces... let's try to use them!
for (_iface_id, wlaniface) in test_results.iface_objects.iter_mut() {
let mut total_scan_time_ms = 0;
let mut total_connect_time_ms = 0;
let mut total_disconnect_time_ms = 0;
for i in 0..opt.repetitions {
if opt.scan_test_enabled {
let start = Instant::now();
let result = await!(wlan_service_util::perform_scan(&wlaniface.sme_proxy));
match result {
Ok(_ess_info) => {
total_scan_time_ms +=
duration_to_ms(Instant::now().duration_since(start));
wlaniface.report.num_successful_scans += 1;
wlaniface.report.last_successful_scan_attempt = i + 1;
}
_ => {}
};
}
if opt.connect_test_enabled {
let start = Instant::now();
let result = await!(wlan_service_util::connect_to_network(
&wlaniface.sme_proxy,
opt.target_ssid.as_bytes().to_vec(),
opt.target_pwd.as_bytes().to_vec()
));
match result {
Ok(true) => {
total_connect_time_ms +=
duration_to_ms(Instant::now().duration_since(start));
wlaniface.report.num_successful_connects += 1;
wlaniface.report.last_successful_connection_attempt = i + 1;
}
_ => {}
};
}
if opt.disconnect_test_enabled {
let start = Instant::now();
let result =
await!(wlan_service_util::disconnect_from_network(&wlaniface.sme_proxy));
match result {
Ok(()) => {
total_disconnect_time_ms +=
duration_to_ms(Instant::now().duration_since(start));
wlaniface.report.num_successful_disconnects += 1;
wlaniface.report.last_successful_disconnect_attempt = i + 1;
}
_ => {}
};
}
sleep(Duration::from_millis(opt.wait_time_ms));
}
if wlaniface.report.num_successful_scans > 0 {
wlaniface.report.average_scan_time_ms =
total_scan_time_ms / wlaniface.report.num_successful_scans;
}
if wlaniface.report.num_successful_connects > 0 {
wlaniface.report.average_connect_time_ms =
total_connect_time_ms / wlaniface.report.num_successful_connects;
}
if wlaniface.report.num_successful_disconnects > 0 {
wlaniface.report.average_disconnect_time_ms =
total_disconnect_time_ms / wlaniface.report.num_successful_disconnects;
}
}
for (_iface_id, wlaniface) in test_results.iface_objects.iter_mut() {
if opt.scan_test_enabled && (wlaniface.report.num_successful_scans < opt.repetitions) {
scan_test_pass = false;
}
if opt.connect_test_enabled
&& (wlaniface.report.num_successful_connects < opt.repetitions)
{
connect_test_pass = false;
}
if opt.disconnect_test_enabled
&& (wlaniface.report.num_successful_disconnects < opt.repetitions)
{
disconnect_test_pass = false;
}
}
Ok(())
};
exec.run_singlethreaded(fut)?;
let mut error_string = String::from("Test Failed:");
if opt.scan_test_enabled && !scan_test_pass {
error_string.push_str(" Scan Failed");
}
if opt.connect_test_enabled && !connect_test_pass {
error_string.push_str(" Connect Failed");
}
if opt.disconnect_test_enabled && !disconnect_test_pass {
error_string.push_str(" Disconnect Failed");
}
if !(scan_test_pass && connect_test_pass && disconnect_test_pass) {
bail!(error_string)
}
Ok(())
}
#[derive(Serialize)]
struct TestReport {
num_successful_scans: u128,
last_successful_scan_attempt: u128,
average_scan_time_ms: u128,
num_successful_connects: u128,
last_successful_connection_attempt: u128,
average_connect_time_ms: u128,
num_successful_disconnects: u128,
last_successful_disconnect_attempt: u128,
average_disconnect_time_ms: u128,
}
impl TestReport {
pub fn new() -> TestReport {
TestReport {
num_successful_scans: 0,
last_successful_scan_attempt: 0,
average_scan_time_ms: 0,
num_successful_connects: 0,
last_successful_connection_attempt: 0,
average_connect_time_ms: 0,
num_successful_disconnects: 0,
last_successful_disconnect_attempt: 0,
average_disconnect_time_ms: 0,
}
}
}
#[derive(Serialize)]
struct WlanIface {
#[serde(skip_serializing)]
sme_proxy: fidl_sme::ClientSmeProxy,
report: TestReport,
}
impl WlanIface {
pub fn new(sme_proxy: fidl_sme::ClientSmeProxy, report: TestReport) -> WlanIface {
WlanIface { sme_proxy: sme_proxy, report: report }
}
}
// Object to hold overall test status
#[derive(Default, Serialize)]
struct TestResults {
num_repetitions: u128,
scan_test_enabled: bool,
connect_test_enabled: bool,
disconnect_test_enabled: bool,
#[serde(flatten)]
iface_objects: HashMap<u16, WlanIface>,
error_message: String,
}
fn report_results(test_results: &TestResults) {
println!("{}", serde_json::to_string_pretty(&test_results).unwrap());
}