mirror of
https://github.com/kidfromjupiter/nearby.git
synced 2026-09-14 14:46:12 -04:00
[fp-rs] Added FP Advertisement unit tests in Rust.
This commit is contained in:
@@ -33,7 +33,7 @@ type DecibelMilliwatts = i16;
|
||||
|
||||
impl BleAdvertisement {
|
||||
/// Construct a new `BleAdvertisement` instance.
|
||||
pub(crate) fn new(
|
||||
pub fn new(
|
||||
address: BleAddress,
|
||||
rssi: Option<DecibelMilliwatts>,
|
||||
tx_power: Option<DecibelMilliwatts>,
|
||||
|
||||
@@ -17,8 +17,8 @@ mod common;
|
||||
|
||||
use api::{BleAdapter, BleDevice, ClassicDevice};
|
||||
pub use common::{
|
||||
BleAddress, BleAdvertisement, BleDataTypeId, BluetoothError,
|
||||
ClassicAddress, PairingResult, ServiceData,
|
||||
BleAddress, BleAddressKind, BleAdvertisement, BleDataTypeId,
|
||||
BluetoothError, ClassicAddress, PairingResult, ServiceData,
|
||||
};
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
|
||||
@@ -14,10 +14,7 @@
|
||||
|
||||
use bluetooth::{BleAddress, BleAdvertisement, ServiceData};
|
||||
|
||||
use crate::{
|
||||
decoder::FpDecoder,
|
||||
fetcher::{DeviceInfo, FpFetcher, FpFetcherFs},
|
||||
};
|
||||
use crate::{decoder::FpDecoder, fetcher::FpFetcher};
|
||||
|
||||
/// Represents a FP device model ID.
|
||||
pub(crate) type ModelId = String;
|
||||
@@ -30,7 +27,7 @@ pub(crate) struct FpPairingAdvertisement {
|
||||
/// Estimated distance in meters of device from BLE adapter.
|
||||
distance: f64,
|
||||
model_id: ModelId,
|
||||
name: String,
|
||||
device_name: String,
|
||||
image_url: String,
|
||||
}
|
||||
|
||||
@@ -39,6 +36,7 @@ impl FpPairingAdvertisement {
|
||||
pub(crate) fn new(
|
||||
adv: BleAdvertisement,
|
||||
service_data: &ServiceData<u16>,
|
||||
fetcher: &Box<dyn FpFetcher>,
|
||||
) -> Result<Self, anyhow::Error> {
|
||||
let rssi = adv.rssi().ok_or(anyhow::anyhow!(
|
||||
"Windows advertisements should contain RSSI information."
|
||||
@@ -78,16 +76,13 @@ impl FpPairingAdvertisement {
|
||||
let model_id = format!("{}", u32::from_be_bytes(model_id.try_into().unwrap()));
|
||||
|
||||
// Retrieve device info of the device corresponding to this model ID.
|
||||
let fetcher = FpFetcherFs::new(String::from("./local"));
|
||||
let device_info = fetcher
|
||||
.get_device_info_from_model_id(&model_id)
|
||||
.expect("Failed to create device info from model ID.");
|
||||
let device_info = fetcher.get_device_info_from_model_id(&model_id)?;
|
||||
|
||||
Ok(FpPairingAdvertisement {
|
||||
inner: adv,
|
||||
distance,
|
||||
model_id,
|
||||
name: device_info.name().to_string(),
|
||||
device_name: device_info.name().to_string(),
|
||||
image_url: device_info.image_url().to_string(),
|
||||
})
|
||||
}
|
||||
@@ -109,8 +104,8 @@ impl FpPairingAdvertisement {
|
||||
&self.model_id
|
||||
}
|
||||
|
||||
pub(crate) fn name(&self) -> &String {
|
||||
&self.name
|
||||
pub(crate) fn device_name(&self) -> &String {
|
||||
&self.device_name
|
||||
}
|
||||
|
||||
pub(crate) fn image_url(&self) -> &String {
|
||||
@@ -149,3 +144,109 @@ pub(crate) fn distance_from_rssi_and_tx_power(rssi: i16, tx_power: i16) -> f64 {
|
||||
(f64::from(tx_power - rssi - RSSI_DROPOFF_AT_1_M)) / f64::from(10 * PATH_LOSS_EXPONENT),
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::fetcher::{mock::FpFetcherMock, DeviceInfo};
|
||||
|
||||
use bluetooth::BleAddressKind;
|
||||
|
||||
#[test]
|
||||
fn test_new_fp_pairing_advertisement() {
|
||||
let addr = BleAddress::new(0x112233, BleAddressKind::Public);
|
||||
let ble_adv = BleAdvertisement::new(addr, Some(-60), Some(10));
|
||||
|
||||
let raw_data = vec![3, 2, 1];
|
||||
let expected_model_id = "197121"; // (3 << 16) + (2 << 8) + 1.
|
||||
let service_data = ServiceData::new(0x123 as u16, raw_data);
|
||||
|
||||
let image_url = String::from("image_url");
|
||||
let device_name = String::from("name");
|
||||
let device_info = Ok(DeviceInfo::new(image_url.clone(), device_name.clone()));
|
||||
let fetcher: Box<dyn FpFetcher> = Box::new(FpFetcherMock::new(device_info));
|
||||
|
||||
let fp_adv = FpPairingAdvertisement::new(ble_adv, &service_data, &fetcher);
|
||||
|
||||
assert!(fp_adv.is_ok());
|
||||
let fp_adv = fp_adv.unwrap();
|
||||
assert_eq!(fp_adv.address(), addr);
|
||||
assert_eq!(fp_adv.image_url(), &image_url);
|
||||
assert_eq!(fp_adv.device_name(), &device_name);
|
||||
assert_eq!(fp_adv.model_id(), &expected_model_id);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_new_fp_pairing_advertisement_bad_rssi() {
|
||||
let addr = BleAddress::new(0x112233, BleAddressKind::Public);
|
||||
let ble_adv = BleAdvertisement::new(addr, None, Some(10));
|
||||
|
||||
let raw_data = vec![3, 2, 1];
|
||||
let service_data = ServiceData::new(0x123 as u16, raw_data);
|
||||
|
||||
let device_info = Ok(DeviceInfo::new(
|
||||
String::from("image_url"),
|
||||
String::from("name"),
|
||||
));
|
||||
let fetcher: Box<dyn FpFetcher> = Box::new(FpFetcherMock::new(device_info));
|
||||
|
||||
let fp_adv = FpPairingAdvertisement::new(ble_adv, &service_data, &fetcher);
|
||||
|
||||
assert!(fp_adv.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_new_fp_pairing_advertisement_bad_tx_power() {
|
||||
let addr = BleAddress::new(0x112233, BleAddressKind::Public);
|
||||
let ble_adv = BleAdvertisement::new(addr, Some(-60), None);
|
||||
|
||||
let raw_data = vec![3, 2, 1];
|
||||
let service_data = ServiceData::new(0x123 as u16, raw_data);
|
||||
|
||||
let device_info = Ok(DeviceInfo::new(
|
||||
String::from("image_url"),
|
||||
String::from("name"),
|
||||
));
|
||||
let fetcher: Box<dyn FpFetcher> = Box::new(FpFetcherMock::new(device_info));
|
||||
|
||||
let fp_adv = FpPairingAdvertisement::new(ble_adv, &service_data, &fetcher);
|
||||
|
||||
assert!(fp_adv.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_new_fp_pairing_advertisement_bad_service_data() {
|
||||
let addr = BleAddress::new(0x112233, BleAddressKind::Public);
|
||||
let ble_adv = BleAdvertisement::new(addr, Some(-60), Some(10));
|
||||
|
||||
let raw_data = vec![4, 3, 2, 1];
|
||||
let service_data = ServiceData::new(0x123 as u16, raw_data);
|
||||
|
||||
let device_info = Ok(DeviceInfo::new(
|
||||
String::from("image_url"),
|
||||
String::from("name"),
|
||||
));
|
||||
let fetcher: Box<dyn FpFetcher> = Box::new(FpFetcherMock::new(device_info));
|
||||
|
||||
let fp_adv = FpPairingAdvertisement::new(ble_adv, &service_data, &fetcher);
|
||||
|
||||
assert!(fp_adv.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_new_fp_pairing_advertisement_bad_fetcher() {
|
||||
let addr = BleAddress::new(0x112233, BleAddressKind::Public);
|
||||
let ble_adv = BleAdvertisement::new(addr, Some(-60), Some(10));
|
||||
|
||||
let raw_data = vec![3, 2, 1];
|
||||
let service_data = ServiceData::new(0x123 as u16, raw_data);
|
||||
|
||||
let fetcher: Box<dyn FpFetcher> = Box::new(FpFetcherMock::new(Err(anyhow::anyhow!(
|
||||
"mock intentional error"
|
||||
))));
|
||||
|
||||
let fp_adv = FpPairingAdvertisement::new(ble_adv, &service_data, &fetcher);
|
||||
|
||||
assert!(fp_adv.is_err());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,10 @@ use futures::executor;
|
||||
use tracing::{info, warn};
|
||||
use ttl_cache::TtlCache;
|
||||
|
||||
use crate::advertisement::{FpPairingAdvertisement, ModelId};
|
||||
use crate::{
|
||||
advertisement::{FpPairingAdvertisement, ModelId},
|
||||
fetcher::{FpFetcher, FpFetcherFs},
|
||||
};
|
||||
|
||||
// Sends a device name to Flutter via `StreamSink` FFI layer.
|
||||
static DEVICE_STREAM: RwLock<Option<StreamSink<Option<[String; 2]>>>> = RwLock::new(None);
|
||||
@@ -29,7 +32,7 @@ async fn update_best_device(best_adv: FpPairingAdvertisement) {
|
||||
match DEVICE_STREAM.read().unwrap().as_ref() {
|
||||
Some(stream) => {
|
||||
stream.add(Some([
|
||||
best_adv.name().to_string(),
|
||||
best_adv.device_name().to_string(),
|
||||
best_adv.image_url().to_string(),
|
||||
]));
|
||||
}
|
||||
@@ -47,6 +50,7 @@ async fn update_best_device(best_adv: FpPairingAdvertisement) {
|
||||
fn new_best_fp_advertisement(
|
||||
advertisement: BleAdvertisement,
|
||||
service_data: &ServiceData<u16>,
|
||||
fetcher: &Box<dyn FpFetcher>,
|
||||
latest_advertisement_map: &mut HashMap<String, FpPairingAdvertisement>,
|
||||
) -> Option<FpPairingAdvertisement> {
|
||||
// Analyze service data sections.
|
||||
@@ -57,7 +61,7 @@ fn new_best_fp_advertisement(
|
||||
return None;
|
||||
}
|
||||
|
||||
let fp_adv = match FpPairingAdvertisement::new(advertisement, service_data) {
|
||||
let fp_adv = match FpPairingAdvertisement::new(advertisement, service_data, fetcher) {
|
||||
Ok(fp_adv) => fp_adv,
|
||||
Err(err) => {
|
||||
// If error during construction (e.g. non-discoverable
|
||||
@@ -130,6 +134,7 @@ pub fn init() {
|
||||
|
||||
let mut latest_advertisement_map = HashMap::new();
|
||||
let datatype_selector = vec![BleDataTypeId::ServiceData16BitUuid];
|
||||
let fetcher: Box<dyn FpFetcher> = Box::new(FpFetcherFs::new(String::from("./local")));
|
||||
|
||||
loop {
|
||||
// Retrieve the next received advertisement.
|
||||
@@ -142,6 +147,7 @@ pub fn init() {
|
||||
if let Some(best_adv) = new_best_fp_advertisement(
|
||||
advertisement.clone(),
|
||||
service_data,
|
||||
&fetcher,
|
||||
&mut latest_advertisement_map,
|
||||
) {
|
||||
update_best_device(best_adv).await;
|
||||
|
||||
Reference in New Issue
Block a user