Skip to main content

bt_gatt/
test_utils.rs

1// Copyright 2023 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use bt_common::core::{Address, AddressType};
6use futures::channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender};
7use futures::future::{ready, Ready};
8use futures::stream::{FusedStream, Stream};
9use parking_lot::Mutex;
10use std::collections::{HashMap, HashSet, VecDeque};
11use std::sync::Arc;
12use std::task::{Poll, Waker};
13
14use bt_common::{PeerId, Uuid};
15
16use crate::central::ScanResult;
17use crate::client::CharacteristicNotification;
18
19use crate::periodic_advertising::{PeriodicAdvertising, SyncReport};
20use crate::pii::GetPeerAddr;
21use crate::server::{
22    self, LocalService, NotificationType, ReadResponder, ServiceDefinition, WriteResponder,
23};
24use crate::{types::*, GattTypes, ServerTypes};
25
26#[derive(Default)]
27struct FakePeerServiceInner {
28    // Notifier that's used to send out notification.
29    notifiers: HashMap<Handle, UnboundedSender<Result<CharacteristicNotification>>>,
30
31    // Characteristics to return when `read_characteristic` and `discover_characteristics` are
32    // called.
33    characteristics: HashMap<Handle, (Characteristic, Vec<u8>)>,
34}
35
36#[derive(Clone)]
37pub struct FakePeerService {
38    inner: Arc<Mutex<FakePeerServiceInner>>,
39}
40
41impl FakePeerService {
42    pub fn new() -> Self {
43        Self { inner: Arc::new(Mutex::new(Default::default())) }
44    }
45
46    // Adds a characteristic so that it can be returned when discover/read method is
47    // called.
48    // Also triggers sending a characteristic value change notification to be sent.
49    pub fn add_characteristic(&mut self, char: Characteristic, value: Vec<u8>) {
50        let mut lock = self.inner.lock();
51        let handle = char.handle;
52        lock.characteristics.insert(handle, (char, value.clone()));
53        if let Some(notifier) = lock.notifiers.get_mut(&handle) {
54            notifier
55                .unbounded_send(Ok(CharacteristicNotification {
56                    handle,
57                    value,
58                    maybe_truncated: false,
59                }))
60                .expect("should succeed");
61        }
62    }
63
64    // Sets expected characteristic value so that it can be used for validation when
65    // write method is called.
66    pub fn expect_characteristic_value(&mut self, handle: &Handle, value: Vec<u8>) {
67        let mut lock = self.inner.lock();
68        let Some(char) = lock.characteristics.get_mut(handle) else {
69            panic!("Can't find characteristic {handle:?} to set expected value");
70        };
71        char.1 = value;
72    }
73
74    /// Sends a notification on the characteristic with the provided `handle`.
75    pub fn notify(&self, handle: &Handle, notification: Result<CharacteristicNotification>) {
76        let mut lock = self.inner.lock();
77        if let Some(notifier) = lock.notifiers.get_mut(handle) {
78            notifier.unbounded_send(notification).expect("can send notification");
79        }
80    }
81
82    /// Removes the notification subscription for the characteristic with the
83    /// provided `handle`.
84    pub fn clear_notifier(&self, handle: &Handle) {
85        let mut lock = self.inner.lock();
86        let _ = lock.notifiers.remove(handle);
87    }
88}
89
90impl crate::client::PeerService<FakeTypes> for FakePeerService {
91    fn discover_characteristics(
92        &self,
93        uuid: Option<Uuid>,
94    ) -> <FakeTypes as GattTypes>::CharacteristicDiscoveryFut {
95        let lock = self.inner.lock();
96        let mut result = Vec::new();
97        for (_handle, (char, _value)) in &lock.characteristics {
98            match uuid {
99                Some(uuid) if uuid == char.uuid => result.push(char.clone()),
100                None => result.push(char.clone()),
101                _ => {}
102            }
103        }
104        ready(Ok(result))
105    }
106
107    fn read_characteristic<'a>(
108        &self,
109        handle: &Handle,
110        _offset: u16,
111        buf: &'a mut [u8],
112    ) -> <FakeTypes as GattTypes>::ReadFut<'a> {
113        let read_characteristics = &(*self.inner.lock()).characteristics;
114        let Some((_, value)) = read_characteristics.get(handle) else {
115            return ready(Err(Error::Gatt(GattError::InvalidHandle)));
116        };
117        buf[..value.len()].copy_from_slice(value.as_slice());
118        ready(Ok((value.len(), false)))
119    }
120
121    // For testing, should call `expect_characteristic_value` with the expected
122    // value.
123    fn write_characteristic<'a>(
124        &self,
125        handle: &Handle,
126        _mode: WriteMode,
127        _offset: u16,
128        buf: &'a [u8],
129    ) -> <FakeTypes as GattTypes>::WriteFut<'a> {
130        let expected_characteristics = &(*self.inner.lock()).characteristics;
131        // The write operation was not expected.
132        let Some((_, expected)) = expected_characteristics.get(handle) else {
133            panic!("Write operation to characteristic {handle:?} was not expected");
134        };
135        // Value written was not expected.
136        if buf.len() != expected.len() || &buf[..expected.len()] != expected.as_slice() {
137            panic!("Value written to characteristic {handle:?} was not expected: {buf:?}");
138        }
139        ready(Ok(()))
140    }
141
142    fn read_descriptor<'a>(
143        &self,
144        _handle: &Handle,
145        _offset: u16,
146        _buf: &'a mut [u8],
147    ) -> <FakeTypes as GattTypes>::ReadFut<'a> {
148        todo!()
149    }
150
151    fn write_descriptor<'a>(
152        &self,
153        _handle: &Handle,
154        _offset: u16,
155        _buf: &'a [u8],
156    ) -> <FakeTypes as GattTypes>::WriteFut<'a> {
157        todo!()
158    }
159
160    fn subscribe(&self, handle: &Handle) -> <FakeTypes as GattTypes>::NotificationStream {
161        let (sender, receiver) = unbounded();
162        (*self.inner.lock()).notifiers.insert(*handle, sender);
163        receiver
164    }
165}
166
167#[derive(Clone)]
168pub struct FakeServiceHandle {
169    pub uuid: Uuid,
170    pub is_primary: bool,
171    pub fake_service: FakePeerService,
172}
173
174impl crate::client::PeerServiceHandle<FakeTypes> for FakeServiceHandle {
175    fn uuid(&self) -> Uuid {
176        self.uuid
177    }
178
179    fn is_primary(&self) -> bool {
180        self.is_primary
181    }
182
183    fn connect(&self) -> <FakeTypes as GattTypes>::ServiceConnectFut {
184        futures::future::ready(Ok(self.fake_service.clone()))
185    }
186}
187
188#[derive(Default)]
189struct FakeClientInner {
190    fake_services: Vec<FakeServiceHandle>,
191}
192
193#[derive(Clone)]
194pub struct FakeClient {
195    inner: Arc<Mutex<FakeClientInner>>,
196}
197
198impl FakeClient {
199    pub fn new() -> Self {
200        FakeClient { inner: Arc::new(Mutex::new(FakeClientInner::default())) }
201    }
202
203    /// Add a fake peer service to this client.
204    pub fn add_service(&mut self, uuid: Uuid, is_primary: bool, fake_service: FakePeerService) {
205        self.inner.lock().fake_services.push(FakeServiceHandle { uuid, is_primary, fake_service });
206    }
207}
208
209impl crate::Client<FakeTypes> for FakeClient {
210    fn peer_id(&self) -> PeerId {
211        todo!()
212    }
213
214    fn find_service(&self, uuid: Uuid) -> <FakeTypes as GattTypes>::FindServicesFut {
215        let fake_services = &self.inner.lock().fake_services;
216        let mut filtered_services = Vec::new();
217        for handle in fake_services {
218            if handle.uuid == uuid {
219                filtered_services.push(handle.clone());
220            }
221        }
222
223        futures::future::ready(Ok(filtered_services))
224    }
225}
226
227#[derive(Default, Debug)]
228struct ScannedResultStreamInner {
229    results: VecDeque<Result<ScanResult>>,
230    waker: Option<Waker>,
231}
232
233#[derive(Clone, Debug, Default)]
234pub struct ScannedResultStreamController(Arc<Mutex<ScannedResultStreamInner>>);
235
236impl ScannedResultStreamController {
237    /// Add a single scanned result item to output from the stream.
238    pub fn add_scanned_result(&self, item: Result<ScanResult>) {
239        let mut lock = self.0.lock();
240        lock.results.push_back(item);
241        if let Some(waker) = lock.waker.take() {
242            waker.wake();
243        }
244    }
245}
246
247#[derive(Debug, Default)]
248pub struct ScannedResultStream {
249    inner: Arc<Mutex<ScannedResultStreamInner>>,
250}
251
252impl ScannedResultStream {
253    /// Creates a new ScannedResultStream.
254    /// Client can get a ScannedResultStreamController using the `controller`
255    /// method.
256    pub fn new() -> Self {
257        Self::default()
258    }
259
260    pub fn controller(&self) -> ScannedResultStreamController {
261        ScannedResultStreamController(self.inner.clone())
262    }
263}
264
265impl FusedStream for ScannedResultStream {
266    fn is_terminated(&self) -> bool {
267        self.inner.lock().results.is_empty()
268    }
269}
270
271impl Stream for ScannedResultStream {
272    type Item = Result<ScanResult>;
273
274    fn poll_next(
275        self: std::pin::Pin<&mut Self>,
276        cx: &mut std::task::Context<'_>,
277    ) -> Poll<Option<Self::Item>> {
278        let mut lock = self.inner.lock();
279        match lock.results.pop_front() {
280            Some(result) => Poll::Ready(Some(result)),
281            None => {
282                lock.waker = Some(cx.waker().clone());
283                Poll::Pending
284            }
285        }
286    }
287}
288
289/// Implements a fake [`GetPeerAddr`] that just converts the peer_id into a
290/// public [`Address`] based on the given peer_id.
291pub struct FakeGetPeerAddr;
292
293impl GetPeerAddr for FakeGetPeerAddr {
294    async fn get_peer_address(&self, peer_id: PeerId) -> Result<(Address, AddressType)> {
295        Ok((
296            [
297                peer_id.0 as u8,
298                ((peer_id.0 >> 8) & 0xff) as u8,
299                ((peer_id.0 >> 16) & 0xff) as u8,
300                ((peer_id.0 >> 24) & 0xff) as u8,
301                ((peer_id.0 >> 32) & 0xff) as u8,
302                ((peer_id.0 >> 48) & 0xff) as u8,
303            ],
304            AddressType::Public,
305        ))
306    }
307}
308
309pub struct FakeTypes {}
310
311impl GattTypes for FakeTypes {
312    type Central = FakeCentral;
313    type ScanResultStream = ScannedResultStream;
314    type Client = FakeClient;
315    type ConnectFuture = Ready<Result<FakeClient>>;
316    type PeerServiceHandle = FakeServiceHandle;
317    type FindServicesFut = Ready<Result<Vec<FakeServiceHandle>>>;
318    type PeerService = FakePeerService;
319    type ServiceConnectFut = Ready<Result<FakePeerService>>;
320    type CharacteristicDiscoveryFut = Ready<Result<Vec<Characteristic>>>;
321    type NotificationStream = UnboundedReceiver<Result<CharacteristicNotification>>;
322    type ReadFut<'a> = Ready<Result<(usize, bool)>>;
323    type WriteFut<'a> = Ready<Result<()>>;
324    type PeriodicAdvertising = FakePeriodicAdvertising;
325}
326
327impl ServerTypes for FakeTypes {
328    type Server = FakeServer;
329    type LocalService = FakeLocalService;
330    type LocalServiceFut = Ready<Result<FakeLocalService>>;
331    type ServiceEventStream = UnboundedReceiver<Result<server::ServiceEvent<FakeTypes>>>;
332    type ServiceWriteType = Vec<u8>;
333    type ReadResponder = FakeResponder;
334    type WriteResponder = FakeResponder;
335    type IndicateConfirmationStream = UnboundedReceiver<Result<server::ConfirmationEvent>>;
336}
337
338#[derive(Default)]
339struct FakePeriodicAdvertisingInner {
340    sync_registry: HashMap<PeerId, UnboundedSender<Result<SyncReport>>>,
341}
342
343#[derive(Clone, Default)]
344pub struct FakePeriodicAdvertising {
345    inner: Arc<Mutex<FakePeriodicAdvertisingInner>>,
346}
347
348impl FakePeriodicAdvertising {
349    pub fn new() -> Self {
350        Self::default()
351    }
352
353    pub fn get_sender(&self, peer_id: PeerId) -> Option<UnboundedSender<Result<SyncReport>>> {
354        self.inner.lock().sync_registry.get(&peer_id).cloned()
355    }
356}
357
358impl PeriodicAdvertising for FakePeriodicAdvertising {
359    type SyncFut = Ready<Result<Self::SyncStream>>;
360    type SyncStream = UnboundedReceiver<Result<SyncReport>>;
361
362    fn sync_to_advertising_reports(
363        &self,
364        peer_id: PeerId,
365        _advertising_sid: u8,
366        _config: crate::periodic_advertising::SyncConfiguration,
367    ) -> Self::SyncFut {
368        let (tx, rx) = unbounded();
369        self.inner.lock().sync_registry.insert(peer_id, tx);
370        ready(Ok(rx))
371    }
372}
373
374#[derive(Default)]
375pub struct FakeCentralInner {
376    clients: HashMap<PeerId, FakeClient>,
377    pub(crate) periodic_advertising: FakePeriodicAdvertising,
378}
379
380#[derive(Clone)]
381pub struct FakeCentral {
382    inner: Arc<Mutex<FakeCentralInner>>,
383}
384
385impl FakeCentral {
386    pub fn new() -> Self {
387        Self { inner: Arc::new(Mutex::new(FakeCentralInner::default())) }
388    }
389
390    pub fn add_client(&mut self, peer_id: PeerId, client: FakeClient) {
391        let _ = self.inner.lock().clients.insert(peer_id, client);
392    }
393}
394
395impl crate::Central<FakeTypes> for FakeCentral {
396    fn scan(&self, _filters: &[crate::central::ScanFilter]) -> ScannedResultStream {
397        ScannedResultStream::default()
398    }
399
400    fn connect(&self, peer_id: PeerId) -> <FakeTypes as GattTypes>::ConnectFuture {
401        let clients = &self.inner.lock().clients;
402        let res = match clients.get(&peer_id) {
403            Some(client) => Ok(client.clone()),
404            None => Err(Error::PeerDisconnected(peer_id)),
405        };
406        futures::future::ready(res)
407    }
408
409    fn periodic_advertising(&self) -> Result<<FakeTypes as GattTypes>::PeriodicAdvertising> {
410        Ok(self.inner.lock().periodic_advertising.clone())
411    }
412}
413
414#[derive(Debug)]
415pub enum FakeServerEvent {
416    ReadResponded {
417        service_id: server::ServiceId,
418        handle: Handle,
419        value: Result<Vec<u8>>,
420    },
421    WriteResponded {
422        service_id: server::ServiceId,
423        handle: Handle,
424        value: Result<()>,
425    },
426    Notified {
427        service_id: server::ServiceId,
428        handle: Handle,
429        value: Vec<u8>,
430        peers: Vec<PeerId>,
431    },
432    Indicated {
433        service_id: server::ServiceId,
434        handle: Handle,
435        value: Vec<u8>,
436        peers: Vec<PeerId>,
437        confirmations: UnboundedSender<Result<server::ConfirmationEvent>>,
438    },
439    Unpublished {
440        id: server::ServiceId,
441    },
442    Published {
443        id: server::ServiceId,
444        definition: ServiceDefinition,
445    },
446}
447
448#[derive(Debug)]
449struct FakeServerInner {
450    services: HashMap<server::ServiceId, ServiceDefinition>,
451    service_senders:
452        HashMap<server::ServiceId, UnboundedSender<Result<server::ServiceEvent<FakeTypes>>>>,
453    sender: UnboundedSender<FakeServerEvent>,
454    notification_peers: HashSet<PeerId>,
455    indication_peers: HashSet<PeerId>,
456}
457
458#[derive(Clone, Debug)]
459pub struct FakeServer {
460    inner: Arc<Mutex<FakeServerInner>>,
461}
462
463impl server::Server<FakeTypes> for FakeServer {
464    fn prepare(
465        &self,
466        service: server::ServiceDefinition,
467    ) -> <FakeTypes as ServerTypes>::LocalServiceFut {
468        let id = service.id();
469        self.inner.lock().services.insert(id, service);
470        futures::future::ready(Ok(FakeLocalService::new(id, self.inner.clone())))
471    }
472}
473
474impl FakeServer {
475    pub fn new() -> (Self, UnboundedReceiver<FakeServerEvent>) {
476        let (sender, receiver) = futures::channel::mpsc::unbounded();
477        (
478            Self {
479                inner: Arc::new(Mutex::new(FakeServerInner {
480                    services: Default::default(),
481                    service_senders: Default::default(),
482                    sender,
483                    notification_peers: HashSet::new(),
484                    indication_peers: HashSet::new(),
485                })),
486            },
487            receiver,
488        )
489    }
490
491    pub fn service(&self, id: server::ServiceId) -> Option<ServiceDefinition> {
492        self.inner.lock().services.get(&id).cloned()
493    }
494
495    pub fn incoming_write(
496        &self,
497        peer_id: PeerId,
498        id: server::ServiceId,
499        handle: Handle,
500        offset: u32,
501        value: Vec<u8>,
502    ) {
503        // TODO: check that the write is allowed
504        let sender = self.inner.lock().sender.clone();
505        self.inner
506            .lock()
507            .service_senders
508            .get(&id)
509            .unwrap()
510            .unbounded_send(Ok(server::ServiceEvent::Write {
511                peer_id,
512                handle,
513                offset,
514                value,
515                responder: FakeResponder { sender, service_id: id, handle },
516            }))
517            .unwrap();
518    }
519
520    pub fn incoming_read(
521        &self,
522        peer_id: PeerId,
523        id: server::ServiceId,
524        handle: Handle,
525        offset: u32,
526    ) {
527        // TODO: check that the read is allowed
528        let sender = self.inner.lock().sender.clone();
529        self.inner
530            .lock()
531            .service_senders
532            .get(&id)
533            .unwrap()
534            .unbounded_send(Ok(server::ServiceEvent::Read {
535                peer_id,
536                handle,
537                offset,
538                responder: FakeResponder { sender, service_id: id, handle },
539            }))
540            .unwrap();
541    }
542
543    pub fn incoming_client_configuration(
544        &self,
545        peer_id: PeerId,
546        id: server::ServiceId,
547        handle: Handle,
548        notification_type: NotificationType,
549    ) {
550        let mut inner = self.inner.lock();
551        match notification_type {
552            NotificationType::Notify => {
553                inner.notification_peers.insert(peer_id);
554            }
555            NotificationType::Indicate => {
556                inner.indication_peers.insert(peer_id);
557            }
558            NotificationType::Disable => {
559                inner.notification_peers.remove(&peer_id);
560                inner.indication_peers.remove(&peer_id);
561            }
562        }
563        inner
564            .service_senders
565            .get(&id)
566            .unwrap()
567            .unbounded_send(Ok(server::ServiceEvent::ClientConfiguration {
568                peer_id,
569                handle,
570                notification_type,
571            }))
572            .unwrap();
573    }
574}
575
576pub struct FakeLocalService {
577    id: server::ServiceId,
578    inner: Arc<Mutex<FakeServerInner>>,
579}
580
581impl FakeLocalService {
582    fn new(id: server::ServiceId, inner: Arc<Mutex<FakeServerInner>>) -> Self {
583        Self { id, inner }
584    }
585}
586
587impl Drop for FakeLocalService {
588    fn drop(&mut self) {
589        self.inner.lock().services.remove(&self.id);
590    }
591}
592
593impl LocalService<FakeTypes> for FakeLocalService {
594    fn publish(&self) -> <FakeTypes as ServerTypes>::ServiceEventStream {
595        let (sender, receiver) = futures::channel::mpsc::unbounded();
596        let _ = self.inner.lock().service_senders.insert(self.id, sender);
597        let definition = self.inner.lock().services.get(&self.id).unwrap().clone();
598        self.inner
599            .lock()
600            .sender
601            .unbounded_send(FakeServerEvent::Published { id: self.id, definition })
602            .unwrap();
603        receiver
604    }
605
606    fn notify(&self, characteristic: &Handle, data: &[u8], peers: &[PeerId]) {
607        let inner = self.inner.lock();
608        let peers_to_notify: HashSet<_> = if peers.is_empty() {
609            inner.notification_peers.clone()
610        } else {
611            peers.iter().filter(|p| inner.notification_peers.contains(p)).cloned().collect()
612        };
613
614        if !peers_to_notify.is_empty() {
615            inner
616                .sender
617                .unbounded_send(FakeServerEvent::Notified {
618                    service_id: self.id,
619                    handle: *characteristic,
620                    value: data.into(),
621                    peers: peers_to_notify.into_iter().collect(),
622                })
623                .unwrap();
624        }
625    }
626
627    fn indicate(
628        &self,
629        characteristic: &Handle,
630        data: &[u8],
631        peers: &[PeerId],
632    ) -> <FakeTypes as ServerTypes>::IndicateConfirmationStream {
633        let (sender, receiver) = futures::channel::mpsc::unbounded();
634        let inner = self.inner.lock();
635        let peers_to_indicate: HashSet<_> = if peers.is_empty() {
636            inner.indication_peers.clone()
637        } else {
638            peers.iter().filter(|p| inner.indication_peers.contains(p)).cloned().collect()
639        };
640
641        if !peers_to_indicate.is_empty() {
642            inner
643                .sender
644                .unbounded_send(FakeServerEvent::Indicated {
645                    service_id: self.id,
646                    handle: *characteristic,
647                    value: data.into(),
648                    peers: peers_to_indicate.into_iter().collect(),
649                    confirmations: sender,
650                })
651                .unwrap();
652        }
653        receiver
654    }
655}
656
657pub struct FakeResponder {
658    sender: UnboundedSender<FakeServerEvent>,
659    service_id: server::ServiceId,
660    handle: Handle,
661}
662
663impl ReadResponder for FakeResponder {
664    fn respond(self, value: &[u8]) {
665        self.sender
666            .unbounded_send(FakeServerEvent::ReadResponded {
667                service_id: self.service_id,
668                handle: self.handle,
669                value: Ok(value.into()),
670            })
671            .unwrap();
672    }
673
674    fn error(self, error: GattError) {
675        self.sender
676            .unbounded_send(FakeServerEvent::ReadResponded {
677                service_id: self.service_id,
678                handle: self.handle,
679                value: Err(Error::Gatt(error)),
680            })
681            .unwrap();
682    }
683}
684
685impl WriteResponder for FakeResponder {
686    fn acknowledge(self) {
687        self.sender
688            .unbounded_send(FakeServerEvent::WriteResponded {
689                service_id: self.service_id,
690                handle: self.handle,
691                value: Ok(()),
692            })
693            .unwrap();
694    }
695
696    fn error(self, error: GattError) {
697        self.sender
698            .unbounded_send(FakeServerEvent::WriteResponded {
699                service_id: self.service_id,
700                handle: self.handle,
701                value: Err(Error::Gatt(error)),
702            })
703            .unwrap();
704    }
705}