bt_test_harness/
low_energy_central.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
// Copyright 2022 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::{Context as _, Error};
use fidl_fuchsia_bluetooth_le::{CentralEvent, CentralMarker, CentralProxy};
use fidl_fuchsia_hardware_bluetooth::EmulatorProxy;
use fuchsia_bluetooth::expectation::asynchronous::{
    expectable, Expectable, ExpectableExt, ExpectableState,
};
use fuchsia_bluetooth::types::le::RemoteDevice;
use futures::future::BoxFuture;
use futures::{FutureExt, TryStreamExt};
use std::ops::{Deref, DerefMut};
use std::sync::Arc;
use test_harness::{SharedState, TestHarness, SHARED_STATE_TEST_COMPONENT_INDEX};

use crate::core_realm::{CoreRealm, SHARED_STATE_INDEX};
use crate::host_watcher::ActivatedFakeHost;

#[derive(PartialEq, Debug, Clone, Copy)]
pub enum ScanStateChange {
    ScanEnabled,
    ScanDisabled,
}

/// A snapshot of the current LowEnergy Central State
#[derive(Clone, Default)]
pub struct CentralState {
    /// Observed scan state changes.
    pub scan_state_changes: Vec<ScanStateChange>,

    /// Discovered devices.
    pub remote_devices: Vec<RemoteDevice>,
}

/// Auxilliary data for the CentralHarness
pub struct Aux {
    pub central: CentralProxy,
    emulator: EmulatorProxy,
}

impl AsRef<EmulatorProxy> for Aux {
    fn as_ref(&self) -> &EmulatorProxy {
        &self.emulator
    }
}

#[derive(Clone)]
pub struct CentralHarness(Expectable<CentralState, Aux>);

impl Deref for CentralHarness {
    type Target = Expectable<CentralState, Aux>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl DerefMut for CentralHarness {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}

impl TestHarness for CentralHarness {
    type Env = (ActivatedFakeHost, Arc<CoreRealm>);
    type Runner = BoxFuture<'static, Result<(), Error>>;

    fn init(
        shared_state: &Arc<SharedState>,
    ) -> BoxFuture<'static, Result<(Self, Self::Env, Self::Runner), Error>> {
        let shared_state = shared_state.clone();
        async move {
            let test_component: Arc<String> = shared_state
                .get(SHARED_STATE_TEST_COMPONENT_INDEX)
                .expect("SharedState must have TEST-COMPONENT")?;
            let inserter = move || CoreRealm::create(test_component.to_string());
            let realm = shared_state.get_or_insert_with(SHARED_STATE_INDEX, inserter).await?;
            let fake_host = ActivatedFakeHost::new(realm.clone()).await?;
            let central = realm
                .instance()
                .connect_to_protocol_at_exposed_dir::<CentralMarker>()
                .context("Failed to connect to BLE Central service")?;

            let harness = CentralHarness(expectable(
                Default::default(),
                Aux { central, emulator: fake_host.emulator().clone() },
            ));
            let run_central = handle_central_events(harness.clone()).boxed();
            Ok((harness, (fake_host, realm), run_central))
        }
        .boxed()
    }

    fn terminate((emulator, realm): Self::Env) -> BoxFuture<'static, Result<(), Error>> {
        // The realm must be kept alive in order for emulator.release() to work properly.
        async move {
            let _realm = realm;
            emulator.release().await
        }
        .boxed()
    }
}

async fn handle_central_events(harness: CentralHarness) -> Result<(), Error> {
    let mut events = harness.aux().central.take_event_stream();

    while let Some(e) = events.try_next().await? {
        match e {
            CentralEvent::OnDeviceDiscovered { device } => {
                harness.write_state().remote_devices.push(device.try_into()?);
                harness.notify_state_changed();
            }
            CentralEvent::OnScanStateChanged { scanning } => {
                let change = if scanning {
                    ScanStateChange::ScanEnabled
                } else {
                    ScanStateChange::ScanDisabled
                };
                harness.write_state().scan_state_changes.push(change);
                harness.notify_state_changed();
            }
            CentralEvent::OnPeripheralDisconnected { identifier: _ } => {}
        };
    }
    Ok(())
}