1 mod utils;
2 
3 use std::{rc::Rc, time::Duration};
4 
5 use bluetooth_core::{
6     gatt::{
7         callbacks::{
8             CallbackResponseError, CallbackTransactionManager, GattWriteRequestType, GattWriteType,
9             RawGattDatastore, TransactionDecision,
10         },
11         ffi::AttributeBackingType,
12         ids::{AttHandle, ConnectionId, ServerId, TransactionId, TransportIndex},
13         mocks::mock_callbacks::{MockCallbackEvents, MockCallbacks},
14     },
15     packets::AttErrorCode,
16 };
17 use tokio::{sync::mpsc::UnboundedReceiver, task::spawn_local, time::Instant};
18 use utils::start_test;
19 
20 const TCB_IDX: TransportIndex = TransportIndex(1);
21 const SERVER_ID: ServerId = ServerId(2);
22 
23 const CONN_ID: ConnectionId = ConnectionId::new(TCB_IDX, SERVER_ID);
24 
25 const HANDLE_1: AttHandle = AttHandle(3);
26 const BACKING_TYPE: AttributeBackingType = AttributeBackingType::Descriptor;
27 
28 const OFFSET: u32 = 12;
29 const WRITE_REQUEST_TYPE: GattWriteRequestType = GattWriteRequestType::Prepare { offset: 7 };
30 
initialize_manager_with_connection( ) -> (Rc<CallbackTransactionManager>, UnboundedReceiver<MockCallbackEvents>)31 fn initialize_manager_with_connection(
32 ) -> (Rc<CallbackTransactionManager>, UnboundedReceiver<MockCallbackEvents>) {
33     let (callbacks, callbacks_rx) = MockCallbacks::new();
34     let callback_manager = Rc::new(CallbackTransactionManager::new(Rc::new(callbacks)));
35     (callback_manager, callbacks_rx)
36 }
37 
pull_trans_id(events_rx: &mut UnboundedReceiver<MockCallbackEvents>) -> TransactionId38 async fn pull_trans_id(events_rx: &mut UnboundedReceiver<MockCallbackEvents>) -> TransactionId {
39     match events_rx.recv().await.unwrap() {
40         MockCallbackEvents::OnServerRead(_, trans_id, _, _, _) => trans_id,
41         MockCallbackEvents::OnServerWrite(_, trans_id, _, _, _, _) => trans_id,
42         MockCallbackEvents::OnExecute(_, trans_id, _) => trans_id,
43         _ => unimplemented!(),
44     }
45 }
46 
47 #[test]
test_read_characteristic_callback()48 fn test_read_characteristic_callback() {
49     start_test(async {
50         // arrange
51         let (callback_manager, mut callbacks_rx) = initialize_manager_with_connection();
52 
53         // act: start read operation
54         spawn_local(async move {
55             callback_manager
56                 .get_datastore(SERVER_ID)
57                 .read(TCB_IDX, HANDLE_1, OFFSET, BACKING_TYPE)
58                 .await
59         });
60 
61         // assert: verify the read callback is received
62         let MockCallbackEvents::OnServerRead(CONN_ID, _, HANDLE_1, BACKING_TYPE, OFFSET) =
63             callbacks_rx.recv().await.unwrap()
64         else {
65             unreachable!()
66         };
67     });
68 }
69 
70 #[test]
test_read_characteristic_response()71 fn test_read_characteristic_response() {
72     start_test(async {
73         // arrange
74         let (callback_manager, mut callbacks_rx) = initialize_manager_with_connection();
75         let data = [1, 2];
76 
77         // act: start read operation
78         let datastore = callback_manager.get_datastore(SERVER_ID);
79         let pending_read =
80             spawn_local(
81                 async move { datastore.read(TCB_IDX, HANDLE_1, OFFSET, BACKING_TYPE).await },
82             );
83         // provide a response
84         let trans_id = pull_trans_id(&mut callbacks_rx).await;
85         callback_manager.send_response(CONN_ID, trans_id, Ok(data.to_vec())).unwrap();
86 
87         // assert: that the supplied data was correctly read
88         assert_eq!(pending_read.await.unwrap(), Ok(data.to_vec()));
89     });
90 }
91 
92 #[test]
test_sequential_reads()93 fn test_sequential_reads() {
94     start_test(async {
95         // arrange
96         let (callback_manager, mut callbacks_rx) = initialize_manager_with_connection();
97         let data1 = [1, 2];
98         let data2 = [3, 4];
99 
100         // act: start read operation
101         let datastore = callback_manager.get_datastore(SERVER_ID);
102         let pending_read_1 =
103             spawn_local(
104                 async move { datastore.read(TCB_IDX, HANDLE_1, OFFSET, BACKING_TYPE).await },
105             );
106         // respond to first
107         let trans_id = pull_trans_id(&mut callbacks_rx).await;
108         callback_manager.send_response(CONN_ID, trans_id, Ok(data1.to_vec())).unwrap();
109 
110         // do a second read operation
111         let datastore = callback_manager.get_datastore(SERVER_ID);
112         let pending_read_2 =
113             spawn_local(
114                 async move { datastore.read(TCB_IDX, HANDLE_1, OFFSET, BACKING_TYPE).await },
115             );
116         // respond to second
117         let trans_id = pull_trans_id(&mut callbacks_rx).await;
118         callback_manager.send_response(CONN_ID, trans_id, Ok(data2.to_vec())).unwrap();
119 
120         // assert: that both operations got the correct response
121         assert_eq!(pending_read_1.await.unwrap(), Ok(data1.to_vec()));
122         assert_eq!(pending_read_2.await.unwrap(), Ok(data2.to_vec()));
123     });
124 }
125 
126 #[test]
test_concurrent_reads()127 fn test_concurrent_reads() {
128     start_test(async {
129         // arrange
130         let (callback_manager, mut callbacks_rx) = initialize_manager_with_connection();
131         let data1 = [1, 2];
132         let data2 = [3, 4];
133 
134         // act: start read operation
135         let datastore = callback_manager.get_datastore(SERVER_ID);
136         let pending_read_1 =
137             spawn_local(
138                 async move { datastore.read(TCB_IDX, HANDLE_1, OFFSET, BACKING_TYPE).await },
139             );
140 
141         // do a second read operation
142         let datastore = callback_manager.get_datastore(SERVER_ID);
143         let pending_read_2 =
144             spawn_local(
145                 async move { datastore.read(TCB_IDX, HANDLE_1, OFFSET, BACKING_TYPE).await },
146             );
147 
148         // respond to first
149         let trans_id = pull_trans_id(&mut callbacks_rx).await;
150         callback_manager.send_response(CONN_ID, trans_id, Ok(data1.to_vec())).unwrap();
151 
152         // respond to second
153         let trans_id = pull_trans_id(&mut callbacks_rx).await;
154         callback_manager.send_response(CONN_ID, trans_id, Ok(data2.to_vec())).unwrap();
155 
156         // assert: that both operations got the correct response
157         assert_eq!(pending_read_1.await.unwrap(), Ok(data1.to_vec()));
158         assert_eq!(pending_read_2.await.unwrap(), Ok(data2.to_vec()));
159     });
160 }
161 
162 #[test]
test_distinct_transaction_ids()163 fn test_distinct_transaction_ids() {
164     start_test(async {
165         // arrange
166         let (callback_manager, mut callbacks_rx) = initialize_manager_with_connection();
167 
168         // act: start two read operations concurrently
169         let datastore = callback_manager.get_datastore(SERVER_ID);
170         spawn_local(async move { datastore.read(TCB_IDX, HANDLE_1, OFFSET, BACKING_TYPE).await });
171         let datastore = callback_manager.get_datastore(SERVER_ID);
172         spawn_local(async move { datastore.read(TCB_IDX, HANDLE_1, OFFSET, BACKING_TYPE).await });
173 
174         // pull both trans_ids
175         let trans_id_1 = pull_trans_id(&mut callbacks_rx).await;
176         let trans_id_2 = pull_trans_id(&mut callbacks_rx).await;
177 
178         // assert: that the trans_ids are distinct
179         assert_ne!(trans_id_1, trans_id_2);
180     });
181 }
182 
183 #[test]
test_invalid_trans_id()184 fn test_invalid_trans_id() {
185     start_test(async {
186         // arrange
187         let (callback_manager, mut callbacks_rx) = initialize_manager_with_connection();
188         let data = [1, 2];
189 
190         // act: start a read operation
191         let datastore = callback_manager.get_datastore(SERVER_ID);
192         spawn_local(async move { datastore.read(TCB_IDX, HANDLE_1, OFFSET, BACKING_TYPE).await });
193         // respond with the correct conn_id but an invalid trans_id
194         let trans_id = pull_trans_id(&mut callbacks_rx).await;
195         let invalid_trans_id = TransactionId(trans_id.0 + 1);
196         let err = callback_manager
197             .send_response(CONN_ID, invalid_trans_id, Ok(data.to_vec()))
198             .unwrap_err();
199 
200         // assert
201         assert_eq!(err, CallbackResponseError::NonExistentTransaction(invalid_trans_id));
202     });
203 }
204 
205 #[test]
test_invalid_conn_id()206 fn test_invalid_conn_id() {
207     start_test(async {
208         // arrange
209         let (callback_manager, mut callbacks_rx) = initialize_manager_with_connection();
210         let data = [1, 2];
211 
212         // act: start a read operation
213         let datastore = callback_manager.get_datastore(SERVER_ID);
214         spawn_local(async move { datastore.read(TCB_IDX, HANDLE_1, OFFSET, BACKING_TYPE).await });
215         // respond with the correct trans_id but an invalid conn_id
216         let trans_id = pull_trans_id(&mut callbacks_rx).await;
217         let invalid_conn_id = ConnectionId(CONN_ID.0 + 1);
218         let err = callback_manager
219             .send_response(invalid_conn_id, trans_id, Ok(data.to_vec()))
220             .unwrap_err();
221 
222         // assert
223         assert_eq!(err, CallbackResponseError::NonExistentTransaction(trans_id));
224     });
225 }
226 
227 #[test]
test_write_characteristic_callback()228 fn test_write_characteristic_callback() {
229     start_test(async {
230         // arrange
231         let (callback_manager, mut callbacks_rx) = initialize_manager_with_connection();
232 
233         // act: start write operation
234         let data = [1, 2];
235         spawn_local(async move {
236             callback_manager
237                 .get_datastore(SERVER_ID)
238                 .write(TCB_IDX, HANDLE_1, BACKING_TYPE, WRITE_REQUEST_TYPE, &data)
239                 .await
240         });
241 
242         // assert: verify the write callback is received
243         let MockCallbackEvents::OnServerWrite(
244             CONN_ID,
245             _,
246             HANDLE_1,
247             BACKING_TYPE,
248             GattWriteType::Request(WRITE_REQUEST_TYPE),
249             recv_data,
250         ) = callbacks_rx.recv().await.unwrap()
251         else {
252             unreachable!()
253         };
254         assert_eq!(recv_data, data);
255     });
256 }
257 
258 #[test]
test_write_characteristic_response()259 fn test_write_characteristic_response() {
260     start_test(async {
261         // arrange
262         let (callback_manager, mut callbacks_rx) = initialize_manager_with_connection();
263 
264         // act: start write operation
265         let data = [1, 2];
266         let datastore = callback_manager.get_datastore(SERVER_ID);
267         let pending_write = spawn_local(async move {
268             datastore
269                 .write(TCB_IDX, HANDLE_1, BACKING_TYPE, GattWriteRequestType::Request, &data)
270                 .await
271         });
272         // provide a response with some error code
273         let trans_id = pull_trans_id(&mut callbacks_rx).await;
274         callback_manager
275             .send_response(CONN_ID, trans_id, Err(AttErrorCode::WRITE_NOT_PERMITTED))
276             .unwrap();
277 
278         // assert: that the error code was received
279         assert_eq!(pending_write.await.unwrap(), Err(AttErrorCode::WRITE_NOT_PERMITTED));
280     });
281 }
282 
283 #[test]
test_response_timeout()284 fn test_response_timeout() {
285     start_test(async {
286         // arrange
287         let (callback_manager, _callbacks_rx) = initialize_manager_with_connection();
288 
289         // act: start operation
290         let time_sent = Instant::now();
291         let datastore = callback_manager.get_datastore(SERVER_ID);
292         let pending_write =
293             spawn_local(
294                 async move { datastore.read(TCB_IDX, HANDLE_1, OFFSET, BACKING_TYPE).await },
295             );
296 
297         // assert: that we time-out after 15s
298         assert_eq!(pending_write.await.unwrap(), Err(AttErrorCode::UNLIKELY_ERROR));
299         let time_slept = Instant::now().duration_since(time_sent);
300         assert!(time_slept > Duration::from_secs(14));
301         assert!(time_slept < Duration::from_secs(16));
302     });
303 }
304 
305 #[test]
test_transaction_cleanup_after_timeout()306 fn test_transaction_cleanup_after_timeout() {
307     start_test(async {
308         // arrange
309         let (callback_manager, mut callbacks_rx) = initialize_manager_with_connection();
310 
311         // act: start an operation
312         let datastore = callback_manager.get_datastore(SERVER_ID);
313         let pending =
314             spawn_local(
315                 async move { datastore.read(TCB_IDX, HANDLE_1, OFFSET, BACKING_TYPE).await },
316             );
317         let trans_id = pull_trans_id(&mut callbacks_rx).await;
318         // let it time out
319         assert_eq!(pending.await.unwrap(), Err(AttErrorCode::UNLIKELY_ERROR));
320         // try responding to it now
321         let resp =
322             callback_manager.send_response(CONN_ID, trans_id, Err(AttErrorCode::INVALID_HANDLE));
323 
324         // assert: the response failed
325         assert_eq!(resp, Err(CallbackResponseError::NonExistentTransaction(trans_id)));
326     });
327 }
328 
329 #[test]
test_listener_hang_up()330 fn test_listener_hang_up() {
331     start_test(async {
332         // arrange
333         let (callback_manager, mut callbacks_rx) = initialize_manager_with_connection();
334 
335         // act: start an operation
336         let datastore = callback_manager.get_datastore(SERVER_ID);
337         let pending =
338             spawn_local(
339                 async move { datastore.read(TCB_IDX, HANDLE_1, OFFSET, BACKING_TYPE).await },
340             );
341         let trans_id = pull_trans_id(&mut callbacks_rx).await;
342         // cancel the listener, wait for it to stop
343         pending.abort();
344         pending.await.unwrap_err();
345         // try responding to it now
346         let resp =
347             callback_manager.send_response(CONN_ID, trans_id, Err(AttErrorCode::INVALID_HANDLE));
348 
349         // assert: we get the expected error
350         assert_eq!(resp, Err(CallbackResponseError::ListenerHungUp(trans_id)));
351     });
352 }
353 
354 #[test]
test_write_no_response_callback()355 fn test_write_no_response_callback() {
356     start_test(async {
357         // arrange
358         let (callback_manager, mut callbacks_rx) = initialize_manager_with_connection();
359 
360         // act: start write_no_response operation
361         let data = [1, 2];
362         callback_manager.get_datastore(SERVER_ID).write_no_response(
363             TCB_IDX,
364             HANDLE_1,
365             BACKING_TYPE,
366             &data,
367         );
368 
369         // assert: verify the write callback is received
370         let MockCallbackEvents::OnServerWrite(
371             CONN_ID,
372             _,
373             HANDLE_1,
374             BACKING_TYPE,
375             GattWriteType::Command,
376             recv_data,
377         ) = callbacks_rx.recv().await.unwrap()
378         else {
379             unreachable!()
380         };
381         assert_eq!(recv_data, data);
382     });
383 }
384 
385 #[test]
test_execute_characteristic_callback()386 fn test_execute_characteristic_callback() {
387     start_test(async {
388         // arrange
389         let (callback_manager, mut callbacks_rx) = initialize_manager_with_connection();
390 
391         // act: start execute operation
392         spawn_local(async move {
393             callback_manager
394                 .get_datastore(SERVER_ID)
395                 .execute(TCB_IDX, TransactionDecision::Cancel)
396                 .await
397         });
398 
399         // assert: verify the execute callback is received
400         let MockCallbackEvents::OnExecute(CONN_ID, _, TransactionDecision::Cancel) =
401             callbacks_rx.recv().await.unwrap()
402         else {
403             unreachable!()
404         };
405     });
406 }
407 
408 #[test]
test_execute_characteristic_response()409 fn test_execute_characteristic_response() {
410     start_test(async {
411         // arrange
412         let (callback_manager, mut callbacks_rx) = initialize_manager_with_connection();
413 
414         // act: start execute operation
415         let cloned_manager = callback_manager.clone();
416         let pending_execute = spawn_local(async move {
417             cloned_manager
418                 .get_datastore(SERVER_ID)
419                 .execute(TCB_IDX, TransactionDecision::Cancel)
420                 .await
421         });
422         // provide a response with some error code
423         let trans_id = pull_trans_id(&mut callbacks_rx).await;
424         callback_manager
425             .send_response(CONN_ID, trans_id, Err(AttErrorCode::WRITE_NOT_PERMITTED))
426             .unwrap();
427 
428         // assert: that the error code was received
429         assert_eq!(pending_execute.await.unwrap(), Err(AttErrorCode::WRITE_NOT_PERMITTED));
430     });
431 }
432