mirror of
https://github.com/kidfromjupiter/nearby.git
synced 2026-09-14 14:46:12 -04:00
Merge pull request #2070 from TheShepord:fpwinrs_tests
PiperOrigin-RevId: 557357785
This commit is contained in:
@@ -93,3 +93,87 @@ impl From<ClassicAddress> 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<ClassicAddress, BluetoothError> =
|
||||
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<ClassicAddress, BluetoothError> =
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>,
|
||||
@@ -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<U: Copy> {
|
||||
uuid: U,
|
||||
data: Vec<u8>,
|
||||
@@ -115,3 +115,58 @@ impl<U: Copy> ServiceData<U> {
|
||||
&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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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::{FpFetcher, FpFetcherLocal},
|
||||
};
|
||||
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 = FpFetcherLocal::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;
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
+22
-41
@@ -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<DeviceInfo, anyhow::Error>;
|
||||
}
|
||||
|
||||
/// 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<DeviceInfo, anyhow::Error> {
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -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<DeviceInfo, anyhow::Error> {
|
||||
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())
|
||||
}
|
||||
}
|
||||
@@ -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<DeviceInfo, anyhow::Error>,
|
||||
}
|
||||
|
||||
impl FpFetcherMock {
|
||||
pub(crate) fn new(get_device_info_from_model_id: Result<DeviceInfo, anyhow::Error>) -> Self {
|
||||
FpFetcherMock {
|
||||
get_device_info_from_model_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FpFetcher for FpFetcherMock {
|
||||
fn get_device_info_from_model_id(
|
||||
&self,
|
||||
_model_id: &ModelId,
|
||||
) -> Result<DeviceInfo, anyhow::Error> {
|
||||
match &self.get_device_info_from_model_id {
|
||||
Ok(result) => Ok(result.clone()),
|
||||
Err(_) => Err(anyhow::anyhow!("intentional mock error")),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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::*;
|
||||
Reference in New Issue
Block a user