blob: 2593b2f56e1415e133c05b7c928117daccd14d1d [file] [log] [blame]
// Copyright 2023 Google LLC
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std::task::Poll;
use assert_matches::assert_matches;
use futures::{Future, FutureExt, StreamExt};
use bt_common::{PeerId, Uuid};
use crate::test_utils::*;
use crate::types::*;
use crate::{central::Filter, client::PeerService, Central};
const TEST_UUID_1: Uuid = Uuid::from_u16(0x1234);
const TEST_UUID_2: Uuid = Uuid::from_u16(0x2345);
const TEST_UUID_3: Uuid = Uuid::from_u16(0x3456);
// Sets up a fake peer service with some characteristics.
fn set_up() -> FakePeerService {
let mut fake_peer_service = FakePeerService::new();
fake_peer_service.add_characteristic(
Characteristic {
handle: Handle(1),
uuid: TEST_UUID_1,
properties: CharacteristicProperties(vec![
CharacteristicProperty::Broadcast,
CharacteristicProperty::Notify,
]),
permissions: AttributePermissions::default(),
descriptors: vec![],
},
vec![],
);
fake_peer_service.add_characteristic(
Characteristic {
handle: Handle(2),
uuid: TEST_UUID_1,
properties: CharacteristicProperties(vec![
CharacteristicProperty::Broadcast,
CharacteristicProperty::Notify,
]),
permissions: AttributePermissions::default(),
descriptors: vec![],
},
vec![],
);
fake_peer_service.add_characteristic(
Characteristic {
handle: Handle(3),
uuid: TEST_UUID_1,
properties: CharacteristicProperties(vec![
CharacteristicProperty::Broadcast,
CharacteristicProperty::Notify,
]),
permissions: AttributePermissions::default(),
descriptors: vec![],
},
vec![],
);
fake_peer_service.add_characteristic(
Characteristic {
handle: Handle(4),
uuid: TEST_UUID_2,
properties: CharacteristicProperties(vec![CharacteristicProperty::Notify]),
permissions: AttributePermissions::default(),
descriptors: vec![],
},
vec![],
);
fake_peer_service.add_characteristic(
Characteristic {
handle: Handle(5),
uuid: TEST_UUID_3,
properties: CharacteristicProperties(vec![CharacteristicProperty::Broadcast]),
permissions: AttributePermissions::default(),
descriptors: vec![],
},
vec![],
);
fake_peer_service
}
#[test]
fn peer_service_discover_characteristics_works() {
let mut noop_cx = futures::task::Context::from_waker(futures::task::noop_waker_ref());
let fake_peer_service = set_up();
let mut discover_results = fake_peer_service.discover_characteristics(Some(TEST_UUID_1));
let polled = discover_results.poll_unpin(&mut noop_cx);
assert_matches!(polled, Poll::Ready(Ok(chars)) => { assert_eq!(chars.len(), 3) });
let mut discover_results = fake_peer_service.discover_characteristics(Some(TEST_UUID_2));
let polled = discover_results.poll_unpin(&mut noop_cx);
assert_matches!(polled, Poll::Ready(Ok(chars)) => { assert_eq!(chars.len(), 1) });
let mut discover_results =
fake_peer_service.discover_characteristics(Some(Uuid::from_u16(0xFF22)));
let polled = discover_results.poll_unpin(&mut noop_cx);
assert_matches!(polled, Poll::Ready(Ok(chars)) => { assert_eq!(chars.len(), 0) });
let mut discover_results = fake_peer_service.discover_characteristics(None);
let polled = discover_results.poll_unpin(&mut noop_cx);
assert_matches!(polled, Poll::Ready(Ok(chars)) => { assert_eq!(chars.len(), 5) });
}
#[test]
fn peer_service_read_characteristic() {
let mut noop_cx = futures::task::Context::from_waker(futures::task::noop_waker_ref());
let mut fake_peer_service = set_up();
let mut buf = vec![0; 255];
// For characteristic that was added, value is returned
let mut read_result = fake_peer_service.read_characteristic(&Handle(0x1), 0, &mut buf[..]);
let polled: Poll<std::prelude::v1::Result<(usize, bool), Error>> =
read_result.poll_unpin(&mut noop_cx);
assert_matches!(polled, Poll::Ready(Ok((len, _))) => { assert_eq!(len, 0) });
// For characteristic that doesn't exist, fails.
let mut read_result = fake_peer_service.read_characteristic(&Handle(0xF), 0, &mut buf[..]);
let polled = read_result.poll_unpin(&mut noop_cx);
assert_matches!(polled, Poll::Ready(Err(_)));
// Change the value for characteristic with handle 1.
fake_peer_service.add_characteristic(
Characteristic {
handle: Handle(1),
uuid: TEST_UUID_1,
properties: CharacteristicProperties(vec![
CharacteristicProperty::Broadcast,
CharacteristicProperty::Notify,
]),
permissions: AttributePermissions::default(),
descriptors: vec![],
},
vec![0, 1, 2, 3],
);
// Successfully reads the updated value.
let mut read_result = fake_peer_service.read_characteristic(&Handle(0x1), 0, &mut buf[..]);
let polled = read_result.poll_unpin(&mut noop_cx);
assert_matches!(polled, Poll::Ready(Ok((len, _))) => {
assert_eq!(len, 4);
assert_eq!(buf[..len], vec![0,1,2,3]);
});
}
#[test]
fn peer_service_write_characteristic() {
let mut noop_cx = futures::task::Context::from_waker(futures::task::noop_waker_ref());
let mut fake_peer_service: FakePeerService = set_up();
// Set expected characteristic value before calling `write_characteristic`.
fake_peer_service.expect_characteristic_value(&Handle(1), vec![0, 1, 2, 3]);
let mut write_result = fake_peer_service.write_characteristic(
&Handle(0x1),
WriteMode::WithoutResponse,
0,
vec![0, 1, 2, 3].as_slice(),
);
match write_result.poll_unpin(&mut noop_cx) {
Poll::Ready(Ok(())) => {}
_ => panic!("expected write to succeed"),
}
}
#[test]
#[should_panic]
fn peer_service_write_characteristic_fail() {
let mut noop_cx = futures::task::Context::from_waker(futures::task::noop_waker_ref());
let fake_peer_service = set_up();
// Write some random value without calling `expect_characteristic_value` first.
let mut write_result = fake_peer_service.write_characteristic(
&Handle(0x1),
WriteMode::WithoutResponse,
0,
vec![13, 14, 15, 16].as_slice(),
);
match write_result.poll_unpin(&mut noop_cx) {
Poll::Ready(Err(_)) => {}
_ => panic!("expected write to fail"),
}
}
#[test]
fn peer_service_subsribe() {
let mut noop_cx = futures::task::Context::from_waker(futures::task::noop_waker_ref());
let mut fake_peer_service = set_up();
let mut notification_stream = fake_peer_service.subscribe(&Handle(0x1));
// Stream is empty unless we add an item through the FakeNotificationStream
// struct.
assert!(notification_stream.poll_next_unpin(&mut noop_cx).is_pending());
// Update the characteristic value so that notification is sent.
fake_peer_service.add_characteristic(
Characteristic {
handle: Handle(1),
uuid: TEST_UUID_1,
properties: CharacteristicProperties(vec![
CharacteristicProperty::Broadcast,
CharacteristicProperty::Notify,
]),
permissions: AttributePermissions::default(),
descriptors: vec![],
},
vec![0, 1, 2, 3],
);
// Stream should be ready.
let polled = notification_stream.poll_next_unpin(&mut noop_cx);
assert_matches!(polled, Poll::Ready(Some(Ok(notification))) => {
assert_eq!(notification.handle, Handle(0x1));
assert_eq!(notification.value, vec![0,1,2,3]);
assert_eq!(notification.maybe_truncated, false);
});
assert!(notification_stream.poll_next_unpin(&mut noop_cx).is_pending());
}
#[test]
fn central_search_works() {
let mut noop_cx = futures::task::Context::from_waker(futures::task::noop_waker_ref());
let central = FakeCentral::default();
let mut scan_results = central.scan(&[Filter::ServiceUuid(Uuid::from_u16(0x1844)).into()]);
let polled = scan_results.poll_next_unpin(&mut noop_cx);
assert_matches!(polled, Poll::Ready(Some(Ok(_))));
}
fn boxed_generic_usage<T: crate::GattTypes>(central: Box<dyn crate::Central<T>>) {
use crate::client::PeerServiceHandle;
let mut noop_cx = futures::task::Context::from_waker(futures::task::noop_waker_ref());
let _stream = central.scan(&[]);
let connect_fut = central.connect(PeerId(1));
futures::pin_mut!(connect_fut);
let Poll::Ready(Ok(client)) = connect_fut.poll(&mut noop_cx) else {
panic!("Connect should be ready Ok");
};
let client_boxed: Box<dyn crate::client::Client<T>> = Box::new(client);
let find_serv_fut = client_boxed.find_service(Uuid::from_u16(0));
futures::pin_mut!(find_serv_fut);
let Poll::Ready(Ok(services)) = find_serv_fut.poll(&mut noop_cx) else {
panic!("Expected services future to resolve");
};
assert_eq!(services.len(), 1);
let connect_service_fut = services[0].connect();
futures::pin_mut!(connect_service_fut);
let Poll::Ready(Ok(service)) = connect_service_fut.poll(&mut noop_cx) else {
panic!("Expected service to connect");
};
let _service_box: Box<dyn crate::client::PeerService<T>> = Box::new(service);
}
#[test]
fn central_dynamic_usage() {
let central: Box<dyn crate::Central<FakeTypes>> = Box::new(FakeCentral::default());
boxed_generic_usage(central);
}