| // 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::{FutureExt, StreamExt}; |
| |
| use bt_common::Uuid; |
| |
| use crate::test_utils::*; |
| use crate::types::*; |
| use crate::{Central, central::Filter, client::PeerService}; |
| |
| 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_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(_)))); |
| } |