blob: fa2a36edcc3781d1f494ed1685e79d22d14acedd [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 {
failure::{Error, ResultExt},
fidl_fuchsia_bluetooth_le::{
ConnectionProxy, PeripheralEvent, PeripheralMarker, PeripheralProxy,
},
fidl_fuchsia_bluetooth_test::HciEmulatorProxy,
fuchsia_async as fasync,
fuchsia_bluetooth::{
expectation::asynchronous::{ExpectableState, ExpectationHarness},
types::le::Peer,
},
futures::{select, Future, FutureExt, TryStreamExt},
parking_lot::MappedRwLockWriteGuard,
pin_utils::pin_mut,
std::convert::TryInto,
};
use crate::harness::{
control::ActivatedFakeHost,
emulator::{watch_advertising_states, EmulatorHarness, EmulatorHarnessAux, EmulatorState},
TestHarness,
};
/// A snapshot of the current LE peripheral procedure states of the controller.
#[derive(Clone, Default)]
pub struct PeripheralState {
emulator_state: EmulatorState,
/// Observed peer connections.
pub connections: Vec<(Peer, ConnectionProxy)>,
}
impl PeripheralState {
/// Resets to the default state.
pub fn reset(&mut self) {
self.emulator_state = EmulatorState::default();
self.connections.clear();
}
}
impl std::convert::AsMut<EmulatorState> for PeripheralState {
fn as_mut(&mut self) -> &mut EmulatorState {
&mut self.emulator_state
}
}
impl std::convert::AsRef<EmulatorState> for PeripheralState {
fn as_ref(&self) -> &EmulatorState {
&self.emulator_state
}
}
pub type PeripheralHarness = ExpectationHarness<PeripheralState, PeripheralHarnessAux>;
type PeripheralHarnessAux = EmulatorHarnessAux<PeripheralProxy>;
impl EmulatorHarness for PeripheralHarness {
type State = PeripheralState;
fn emulator(&self) -> HciEmulatorProxy {
self.aux().emulator().clone()
}
fn state(&self) -> MappedRwLockWriteGuard<PeripheralState> {
self.write_state()
}
}
impl TestHarness for PeripheralHarness {
fn run_with_harness<F, Fut>(test_func: F) -> Result<(), Error>
where
F: FnOnce(Self) -> Fut,
Fut: Future<Output = Result<(), Error>>,
{
let mut executor = fasync::Executor::new().context("error creating event loop")?;
executor.run_singlethreaded(run_peripheral_test_async(test_func))
}
}
async fn run_peripheral_test_async<F, Fut>(test: F) -> Result<(), Error>
where
F: FnOnce(PeripheralHarness) -> Fut,
Fut: Future<Output = Result<(), Error>>,
{
// Don't drop the ActivatedFakeHost until the end of this function.
let host = ActivatedFakeHost::new("bt-integration-le-peripheral").await?;
let proxy = fuchsia_component::client::connect_to_service::<PeripheralMarker>()
.context("Failed to connect to BLE Peripheral service")?;
let state = PeripheralHarness::new(PeripheralHarnessAux::new(proxy, host.emulator().clone()));
// Initialize the state update watcher and the test run tasks.
let watch_adv = watch_advertising_states(state.clone());
let watch_conn = watch_connections(state.clone());
let run_test = test(state);
// Run until one of the tasks finishes.
pin_mut!(watch_adv);
pin_mut!(watch_conn);
pin_mut!(run_test);
let result = select! {
watch_adv_result = watch_adv.fuse() => watch_adv_result,
watch_conn_result = watch_conn.fuse() => watch_conn_result,
test_result = run_test.fuse() => test_result,
};
host.release().await?;
result
}
async fn watch_connections(harness: PeripheralHarness) -> Result<(), Error> {
let mut events = harness.aux().proxy().take_event_stream();
while let Some(e) = events.try_next().await? {
match e {
PeripheralEvent::OnPeerConnected { peer, connection } => {
harness
.write_state()
.connections
.push((peer.try_into()?, connection.into_proxy()?));
}
// Ignore the deprecated events.
PeripheralEvent::OnCentralConnected { .. } => (),
PeripheralEvent::OnCentralDisconnected { .. } => (),
}
harness.notify_state_changed();
}
Ok(())
}