From 748bf9fb187c3daf0bb35a26fb47d70dfca1026a Mon Sep 17 00:00:00 2001 From: Lucas Silva Shepard Date: Sun, 13 Aug 2023 18:23:56 -0700 Subject: [PATCH 1/4] [fp-rs] Added unit tests to cover common portion of Rust Bluetooth library. --- fastpair/rust/bluetooth/src/common/address.rs | 84 +++++++++++++++++++ .../bluetooth/src/common/advertisement.rs | 57 ++++++++++++- fastpair/rust/bluetooth/src/common/error.rs | 2 +- 3 files changed, 141 insertions(+), 2 deletions(-) diff --git a/fastpair/rust/bluetooth/src/common/address.rs b/fastpair/rust/bluetooth/src/common/address.rs index db0481f6..bff124d7 100644 --- a/fastpair/rust/bluetooth/src/common/address.rs +++ b/fastpair/rust/bluetooth/src/common/address.rs @@ -93,3 +93,87 @@ impl From for u64 { u64::from_le_bytes(bytes) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn ble_address_new() { + let addr = BleAddress::new(0x112233445566, BleAddressKind::Public); + assert_eq!(addr.val, [0x66, 0x55, 0x44, 0x33, 0x22, 0x11]); + assert_eq!(addr.kind, BleAddressKind::Public); + } + + #[test] + fn ble_address_get_kind() { + let addr_public = + BleAddress::new(0x112233445566, BleAddressKind::Public); + assert_eq!(addr_public.get_kind(), BleAddressKind::Public); + + let addr_random = + BleAddress::new(0xAABBCCDDEEFF, BleAddressKind::Random); + assert_eq!(addr_random.get_kind(), BleAddressKind::Random); + } + + #[test] + fn ble_address_into_u64() { + let ble_addr = BleAddress::new(0x112233445566, BleAddressKind::Public); + let u64_addr: u64 = ble_addr.into(); + assert_eq!(u64_addr, 0x112233445566); + } + + #[test] + fn classic_address_from_u64() { + let u64_addr = 0x112233445566; + let classic_addr: ClassicAddress = u64_addr.into(); + assert_eq!(classic_addr.0, [0x66, 0x55, 0x44, 0x33, 0x22, 0x11]); + } + + #[test] + fn try_from_ble_address_to_classic() { + let ble_addr = BleAddress::new(0x112233445566, BleAddressKind::Public); + let result: Result = + TryFrom::try_from(ble_addr); + assert!(result.is_ok()); + assert_eq!(result.unwrap().0, [0x66, 0x55, 0x44, 0x33, 0x22, 0x11]); + + let ble_addr_random = + BleAddress::new(0xAABBCCDDEEFF, BleAddressKind::Random); + let result: Result = + TryFrom::try_from(ble_addr_random); + assert!(result.is_err()); + assert!(matches!( + result.unwrap_err(), + BluetoothError::BadTypeConversion(_), + )); + } + + #[test] + fn test_u64_to_6lsb() { + // Test a case where the input number is smaller than 6 bytes + let num = 0x123456; + let expected_result = [0x56, 0x34, 0x12, 0, 0, 0]; + let result = u64_to_6lsb(num); + assert_eq!(result, expected_result); + + // Test a case where the input number is exactly 6 bytes + let num = 0xAABBCCDDEEFF; + let expected_result = [0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA]; + let result = u64_to_6lsb(num); + assert_eq!(result, expected_result); + + // Test a case where the number is too large so the two most significant + // bytes get dropped. + let num = 0x1122334455667788; + let expected_result = [0x88, 0x77, 0x66, 0x55, 0x44, 0x33]; + let result = u64_to_6lsb(num); + assert_eq!(result, expected_result); + + // Test a case where the input number is 0 + let num = 0; + let expected_result = [0, 0, 0, 0, 0, 0]; + let result = u64_to_6lsb(num); + assert_eq!(result, expected_result); + } +} diff --git a/fastpair/rust/bluetooth/src/common/advertisement.rs b/fastpair/rust/bluetooth/src/common/advertisement.rs index 9fb7f999..93413079 100644 --- a/fastpair/rust/bluetooth/src/common/advertisement.rs +++ b/fastpair/rust/bluetooth/src/common/advertisement.rs @@ -96,7 +96,7 @@ pub enum BleDataTypeId { /// Struct representing the Bluetooth Service Data common data type. `U` should /// be one of the valid uuid sizes, specified in: /// Bluetooth Supplement to the Core Specification, Part A, Section 1.11. -#[derive(Clone)] +#[derive(Clone, PartialEq, Eq, Debug)] pub struct ServiceData { uuid: U, data: Vec, @@ -115,3 +115,58 @@ impl ServiceData { &self.data } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::common::BleAddressKind; + + #[test] + fn ble_advertisement_new() { + let address = BleAddress::new(0x112233445566, BleAddressKind::Public); + let ad = BleAdvertisement::new(address, Some(-60), Some(10)); + assert_eq!(ad.address(), address); + assert_eq!(ad.rssi(), Some(-60)); + assert_eq!(ad.tx_power(), Some(10)); + assert!(ad.service_data_16bit_uuid.is_none()); + } + + #[test] + fn ble_advertisement_set_and_get_service_data() { + let address = BleAddress::new(0x112233445566, BleAddressKind::Public); + let mut ad = BleAdvertisement::new(address, Some(-60), Some(10)); + + let service_data = vec![ + ServiceData::new(0x1234, vec![0x01, 0x02, 0x03]), + ServiceData::new(0x5678, vec![0x04, 0x05]), + ]; + + ad.set_service_data_16bit_uuid(service_data.clone()); + + let retrieved_service_data = ad.service_data_16bit_uuid().unwrap(); + assert_eq!(*retrieved_service_data, service_data); + } + + #[test] + fn ble_advertisement_missing_service_data() { + let address = BleAddress::new(0x112233445566, BleAddressKind::Public); + let ad = BleAdvertisement::new(address, Some(-60), Some(10)); + + let result = ad.service_data_16bit_uuid(); + assert!(result.is_err()); + assert!(matches!( + result.unwrap_err(), + BluetoothError::FailedPrecondition(_) + )); + } + + #[test] + fn service_data_new() { + let uuid = 0x1234; + let data = vec![0x01, 0x02, 0x03]; + + let service_data = ServiceData::new(uuid, data.clone()); + assert_eq!(service_data.uuid(), uuid); + assert_eq!(*service_data.data(), data); + } +} diff --git a/fastpair/rust/bluetooth/src/common/error.rs b/fastpair/rust/bluetooth/src/common/error.rs index 8b20b36e..d0d43208 100644 --- a/fastpair/rust/bluetooth/src/common/error.rs +++ b/fastpair/rust/bluetooth/src/common/error.rs @@ -16,7 +16,7 @@ use thiserror::Error; /// Library error type. #[non_exhaustive] -#[derive(Error, Debug)] +#[derive(Error, Debug, PartialEq)] pub enum BluetoothError { /// Reported when the user attempts a bad type conversion, e.g. converting /// a BLE random address to a BT Classic address. From b1fa76f560100a228e5574d40e3d857c7c302a02 Mon Sep 17 00:00:00 2001 From: Lucas Silva Shepard Date: Mon, 14 Aug 2023 16:29:02 -0700 Subject: [PATCH 2/4] [fp-rs] Wrote unit tests for Fast Pair Decoder in Rust. --- fastpair/rust/demo/rust/src/decoder.rs | 35 ++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/fastpair/rust/demo/rust/src/decoder.rs b/fastpair/rust/demo/rust/src/decoder.rs index dd0f80d4..4865a35b 100644 --- a/fastpair/rust/demo/rust/src/decoder.rs +++ b/fastpair/rust/demo/rust/src/decoder.rs @@ -47,3 +47,38 @@ impl FpDecoder { } } } + +mod tests { + use super::*; + + #[test] + fn test_get_model_id_valid() { + // Valid scenario: Length == 3 + let uuid: u16 = 0x1234; + let data = vec![0xAA, 0xBB, 0xCC]; + let service_data = ServiceData::new(uuid, data.clone()); + let result = FpDecoder::get_model_id_from_service_data(&service_data); + assert!(result.is_ok()); + assert_eq!(result.unwrap(), data); + } + + #[test] + fn test_get_model_id_invalid() { + // Invalid scenario: Length < 3 + let uuid: u16 = 0x1234; + let data = vec![0xAA, 0xBB]; + let service_data = ServiceData::new(uuid, data); + let result = FpDecoder::get_model_id_from_service_data(&service_data); + assert!(result.is_err()); + } + + #[test] + fn test_get_model_id_unsupported() { + // Unsupported scenario: Length > 3 + let uuid: u16 = 0x1234; + let data = vec![0xAA, 0xBB, 0xCC, 0xDD]; + let service_data = ServiceData::new(uuid, data); + let result = FpDecoder::get_model_id_from_service_data(&service_data); + assert!(result.is_err()); + } +} From 169c04c43024d92d94b60f0bf79eab32d91cb080 Mon Sep 17 00:00:00 2001 From: Lucas Silva Shepard Date: Tue, 15 Aug 2023 12:22:17 -0700 Subject: [PATCH 3/4] [fp-rs] Split fetcher.rs into directory module, added mock. --- fastpair/rust/demo/rust/src/advertisement.rs | 4 +- .../src/{fetcher.rs => fetcher/common.rs} | 63 +++++++------------ fastpair/rust/demo/rust/src/fetcher/fs.rs | 49 +++++++++++++++ fastpair/rust/demo/rust/src/fetcher/mock.rs | 43 +++++++++++++ fastpair/rust/demo/rust/src/fetcher/mod.rs | 22 +++++++ 5 files changed, 138 insertions(+), 43 deletions(-) rename fastpair/rust/demo/rust/src/{fetcher.rs => fetcher/common.rs} (59%) create mode 100644 fastpair/rust/demo/rust/src/fetcher/fs.rs create mode 100644 fastpair/rust/demo/rust/src/fetcher/mock.rs create mode 100644 fastpair/rust/demo/rust/src/fetcher/mod.rs diff --git a/fastpair/rust/demo/rust/src/advertisement.rs b/fastpair/rust/demo/rust/src/advertisement.rs index bb3dd601..58cc3338 100644 --- a/fastpair/rust/demo/rust/src/advertisement.rs +++ b/fastpair/rust/demo/rust/src/advertisement.rs @@ -16,7 +16,7 @@ use bluetooth::{BleAddress, BleAdvertisement, ServiceData}; use crate::{ decoder::FpDecoder, - fetcher::{FpFetcher, FpFetcherLocal}, + fetcher::{DeviceInfo, FpFetcher, FpFetcherFs}, }; /// Represents a FP device model ID. @@ -78,7 +78,7 @@ 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 = FpFetcherLocal::new(String::from("./local")); + 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."); diff --git a/fastpair/rust/demo/rust/src/fetcher.rs b/fastpair/rust/demo/rust/src/fetcher/common.rs similarity index 59% rename from fastpair/rust/demo/rust/src/fetcher.rs rename to fastpair/rust/demo/rust/src/fetcher/common.rs index e94d49ad..ea7ed605 100644 --- a/fastpair/rust/demo/rust/src/fetcher.rs +++ b/fastpair/rust/demo/rust/src/fetcher/common.rs @@ -11,27 +11,11 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -use std::fs; use serde::Deserialize; use crate::advertisement::ModelId; -/// Holds Fast Pair device information parsed from JSON. -#[derive(Deserialize)] -#[serde(rename_all = "camelCase")] -pub(crate) struct DeviceInfo { - image_url: String, - name: String, -} - -/// Holds top-level Fast Pair information parsed from JSON. See `local` -/// directory for format. -#[derive(Deserialize)] -struct JsonData { - device: DeviceInfo, -} - /// Types that can fetch Fast Pair data from external storage (e.g. filesystem, /// remote server). pub(crate) trait FpFetcher { @@ -41,36 +25,26 @@ pub(crate) trait FpFetcher { ) -> Result; } -/// A unit struct for retrieving Fast Pair information from the local filesystem. -pub(crate) struct FpFetcherLocal { - path: String, +/// Holds Fast Pair device information parsed from JSON. +#[derive(Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct DeviceInfo { + image_url: String, + name: String, } -impl FpFetcherLocal { - pub(crate) fn new(path: String) -> Self { - FpFetcherLocal { path } - } -} - -impl FpFetcher for FpFetcherLocal { - /// Retrieve device information for the provided Model ID. Currently, - /// this information is saved locally. In the future, this should instead - /// be retrieved from a remote server and cached. - /// b/294456411 - fn get_device_info_from_model_id( - &self, - model_id: &ModelId, - ) -> Result { - let file_path = format!("{}/{}.json", self.path, model_id); - let contents = fs::read_to_string(file_path).expect("Couldn't find or open file."); - - let model_info: JsonData = serde_json::from_str(&contents)?; - - Ok(model_info.device) - } +/// Holds top-level Fast Pair information parsed from JSON. See `local` +/// directory for format. +#[derive(Deserialize)] +pub(super) struct JsonData { + device: DeviceInfo, } impl DeviceInfo { + pub(crate) fn new(image_url: String, name: String) -> Self { + DeviceInfo { image_url, name } + } + pub(crate) fn name(&self) -> &String { &self.name } @@ -79,3 +53,10 @@ impl DeviceInfo { &self.image_url } } + +impl JsonData { + // Returns the `DeviceInfo` associated with parsed self, consuming self. + pub(super) fn device(self) -> DeviceInfo { + self.device + } +} diff --git a/fastpair/rust/demo/rust/src/fetcher/fs.rs b/fastpair/rust/demo/rust/src/fetcher/fs.rs new file mode 100644 index 00000000..ee45955e --- /dev/null +++ b/fastpair/rust/demo/rust/src/fetcher/fs.rs @@ -0,0 +1,49 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::fs; + +use crate::{ + advertisement::ModelId, + fetcher::{DeviceInfo, FpFetcher, JsonData}, +}; + +/// A struct for retrieving Fast Pair information from the local filesystem. +pub(crate) struct FpFetcherFs { + path: String, +} + +impl FpFetcherFs { + pub(crate) fn new(path: String) -> Self { + FpFetcherFs { path } + } +} + +impl FpFetcher for FpFetcherFs { + /// Retrieve device information for the provided Model ID. Currently, + /// this information is saved locally. In the future, this should instead + /// be retrieved from a remote server and cached. + /// b/294456411 + fn get_device_info_from_model_id( + &self, + model_id: &ModelId, + ) -> Result { + let file_path = format!("{}/{}.json", self.path, model_id); + let contents = fs::read_to_string(file_path).expect("Couldn't find or open file."); + + let model_info: JsonData = serde_json::from_str(&contents)?; + + Ok(model_info.device()) + } +} diff --git a/fastpair/rust/demo/rust/src/fetcher/mock.rs b/fastpair/rust/demo/rust/src/fetcher/mock.rs new file mode 100644 index 00000000..93f9351f --- /dev/null +++ b/fastpair/rust/demo/rust/src/fetcher/mock.rs @@ -0,0 +1,43 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::{ + advertisement::ModelId, + fetcher::{DeviceInfo, FpFetcher}, +}; + +/// A struct for mocking retrieval of Fast Pair data. +pub(crate) struct FpFetcherMock { + get_device_info_from_model_id: Result, +} + +impl FpFetcherMock { + pub(crate) fn new(get_device_info_from_model_id: Result) -> Self { + FpFetcherMock { + get_device_info_from_model_id, + } + } +} + +impl FpFetcher for FpFetcherMock { + fn get_device_info_from_model_id( + &self, + _model_id: &ModelId, + ) -> Result { + match &self.get_device_info_from_model_id { + Ok(result) => Ok(result.clone()), + Err(_) => Err(anyhow::anyhow!("intentional mock error")), + } + } +} diff --git a/fastpair/rust/demo/rust/src/fetcher/mod.rs b/fastpair/rust/demo/rust/src/fetcher/mod.rs new file mode 100644 index 00000000..98772f6e --- /dev/null +++ b/fastpair/rust/demo/rust/src/fetcher/mod.rs @@ -0,0 +1,22 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +pub(crate) mod common; +pub(crate) mod fs; + +#[cfg(test)] +pub(crate) mod mock; + +pub(crate) use common::*; +pub(crate) use fs::*; From fb95e26bf59dd39381d21194d228035f383755cd Mon Sep 17 00:00:00 2001 From: Lucas Silva Shepard Date: Tue, 15 Aug 2023 13:31:56 -0700 Subject: [PATCH 4/4] [fp-rs] Added FP Advertisement unit tests in Rust. --- .../bluetooth/src/common/advertisement.rs | 2 +- fastpair/rust/bluetooth/src/lib.rs | 4 +- fastpair/rust/demo/rust/src/advertisement.rs | 125 ++++++++++++++++-- fastpair/rust/demo/rust/src/api.rs | 12 +- 4 files changed, 125 insertions(+), 18 deletions(-) diff --git a/fastpair/rust/bluetooth/src/common/advertisement.rs b/fastpair/rust/bluetooth/src/common/advertisement.rs index 93413079..6eb679f3 100644 --- a/fastpair/rust/bluetooth/src/common/advertisement.rs +++ b/fastpair/rust/bluetooth/src/common/advertisement.rs @@ -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, tx_power: Option, diff --git a/fastpair/rust/bluetooth/src/lib.rs b/fastpair/rust/bluetooth/src/lib.rs index ebe8384a..99da6362 100644 --- a/fastpair/rust/bluetooth/src/lib.rs +++ b/fastpair/rust/bluetooth/src/lib.rs @@ -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! { diff --git a/fastpair/rust/demo/rust/src/advertisement.rs b/fastpair/rust/demo/rust/src/advertisement.rs index 58cc3338..9179ad75 100644 --- a/fastpair/rust/demo/rust/src/advertisement.rs +++ b/fastpair/rust/demo/rust/src/advertisement.rs @@ -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, + fetcher: &Box, ) -> Result { 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 = 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 = 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 = 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 = 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 = 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()); + } +} diff --git a/fastpair/rust/demo/rust/src/api.rs b/fastpair/rust/demo/rust/src/api.rs index e80ae950..bb1b3a2f 100644 --- a/fastpair/rust/demo/rust/src/api.rs +++ b/fastpair/rust/demo/rust/src/api.rs @@ -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>>> = 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, + fetcher: &Box, latest_advertisement_map: &mut HashMap, ) -> Option { // 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 = 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;