|  | // 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(_)))); | 
|  | } |