• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2024 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 
15 #include "pw_bluetooth_sapphire/central.h"
16 
17 #include <array>
18 
19 #include "pw_async/fake_dispatcher.h"
20 #include "pw_async2/pend_func_task.h"
21 #include "pw_async2/poll.h"
22 #include "pw_bluetooth_sapphire/internal/host/gap/fake_adapter.h"
23 #include "pw_multibuf/simple_allocator_for_test.h"
24 #include "pw_unit_test/framework.h"
25 
26 namespace {
27 
28 using pw::bluetooth_sapphire::Central;
29 using ScanStartResult = Central::ScanStartResult;
30 using Pending = pw::async2::PendingType;
31 using Ready = pw::async2::ReadyType;
32 using Context = pw::async2::Context;
33 using ScanHandle = Central::ScanHandle;
34 using ScanResult = Central::ScanResult;
35 using pw::async2::PendFuncTask;
36 using pw::async2::Poll;
37 using pw::chrono::SystemClock;
38 using ScanFilter = Central::ScanFilter;
39 using DisconnectReason =
40     pw::bluetooth::low_energy::Connection2::DisconnectReason;
41 
42 const bt::DeviceAddress kAddress0(bt::DeviceAddress::Type::kLEPublic, {0});
43 const bt::StaticByteBuffer kAdvDataWithName(0x05,  // length
44                                             0x09,  // type (name)
45                                             'T',
46                                             'e',
47                                             's',
48                                             't');
49 
50 const pw::bluetooth::Uuid kUuid1(1);
51 const bt::StaticByteBuffer kAdvDataWithUuid1(
52     0x05, bt::DataType::kIncomplete16BitServiceUuids, 0x01, 0x00, 0x00, 0x00);
53 
MakePendResultTask(ScanHandle::Ptr & scan_handle,std::optional<pw::Result<ScanResult>> & scan_result_out)54 auto MakePendResultTask(
55     ScanHandle::Ptr& scan_handle,
56     std::optional<pw::Result<ScanResult>>& scan_result_out) {
57   return PendFuncTask([&scan_handle, &scan_result_out](Context& cx) -> Poll<> {
58     Poll<pw::Result<ScanResult>> pend = scan_handle->PendResult(cx);
59     if (pend.IsPending()) {
60       return Pending();
61     }
62     scan_result_out = std::move(pend.value());
63     return Ready();
64   });
65 }
66 
67 class CentralTest : public ::testing::Test {
68  public:
SetUp()69   void SetUp() override {
70     central_.emplace(
71         adapter_.AsWeakPtr(), async_dispatcher_, multibuf_allocator_);
72   }
73 
Scan(Central::ScanOptions & options)74   ScanHandle::Ptr Scan(Central::ScanOptions& options) {
75     pw::async2::OnceReceiver<ScanStartResult> scan_receiver =
76         central().Scan(options);
77 
78     std::optional<pw::Result<ScanStartResult>> scan_pend_result;
79     PendFuncTask scan_receiver_task(
80         [&scan_receiver, &scan_pend_result](Context& cx) -> Poll<> {
81           Poll<pw::Result<ScanStartResult>> scan_pend = scan_receiver.Pend(cx);
82           if (scan_pend.IsPending()) {
83             return Pending();
84           }
85           scan_pend_result = std::move(scan_pend.value());
86           return Ready();
87         });
88     async2_dispatcher().Post(scan_receiver_task);
89     EXPECT_FALSE(scan_pend_result.has_value());
90 
91     async_dispatcher().RunUntilIdle();
92     EXPECT_TRUE(async2_dispatcher().RunUntilStalled().IsReady());
93 
94     if (!scan_pend_result.has_value()) {
95       ADD_FAILURE();
96       return nullptr;
97     }
98     if (!scan_pend_result.value().ok()) {
99       ADD_FAILURE();
100       return nullptr;
101     }
102     ScanStartResult scan_start_result =
103         std::move(scan_pend_result.value().value());
104     if (!scan_start_result.has_value()) {
105       ADD_FAILURE();
106       return nullptr;
107     }
108     return std::move(scan_start_result.value());
109   }
110 
DestroyCentral()111   void DestroyCentral() { central_.reset(); }
112 
connections()113   auto connections() { return adapter().fake_le()->connections(); }
114 
adapter()115   bt::gap::testing::FakeAdapter& adapter() { return adapter_; }
peer_cache()116   bt::gap::PeerCache& peer_cache() { return *adapter_.peer_cache(); }
central()117   pw::bluetooth_sapphire::Central& central() { return central_.value(); }
async_dispatcher()118   pw::async::test::FakeDispatcher& async_dispatcher() {
119     return async_dispatcher_;
120   }
async2_dispatcher()121   pw::async2::Dispatcher& async2_dispatcher() { return async2_dispatcher_; }
122 
123  private:
124   pw::async::test::FakeDispatcher async_dispatcher_;
125   pw::async2::Dispatcher async2_dispatcher_;
126   bt::gap::testing::FakeAdapter adapter_{async_dispatcher_};
127 
128   pw::multibuf::test::SimpleAllocatorForTest</*kDataSizeBytes=*/2024,
129                                              /*kMetaSizeBytes=*/3000>
130       multibuf_allocator_;
131   std::optional<Central> central_;
132 };
133 
TEST_F(CentralTest,ScanOneResultAndStopScanSuccess)134 TEST_F(CentralTest, ScanOneResultAndStopScanSuccess) {
135   Central::ScanOptions options;
136   options.scan_type = Central::ScanType::kActiveUsePublicAddress;
137   // Don't filter results.
138   std::array<Central::ScanFilter, 1> filters{Central::ScanFilter{}};
139   options.filters = filters;
140 
141   ScanHandle::Ptr scan_handle = Scan(options);
142   ASSERT_TRUE(scan_handle);
143   ASSERT_EQ(adapter().fake_le()->discovery_sessions().size(), 1u);
144   EXPECT_TRUE((*adapter().fake_le()->discovery_sessions().cbegin())->active());
145 
146   std::optional<pw::Result<ScanResult>> scan_result_result;
147   PendFuncTask scan_handle_task =
148       MakePendResultTask(scan_handle, scan_result_result);
149   async2_dispatcher().Post(scan_handle_task);
150   EXPECT_TRUE(async2_dispatcher().RunUntilStalled().IsPending());
151 
152   const bool connectable = true;
153   bt::gap::Peer* peer = peer_cache().NewPeer(kAddress0, connectable);
154   const int rssi = 5;
155   SystemClock::time_point timestamp(SystemClock::duration(5));
156   peer->MutLe().SetAdvertisingData(rssi, kAdvDataWithName, timestamp);
157 
158   adapter().fake_le()->NotifyScanResult(*peer);
159 
160   EXPECT_TRUE(async2_dispatcher().RunUntilStalled().IsReady());
161   ASSERT_TRUE(scan_result_result.has_value());
162   ASSERT_TRUE(scan_result_result.value().ok());
163 
164   ScanResult scan_result = std::move(scan_result_result.value().value());
165   scan_result_result.reset();
166   EXPECT_EQ(scan_result.peer_id, peer->identifier().value());
167   EXPECT_EQ(scan_result.connectable, connectable);
168   EXPECT_EQ(scan_result.rssi, rssi);
169   EXPECT_EQ(scan_result.last_updated, timestamp);
170   ASSERT_EQ(scan_result.data.size(), kAdvDataWithName.size());
171   ASSERT_TRUE(scan_result.data.IsContiguous());
172   for (size_t i = 0; i < kAdvDataWithName.size(); i++) {
173     EXPECT_EQ(scan_result.data.ContiguousSpan().value()[i],
174               kAdvDataWithName.subspan()[i]);
175   }
176   ASSERT_TRUE(scan_result.name.has_value());
177   EXPECT_EQ(scan_result.name.value(), "Test");
178 
179   // No more scan results should be received.
180   async2_dispatcher().Post(scan_handle_task);
181   EXPECT_TRUE(async2_dispatcher().RunUntilStalled().IsPending());
182   EXPECT_FALSE(scan_result_result.has_value());
183   scan_handle_task.Deregister();
184 
185   // Stop scan
186   scan_handle.reset();
187   // The scan should stop asynchronously.
188   EXPECT_EQ(adapter().fake_le()->discovery_sessions().size(), 1u);
189   async_dispatcher().RunUntilIdle();
190   EXPECT_EQ(adapter().fake_le()->discovery_sessions().size(), 0u);
191 }
192 
TEST_F(CentralTest,ScanResultDoesNotMatchFilter)193 TEST_F(CentralTest, ScanResultDoesNotMatchFilter) {
194   Central::ScanOptions options;
195   options.scan_type = Central::ScanType::kActiveUsePublicAddress;
196   Central::ScanFilter filter;
197   filter.name = "different-name";
198   std::array<Central::ScanFilter, 1> filters{filter};
199   options.filters = filters;
200 
201   ScanHandle::Ptr scan_handle = Scan(options);
202   ASSERT_TRUE(scan_handle);
203 
204   std::optional<pw::Result<ScanResult>> scan_result_result;
205   PendFuncTask scan_handle_task =
206       MakePendResultTask(scan_handle, scan_result_result);
207   async2_dispatcher().Post(scan_handle_task);
208 
209   const bool connectable = true;
210   bt::gap::Peer* peer = peer_cache().NewPeer(kAddress0, connectable);
211   const int rssi = 5;
212   SystemClock::time_point timestamp(SystemClock::duration(5));
213   peer->MutLe().SetAdvertisingData(rssi, kAdvDataWithName, timestamp);
214 
215   adapter().fake_le()->NotifyScanResult(*peer);
216 
217   async_dispatcher().RunUntilIdle();
218   EXPECT_TRUE(async2_dispatcher().RunUntilStalled().IsPending());
219   EXPECT_FALSE(scan_result_result.has_value());
220   scan_handle_task.Deregister();
221 }
222 
TEST_F(CentralTest,ScanResultMatchesSecondFilterOnly)223 TEST_F(CentralTest, ScanResultMatchesSecondFilterOnly) {
224   Central::ScanOptions options;
225   options.scan_type = Central::ScanType::kActiveUsePublicAddress;
226   ScanFilter filter_0;
227   filter_0.service_uuid = pw::bluetooth::Uuid(2);
228   ScanFilter filter_1;
229   filter_1.service_uuid = kUuid1;
230   std::array<ScanFilter, 2> filters{filter_0, filter_1};
231   options.filters = filters;
232 
233   ScanHandle::Ptr scan_handle = Scan(options);
234   ASSERT_TRUE(scan_handle);
235   ASSERT_EQ(adapter().fake_le()->discovery_sessions().size(), 1u);
236   EXPECT_TRUE((*adapter().fake_le()->discovery_sessions().cbegin())->active());
237 
238   std::optional<pw::Result<ScanResult>> scan_result_result;
239   PendFuncTask scan_handle_task =
240       MakePendResultTask(scan_handle, scan_result_result);
241   async2_dispatcher().Post(scan_handle_task);
242   EXPECT_TRUE(async2_dispatcher().RunUntilStalled().IsPending());
243 
244   const bool connectable = false;
245   bt::gap::Peer* peer = peer_cache().NewPeer(kAddress0, connectable);
246   const int rssi = 6;
247   SystemClock::time_point timestamp(SystemClock::duration(6));
248   peer->MutLe().SetAdvertisingData(rssi, kAdvDataWithUuid1, timestamp);
249 
250   adapter().fake_le()->NotifyScanResult(*peer);
251 
252   EXPECT_TRUE(async2_dispatcher().RunUntilStalled().IsReady());
253   ASSERT_TRUE(scan_result_result.has_value());
254   ASSERT_TRUE(scan_result_result.value().ok());
255 
256   ScanResult scan_result = std::move(scan_result_result.value().value());
257   scan_result_result.reset();
258   EXPECT_EQ(scan_result.peer_id, peer->identifier().value());
259   EXPECT_EQ(scan_result.connectable, connectable);
260   EXPECT_EQ(scan_result.rssi, rssi);
261   EXPECT_EQ(scan_result.last_updated, timestamp);
262   ASSERT_EQ(scan_result.data.size(), kAdvDataWithName.size());
263   ASSERT_TRUE(scan_result.data.IsContiguous());
264   for (size_t i = 0; i < kAdvDataWithUuid1.size(); i++) {
265     EXPECT_EQ(scan_result.data.ContiguousSpan().value()[i],
266               kAdvDataWithUuid1.subspan()[i]);
267   }
268   EXPECT_FALSE(scan_result.name.has_value());
269 }
270 
TEST_F(CentralTest,ScanResultMatchesSolicitationUUID)271 TEST_F(CentralTest, ScanResultMatchesSolicitationUUID) {
272   Central::ScanOptions options;
273   options.scan_type = Central::ScanType::kActiveUsePublicAddress;
274 
275   ScanFilter filter;
276   filter.solicitation_uuid = kUuid1;
277   std::array<Central::ScanFilter, 1> filters{filter};
278   options.filters = filters;
279 
280   ScanHandle::Ptr scan_handle = Scan(options);
281   ASSERT_TRUE(scan_handle);
282 
283   std::optional<pw::Result<ScanResult>> scan_result_result;
284   PendFuncTask scan_handle_task =
285       MakePendResultTask(scan_handle, scan_result_result);
286   async2_dispatcher().Post(scan_handle_task);
287 
288   const bool connectable = false;
289   bt::gap::Peer* peer = peer_cache().NewPeer(kAddress0, connectable);
290   SystemClock::time_point timestamp(SystemClock::duration(6));
291 
292   const int rssi = 6;
293   bt::StaticByteBuffer adv_data(
294       0x05, bt::DataType::kSolicitationUuid16Bit, 0x01, 0x00, 0x00, 0x00);
295   peer->MutLe().SetAdvertisingData(rssi, adv_data, timestamp);
296 
297   adapter().fake_le()->NotifyScanResult(*peer);
298   EXPECT_TRUE(async2_dispatcher().RunUntilStalled().IsReady());
299 
300   ASSERT_TRUE(scan_result_result.has_value());
301   ASSERT_TRUE(scan_result_result.value().ok());
302 
303   ScanResult scan_result = std::move(scan_result_result.value().value());
304   scan_result_result.reset();
305   EXPECT_EQ(scan_result.peer_id, peer->identifier().value());
306   EXPECT_EQ(scan_result.connectable, connectable);
307   EXPECT_EQ(scan_result.rssi, rssi);
308 
309   ASSERT_TRUE(scan_result.data.IsContiguous());
310   for (size_t i = 0; i < adv_data.size(); i++) {
311     EXPECT_EQ(scan_result.data.ContiguousSpan().value()[i],
312               adv_data.subspan()[i]);
313   }
314 }
315 
TEST_F(CentralTest,CachedScanResult)316 TEST_F(CentralTest, CachedScanResult) {
317   const bool connectable = true;
318   bt::gap::Peer* peer = peer_cache().NewPeer(kAddress0, connectable);
319   const int rssi = 5;
320   SystemClock::time_point timestamp(SystemClock::duration(5));
321   peer->MutLe().SetAdvertisingData(rssi, kAdvDataWithName, timestamp);
322   adapter().fake_le()->AddCachedScanResult(peer->identifier());
323 
324   Central::ScanOptions options;
325   options.scan_type = Central::ScanType::kActiveUsePublicAddress;
326   // Don't filter results.
327   std::array<ScanFilter, 1> filters{ScanFilter{}};
328   options.filters = filters;
329 
330   ScanHandle::Ptr scan_handle = Scan(options);
331   ASSERT_TRUE(scan_handle);
332   ASSERT_EQ(adapter().fake_le()->discovery_sessions().size(), 1u);
333   EXPECT_TRUE((*adapter().fake_le()->discovery_sessions().cbegin())->active());
334 
335   std::optional<pw::Result<ScanResult>> scan_result_result;
336   PendFuncTask scan_handle_task =
337       MakePendResultTask(scan_handle, scan_result_result);
338   async2_dispatcher().Post(scan_handle_task);
339 
340   EXPECT_TRUE(async2_dispatcher().RunUntilStalled().IsReady());
341   ASSERT_TRUE(scan_result_result.has_value());
342   ASSERT_TRUE(scan_result_result.value().ok());
343 
344   ScanResult scan_result = std::move(scan_result_result.value().value());
345   EXPECT_EQ(scan_result.peer_id, peer->identifier().value());
346   EXPECT_EQ(scan_result.connectable, connectable);
347   EXPECT_EQ(scan_result.rssi, rssi);
348   EXPECT_EQ(scan_result.last_updated, timestamp);
349   ASSERT_EQ(scan_result.data.size(), kAdvDataWithName.size());
350   ASSERT_TRUE(scan_result.data.IsContiguous());
351   for (size_t i = 0; i < kAdvDataWithName.size(); i++) {
352     EXPECT_EQ(scan_result.data.ContiguousSpan().value()[i],
353               kAdvDataWithName.subspan()[i]);
354   }
355   ASSERT_TRUE(scan_result.name.has_value());
356   EXPECT_EQ(scan_result.name.value(), "Test");
357 }
358 
TEST_F(CentralTest,ScanErrorReceivedByScanHandle)359 TEST_F(CentralTest, ScanErrorReceivedByScanHandle) {
360   Central::ScanOptions options;
361   options.scan_type = Central::ScanType::kActiveUsePublicAddress;
362   // Don't filter results.
363   std::array<Central::ScanFilter, 1> filters{Central::ScanFilter{}};
364   options.filters = filters;
365 
366   ScanHandle::Ptr scan_handle = Scan(options);
367   ASSERT_TRUE(scan_handle);
368   ASSERT_EQ(adapter().fake_le()->discovery_sessions().size(), 1u);
369   EXPECT_TRUE((*adapter().fake_le()->discovery_sessions().cbegin())->active());
370 
371   std::optional<pw::Result<ScanResult>> scan_result_result;
372   PendFuncTask scan_handle_task =
373       MakePendResultTask(scan_handle, scan_result_result);
374   async2_dispatcher().Post(scan_handle_task);
375   EXPECT_TRUE(async2_dispatcher().RunUntilStalled().IsPending());
376 
377   (*adapter().fake_le()->discovery_sessions().cbegin())->NotifyError();
378 
379   EXPECT_TRUE(async2_dispatcher().RunUntilStalled().IsReady());
380   ASSERT_TRUE(scan_result_result.has_value());
381   EXPECT_TRUE(scan_result_result.value().status().IsCancelled());
382 }
383 
TEST_F(CentralTest,ScanWithoutFiltersFails)384 TEST_F(CentralTest, ScanWithoutFiltersFails) {
385   Central::ScanOptions options;
386   options.scan_type = Central::ScanType::kActiveUsePublicAddress;
387   options.filters = {};
388 
389   pw::async2::OnceReceiver<ScanStartResult> scan_receiver =
390       central().Scan(options);
391 
392   std::optional<pw::Result<ScanStartResult>> scan_pend_result;
393   PendFuncTask scan_receiver_task(
394       [&scan_receiver, &scan_pend_result](Context& cx) -> Poll<> {
395         Poll<pw::Result<ScanStartResult>> scan_pend = scan_receiver.Pend(cx);
396         if (scan_pend.IsPending()) {
397           return Pending();
398         }
399         scan_pend_result = std::move(scan_pend.value());
400         return Ready();
401       });
402   async2_dispatcher().Post(scan_receiver_task);
403   EXPECT_TRUE(async2_dispatcher().RunUntilStalled().IsReady());
404   ASSERT_TRUE(scan_pend_result.has_value());
405   ASSERT_TRUE(scan_pend_result.value().ok());
406   ScanStartResult scan_start_result =
407       std::move(scan_pend_result.value().value());
408   ASSERT_FALSE(scan_start_result.has_value());
409   EXPECT_EQ(scan_start_result.error(),
410             Central::StartScanError::kInvalidParameters);
411 }
412 
TEST_F(CentralTest,QueueMoreThanMaxScanResultsInScanHandleDropsOldest)413 TEST_F(CentralTest, QueueMoreThanMaxScanResultsInScanHandleDropsOldest) {
414   Central::ScanOptions options;
415   options.scan_type = Central::ScanType::kActiveUsePublicAddress;
416   // Don't filter results.
417   std::array<Central::ScanFilter, 1> filters{Central::ScanFilter{}};
418   options.filters = filters;
419 
420   ScanHandle::Ptr scan_handle = Scan(options);
421   ASSERT_TRUE(scan_handle);
422   ASSERT_EQ(adapter().fake_le()->discovery_sessions().size(), 1u);
423   EXPECT_TRUE((*adapter().fake_le()->discovery_sessions().cbegin())->active());
424 
425   std::vector<pw::Result<ScanResult>> scan_result_results;
426   PendFuncTask scan_handle_task =
427       PendFuncTask([&scan_handle, &scan_result_results](Context& cx) -> Poll<> {
428         while (true) {
429           Poll<pw::Result<ScanResult>> pend = scan_handle->PendResult(cx);
430           if (pend.IsPending()) {
431             return Pending();
432           }
433           scan_result_results.emplace_back(std::move(pend.value()));
434           if (!scan_result_results.back().ok()) {
435             return Ready();
436           }
437         }
438       });
439   async2_dispatcher().Post(scan_handle_task);
440   EXPECT_TRUE(async2_dispatcher().RunUntilStalled().IsPending());
441 
442   const bool connectable = true;
443   bt::gap::Peer* peer = peer_cache().NewPeer(kAddress0, connectable);
444   SystemClock::time_point timestamp(SystemClock::duration(5));
445 
446   // Queue 1 more than the max queue size. Put the index in the rssi field.
447   for (int i = 0; i < Central::kMaxScanResultsQueueSize + 1; i++) {
448     peer->MutLe().SetAdvertisingData(/*rssi=*/i, kAdvDataWithName, timestamp);
449     adapter().fake_le()->NotifyScanResult(*peer);
450   }
451 
452   EXPECT_TRUE(async2_dispatcher().RunUntilStalled().IsPending());
453   scan_handle_task.Deregister();
454   ASSERT_EQ(scan_result_results.size(), Central::kMaxScanResultsQueueSize);
455   // The first scan result should have been dropped.
456   for (int i = 0; i < Central::kMaxScanResultsQueueSize; i++) {
457     ASSERT_TRUE(scan_result_results[i].ok());
458     EXPECT_EQ(scan_result_results[i].value().rssi, i + 1);
459   }
460 }
461 
TEST_F(CentralTest,CentralDestroyedBeforeScanHandle)462 TEST_F(CentralTest, CentralDestroyedBeforeScanHandle) {
463   Central::ScanOptions options;
464   options.scan_type = Central::ScanType::kActiveUsePublicAddress;
465   std::array<Central::ScanFilter, 1> filters{Central::ScanFilter{}};
466   options.filters = filters;
467 
468   ScanHandle::Ptr scan_handle = Scan(options);
469   ASSERT_TRUE(scan_handle);
470   ASSERT_EQ(adapter().fake_le()->discovery_sessions().size(), 1u);
471 
472   std::optional<pw::Result<ScanResult>> scan_result_result;
473   PendFuncTask scan_handle_task =
474       MakePendResultTask(scan_handle, scan_result_result);
475   async2_dispatcher().Post(scan_handle_task);
476   EXPECT_TRUE(async2_dispatcher().RunUntilStalled().IsPending());
477 
478   DestroyCentral();
479 
480   EXPECT_TRUE(async2_dispatcher().RunUntilStalled().IsReady());
481   ASSERT_TRUE(scan_result_result.has_value());
482   EXPECT_TRUE(scan_result_result.value().status().IsCancelled());
483 
484   scan_handle.reset();
485 }
486 
TEST_F(CentralTest,ConnectAndDisconnectSuccess)487 TEST_F(CentralTest, ConnectAndDisconnectSuccess) {
488   bt::gap::Peer* peer = peer_cache().NewPeer(kAddress0, /*connectable=*/true);
489   pw::bluetooth::low_energy::Connection2::ConnectionOptions options;
490   std::optional<pw::Result<Central::ConnectResult>> connect_result;
491   pw::async2::OnceReceiver<Central::ConnectResult> receiver =
492       central().Connect(peer->identifier().value(), options);
493   PendFuncTask connect_task =
494       PendFuncTask([&connect_result, &receiver](Context& cx) -> Poll<> {
495         Poll<pw::Result<Central::ConnectResult>> poll = receiver.Pend(cx);
496         if (poll.IsPending()) {
497           return Pending();
498         }
499         connect_result = std::move(poll->value());
500         return Ready();
501       });
502   async2_dispatcher().Post(connect_task);
503   EXPECT_TRUE(async2_dispatcher().RunUntilStalled().IsPending());
504   async_dispatcher().RunUntilIdle();
505   EXPECT_TRUE(async2_dispatcher().RunUntilStalled().IsReady());
506   ASSERT_TRUE(connect_result.has_value());
507   ASSERT_TRUE(connect_result->ok());
508   ASSERT_TRUE(connect_result->value());
509   ASSERT_EQ(adapter().fake_le()->connections().count(peer->identifier()), 1u);
510   pw::bluetooth::low_energy::Connection2::Ptr connection =
511       std::move(connect_result->value().value());
512 
513   // Disconnect
514   connection.reset();
515   ASSERT_EQ(connections().count(peer->identifier()), 1u);
516   async_dispatcher().RunUntilIdle();
517   ASSERT_EQ(connections().count(peer->identifier()), 0u);
518 }
519 
TEST_F(CentralTest,PendDisconnect)520 TEST_F(CentralTest, PendDisconnect) {
521   bt::gap::Peer* peer = peer_cache().NewPeer(kAddress0, /*connectable=*/true);
522   pw::bluetooth::low_energy::Connection2::ConnectionOptions options;
523   std::optional<pw::Result<Central::ConnectResult>> connect_result;
524   pw::async2::OnceReceiver<Central::ConnectResult> receiver =
525       central().Connect(peer->identifier().value(), options);
526   PendFuncTask connect_task =
527       PendFuncTask([&connect_result, &receiver](Context& cx) -> Poll<> {
528         Poll<pw::Result<Central::ConnectResult>> poll = receiver.Pend(cx);
529         if (poll.IsPending()) {
530           return Pending();
531         }
532         connect_result = std::move(poll->value());
533         return Ready();
534       });
535   async2_dispatcher().Post(connect_task);
536   EXPECT_TRUE(async2_dispatcher().RunUntilStalled().IsPending());
537   async_dispatcher().RunUntilIdle();
538   EXPECT_TRUE(async2_dispatcher().RunUntilStalled().IsReady());
539   ASSERT_TRUE(connect_result.has_value());
540   ASSERT_TRUE(connect_result->ok());
541   ASSERT_TRUE(connect_result->value());
542   ASSERT_EQ(adapter().fake_le()->connections().count(peer->identifier()), 1u);
543   pw::bluetooth::low_energy::Connection2::Ptr connection =
544       std::move(connect_result->value().value());
545 
546   std::optional<DisconnectReason> disconnect_reason;
547   PendFuncTask disconnect_task =
548       PendFuncTask([&connection, &disconnect_reason](Context& cx) -> Poll<> {
549         Poll<DisconnectReason> poll = connection->PendDisconnect(cx);
550         if (poll.IsPending()) {
551           return Pending();
552         }
553         disconnect_reason = poll.value();
554         return Ready();
555       });
556   async2_dispatcher().Post(disconnect_task);
557   EXPECT_TRUE(async2_dispatcher().RunUntilStalled().IsPending());
558   ASSERT_FALSE(disconnect_reason.has_value());
559 
560   ASSERT_TRUE(adapter().fake_le()->Disconnect(peer->identifier()));
561   ASSERT_EQ(adapter().fake_le()->connections().count(peer->identifier()), 0u);
562   EXPECT_TRUE(async2_dispatcher().RunUntilStalled().IsReady());
563   ASSERT_TRUE(disconnect_reason.has_value());
564   EXPECT_EQ(disconnect_reason.value(), DisconnectReason::kFailure);
565 
566   connection.reset();
567   async_dispatcher().RunUntilIdle();
568 }
569 
570 }  // namespace
571