adapter_unittest.cc 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506
  1. // Copyright 2020 The Chromium 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. #include "device/bluetooth/adapter.h"
  5. #include "base/callback_helpers.h"
  6. #include "base/containers/contains.h"
  7. #include "base/run_loop.h"
  8. #include "base/task/thread_pool.h"
  9. #include "base/test/bind.h"
  10. #include "base/test/gmock_callback_support.h"
  11. #include "base/test/metrics/histogram_tester.h"
  12. #include "base/test/task_environment.h"
  13. #include "build/build_config.h"
  14. #include "device/bluetooth/bluetooth_advertisement.h"
  15. #include "device/bluetooth/test/mock_bluetooth_adapter.h"
  16. #include "device/bluetooth/test/mock_bluetooth_advertisement.h"
  17. #include "device/bluetooth/test/mock_bluetooth_device.h"
  18. #include "device/bluetooth/test/mock_bluetooth_socket.h"
  19. #include "mojo/public/cpp/bindings/pending_remote.h"
  20. #include "mojo/public/cpp/bindings/remote.h"
  21. #include "testing/gtest/include/gtest/gtest.h"
  22. using base::test::RunOnceCallback;
  23. using testing::_;
  24. using testing::DoAll;
  25. using testing::InvokeWithoutArgs;
  26. using testing::NiceMock;
  27. using testing::Return;
  28. namespace {
  29. const char kKnownDeviceAddress[] = "00:00:00:00:01";
  30. const char kUnknownDeviceAddress[] = "00:00:00:00:02";
  31. const char kServiceName[] = "ServiceName";
  32. const char kServiceId[] = "0000abcd-0000-0000-0000-000000000001";
  33. const char kDeviceServiceDataStr[] = "ServiceData";
  34. std::vector<uint8_t> GetByteVector(const std::string& str) {
  35. return std::vector<uint8_t>(str.begin(), str.end());
  36. }
  37. class MockBluetoothAdapterWithAdvertisements
  38. : public device::MockBluetoothAdapter {
  39. public:
  40. void RegisterAdvertisement(
  41. std::unique_ptr<device::BluetoothAdvertisement::Data> advertisement_data,
  42. device::BluetoothAdapter::CreateAdvertisementCallback callback,
  43. device::BluetoothAdapter::AdvertisementErrorCallback error_callback)
  44. override {
  45. last_advertisement_data_ = std::move(advertisement_data);
  46. if (should_advertisement_registration_succeed_) {
  47. std::move(callback).Run(
  48. base::MakeRefCounted<device::MockBluetoothAdvertisement>());
  49. } else {
  50. std::move(error_callback)
  51. .Run(device::BluetoothAdvertisement::ErrorCode::
  52. INVALID_ADVERTISEMENT_ERROR_CODE);
  53. }
  54. }
  55. bool should_advertisement_registration_succeed_ = true;
  56. std::unique_ptr<device::BluetoothAdvertisement::Data>
  57. last_advertisement_data_;
  58. protected:
  59. ~MockBluetoothAdapterWithAdvertisements() override = default;
  60. };
  61. } // namespace
  62. namespace bluetooth {
  63. class AdapterTest : public testing::Test {
  64. public:
  65. AdapterTest() = default;
  66. ~AdapterTest() override = default;
  67. AdapterTest(const AdapterTest&) = delete;
  68. AdapterTest& operator=(const AdapterTest&) = delete;
  69. void SetUp() override {
  70. mock_bluetooth_adapter_ = base::MakeRefCounted<
  71. NiceMock<MockBluetoothAdapterWithAdvertisements>>();
  72. ON_CALL(*mock_bluetooth_adapter_, IsPresent()).WillByDefault(Return(true));
  73. ON_CALL(*mock_bluetooth_adapter_, IsPowered()).WillByDefault(Return(true));
  74. // |mock_known_bluetooth_device_| is a device found via discovery.
  75. mock_known_bluetooth_device_ =
  76. std::make_unique<testing::NiceMock<device::MockBluetoothDevice>>(
  77. mock_bluetooth_adapter_.get(),
  78. /*class=*/0, "Known Device", kKnownDeviceAddress,
  79. /*paired=*/false,
  80. /*connected=*/false);
  81. // |mock_unknown_bluetooth_device_| is |connected| because it is created
  82. // as a result of calling ConnectDevice().
  83. mock_unknown_bluetooth_device_ =
  84. std::make_unique<testing::NiceMock<device::MockBluetoothDevice>>(
  85. mock_bluetooth_adapter_.get(),
  86. /*class=*/0, "Unknown Device", kUnknownDeviceAddress,
  87. /*paired=*/false,
  88. /*connected=*/true);
  89. // |mock_bluetooth_adapter_| can only find |mock_known_bluetooth_device_|
  90. // via GetDevice(), not |mock_unknown_bluetooth_device_|.
  91. ON_CALL(*mock_bluetooth_adapter_, GetDevice(kKnownDeviceAddress))
  92. .WillByDefault(Return(mock_known_bluetooth_device_.get()));
  93. mock_bluetooth_socket_ =
  94. base::MakeRefCounted<NiceMock<device::MockBluetoothSocket>>();
  95. adapter_ = std::make_unique<Adapter>(mock_bluetooth_adapter_);
  96. }
  97. protected:
  98. void RegisterAdvertisement(bool should_succeed, bool use_scan_data) {
  99. mock_bluetooth_adapter_->should_advertisement_registration_succeed_ =
  100. should_succeed;
  101. auto service_data = GetByteVector(kDeviceServiceDataStr);
  102. mojo::Remote<mojom::Advertisement> advertisement;
  103. base::RunLoop run_loop;
  104. adapter_->RegisterAdvertisement(
  105. device::BluetoothUUID(kServiceId), service_data,
  106. /*use_scan_data=*/use_scan_data,
  107. base::BindLambdaForTesting([&](mojo::PendingRemote<mojom::Advertisement>
  108. pending_advertisement) {
  109. EXPECT_EQ(should_succeed, pending_advertisement.is_valid());
  110. run_loop.Quit();
  111. }));
  112. run_loop.Run();
  113. }
  114. void VerifyAdvertisement() {
  115. auto service_data = GetByteVector(kDeviceServiceDataStr);
  116. auto uuid_list =
  117. mock_bluetooth_adapter_->last_advertisement_data_->service_uuids();
  118. EXPECT_EQ(1u, uuid_list->size());
  119. EXPECT_EQ(kServiceId, (*uuid_list)[0]);
  120. auto last_service_data =
  121. mock_bluetooth_adapter_->last_advertisement_data_->service_data();
  122. EXPECT_EQ(service_data, last_service_data->at(kServiceId));
  123. EXPECT_FALSE(mock_bluetooth_adapter_->last_advertisement_data_
  124. ->scan_response_data());
  125. }
  126. void VerifyAdvertisementWithScanData() {
  127. auto service_data = GetByteVector(kDeviceServiceDataStr);
  128. auto uuid_list =
  129. mock_bluetooth_adapter_->last_advertisement_data_->service_uuids();
  130. EXPECT_EQ(1u, uuid_list->size());
  131. EXPECT_EQ(kServiceId, (*uuid_list)[0]);
  132. EXPECT_FALSE(
  133. mock_bluetooth_adapter_->last_advertisement_data_->service_data());
  134. auto last_scan_response_data =
  135. mock_bluetooth_adapter_->last_advertisement_data_->scan_response_data();
  136. ASSERT_TRUE(base::Contains(*last_scan_response_data, 0x16));
  137. const auto& raw_data = (*last_scan_response_data)[0x16];
  138. // First two bytes should be the identifying bits of the kServiceId UUID.
  139. // They should be in litten endian order (reversed).
  140. EXPECT_EQ(0xCD, raw_data[0]);
  141. EXPECT_EQ(0xAB, raw_data[1]);
  142. EXPECT_EQ(service_data,
  143. std::vector<uint8_t>(raw_data.begin() + 2, raw_data.end()));
  144. }
  145. scoped_refptr<NiceMock<MockBluetoothAdapterWithAdvertisements>>
  146. mock_bluetooth_adapter_;
  147. std::unique_ptr<NiceMock<device::MockBluetoothDevice>>
  148. mock_known_bluetooth_device_;
  149. std::unique_ptr<NiceMock<device::MockBluetoothDevice>>
  150. mock_unknown_bluetooth_device_;
  151. scoped_refptr<NiceMock<device::MockBluetoothSocket>> mock_bluetooth_socket_;
  152. std::unique_ptr<Adapter> adapter_;
  153. private:
  154. base::test::TaskEnvironment task_environment_;
  155. };
  156. TEST_F(AdapterTest, TestRegisterAdvertisement_Success) {
  157. RegisterAdvertisement(/*should_succeed=*/true, /*use_scan_data=*/false);
  158. VerifyAdvertisement();
  159. }
  160. TEST_F(AdapterTest, TestRegisterAdvertisement_Error) {
  161. RegisterAdvertisement(/*should_succeed=*/false, /*use_scan_data=*/false);
  162. VerifyAdvertisement();
  163. }
  164. TEST_F(AdapterTest, TestRegisterAdvertisement_ScanResponseData) {
  165. RegisterAdvertisement(/*should_succeed=*/true, /*use_scan_data=*/true);
  166. VerifyAdvertisementWithScanData();
  167. }
  168. TEST_F(AdapterTest, TestConnectToServiceInsecurely_DisallowedUuid) {
  169. // Do not call Adapter::AllowConnectionsForUuid();
  170. base::RunLoop run_loop;
  171. adapter_->ConnectToServiceInsecurely(
  172. kKnownDeviceAddress, device::BluetoothUUID(kServiceId),
  173. base::BindLambdaForTesting(
  174. [&](mojom::ConnectToServiceResultPtr connect_to_service_result) {
  175. EXPECT_FALSE(connect_to_service_result);
  176. run_loop.Quit();
  177. }));
  178. run_loop.Run();
  179. }
  180. TEST_F(AdapterTest, TestConnectToServiceInsecurely_KnownDevice_Success) {
  181. EXPECT_CALL(
  182. *mock_known_bluetooth_device_,
  183. ConnectToServiceInsecurely(device::BluetoothUUID(kServiceId), _, _))
  184. .WillOnce(RunOnceCallback<1>(mock_bluetooth_socket_));
  185. adapter_->AllowConnectionsForUuid(device::BluetoothUUID(kServiceId));
  186. base::RunLoop run_loop;
  187. adapter_->ConnectToServiceInsecurely(
  188. kKnownDeviceAddress, device::BluetoothUUID(kServiceId),
  189. base::BindLambdaForTesting(
  190. [&](mojom::ConnectToServiceResultPtr connect_to_service_result) {
  191. EXPECT_TRUE(connect_to_service_result);
  192. run_loop.Quit();
  193. }));
  194. run_loop.Run();
  195. }
  196. TEST_F(AdapterTest, TestConnectToServiceInsecurely_KnownDevice_Error) {
  197. EXPECT_CALL(
  198. *mock_known_bluetooth_device_,
  199. ConnectToServiceInsecurely(device::BluetoothUUID(kServiceId), _, _))
  200. .WillOnce(RunOnceCallback<2>("Error"));
  201. adapter_->AllowConnectionsForUuid(device::BluetoothUUID(kServiceId));
  202. base::RunLoop run_loop;
  203. adapter_->ConnectToServiceInsecurely(
  204. kKnownDeviceAddress, device::BluetoothUUID(kServiceId),
  205. base::BindLambdaForTesting(
  206. [&](mojom::ConnectToServiceResultPtr connect_to_service_result) {
  207. EXPECT_FALSE(connect_to_service_result);
  208. run_loop.Quit();
  209. }));
  210. run_loop.Run();
  211. }
  212. #if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX)
  213. TEST_F(
  214. AdapterTest,
  215. TestConnectToServiceInsecurely_UnknownDevice_Success_ServicesAlreadyResolved) {
  216. EXPECT_CALL(*mock_bluetooth_adapter_,
  217. ConnectDevice(kUnknownDeviceAddress, _, _, _))
  218. .WillOnce(RunOnceCallback<2>(mock_unknown_bluetooth_device_.get()));
  219. EXPECT_CALL(
  220. *mock_unknown_bluetooth_device_,
  221. ConnectToServiceInsecurely(device::BluetoothUUID(kServiceId), _, _))
  222. .WillOnce(RunOnceCallback<1>(mock_bluetooth_socket_));
  223. EXPECT_CALL(*mock_unknown_bluetooth_device_,
  224. IsGattServicesDiscoveryComplete())
  225. .WillOnce(Return(true));
  226. adapter_->AllowConnectionsForUuid(device::BluetoothUUID(kServiceId));
  227. base::RunLoop run_loop;
  228. adapter_->ConnectToServiceInsecurely(
  229. kUnknownDeviceAddress, device::BluetoothUUID(kServiceId),
  230. base::BindLambdaForTesting(
  231. [&](mojom::ConnectToServiceResultPtr connect_to_service_result) {
  232. EXPECT_TRUE(connect_to_service_result);
  233. run_loop.Quit();
  234. }));
  235. run_loop.Run();
  236. }
  237. TEST_F(
  238. AdapterTest,
  239. TestConnectToServiceInsecurely_UnknownDevice_Success_WaitForServicesToResolve) {
  240. EXPECT_CALL(*mock_bluetooth_adapter_,
  241. ConnectDevice(kUnknownDeviceAddress, _, _, _))
  242. .WillOnce(RunOnceCallback<2>(mock_unknown_bluetooth_device_.get()));
  243. EXPECT_CALL(
  244. *mock_unknown_bluetooth_device_,
  245. ConnectToServiceInsecurely(device::BluetoothUUID(kServiceId), _, _))
  246. .WillOnce(RunOnceCallback<1>(mock_bluetooth_socket_));
  247. // At first, return false to force |adapter_| to wait for the value to change,
  248. // but subsequently return true. On that first call, post a task to trigger
  249. // a notification that services are now resolved.
  250. EXPECT_CALL(*mock_unknown_bluetooth_device_,
  251. IsGattServicesDiscoveryComplete())
  252. .WillOnce(DoAll(InvokeWithoutArgs([this]() {
  253. base::SequencedTaskRunnerHandle::Get()->PostTask(
  254. FROM_HERE, base::BindLambdaForTesting([&]() {
  255. adapter_->GattServicesDiscovered(
  256. mock_bluetooth_adapter_.get(),
  257. mock_unknown_bluetooth_device_.get());
  258. }));
  259. }),
  260. Return(false)))
  261. .WillRepeatedly(Return(true));
  262. adapter_->AllowConnectionsForUuid(device::BluetoothUUID(kServiceId));
  263. base::RunLoop run_loop;
  264. adapter_->ConnectToServiceInsecurely(
  265. kUnknownDeviceAddress, device::BluetoothUUID(kServiceId),
  266. base::BindLambdaForTesting(
  267. [&](mojom::ConnectToServiceResultPtr connect_to_service_result) {
  268. EXPECT_TRUE(connect_to_service_result);
  269. run_loop.Quit();
  270. }));
  271. run_loop.Run();
  272. }
  273. TEST_F(
  274. AdapterTest,
  275. TestConnectToServiceInsecurely_UnknownDevice_Failure_WaitForServicesToResolve_DeviceRemoved) {
  276. EXPECT_CALL(*mock_bluetooth_adapter_,
  277. ConnectDevice(kUnknownDeviceAddress, _, _, _))
  278. .WillOnce(RunOnceCallback<2>(mock_unknown_bluetooth_device_.get()));
  279. // Return false to force |adapter_| to wait for the value to change.
  280. EXPECT_CALL(*mock_unknown_bluetooth_device_,
  281. IsGattServicesDiscoveryComplete())
  282. .WillOnce(Return(false));
  283. adapter_->AllowConnectionsForUuid(device::BluetoothUUID(kServiceId));
  284. base::RunLoop run_loop;
  285. adapter_->ConnectToServiceInsecurely(
  286. kUnknownDeviceAddress, device::BluetoothUUID(kServiceId),
  287. base::BindLambdaForTesting(
  288. [&](mojom::ConnectToServiceResultPtr connect_to_service_result) {
  289. EXPECT_FALSE(connect_to_service_result);
  290. run_loop.Quit();
  291. }));
  292. // Device is removed before GATT service discovery is complete, resulting in a
  293. // failed connect-to-service result
  294. adapter_->DeviceRemoved(mock_bluetooth_adapter_.get(),
  295. mock_unknown_bluetooth_device_.get());
  296. run_loop.Run();
  297. }
  298. TEST_F(
  299. AdapterTest,
  300. TestConnectToServiceInsecurely_UnknownDevice_Failure_WaitForServicesToResolve_DeviceChangedWithNoRssi) {
  301. EXPECT_CALL(*mock_bluetooth_adapter_,
  302. ConnectDevice(kUnknownDeviceAddress, _, _, _))
  303. .WillOnce(RunOnceCallback<2>(mock_unknown_bluetooth_device_.get()));
  304. // Return false to force |adapter_| to wait for the value to change.
  305. EXPECT_CALL(*mock_unknown_bluetooth_device_,
  306. IsGattServicesDiscoveryComplete())
  307. .WillOnce(Return(false));
  308. adapter_->AllowConnectionsForUuid(device::BluetoothUUID(kServiceId));
  309. base::RunLoop run_loop;
  310. adapter_->ConnectToServiceInsecurely(
  311. kUnknownDeviceAddress, device::BluetoothUUID(kServiceId),
  312. base::BindLambdaForTesting(
  313. [&](mojom::ConnectToServiceResultPtr connect_to_service_result) {
  314. EXPECT_FALSE(connect_to_service_result);
  315. run_loop.Quit();
  316. }));
  317. // Before GATT service discovery is complete, we are notified of a device
  318. // change where the device has no RSSI. This will result in a failed
  319. // connect-to-service result.
  320. EXPECT_CALL(*mock_unknown_bluetooth_device_, GetInquiryRSSI())
  321. .WillRepeatedly(Return(absl::nullopt));
  322. adapter_->DeviceChanged(mock_bluetooth_adapter_.get(),
  323. mock_unknown_bluetooth_device_.get());
  324. run_loop.Run();
  325. }
  326. TEST_F(AdapterTest, TestConnectToServiceInsecurely_UnknownDevice_Error) {
  327. EXPECT_CALL(*mock_bluetooth_adapter_,
  328. ConnectDevice(kUnknownDeviceAddress, _, _, _))
  329. .WillOnce(RunOnceCallback<3>());
  330. adapter_->AllowConnectionsForUuid(device::BluetoothUUID(kServiceId));
  331. base::RunLoop run_loop;
  332. adapter_->ConnectToServiceInsecurely(
  333. kUnknownDeviceAddress, device::BluetoothUUID(kServiceId),
  334. base::BindLambdaForTesting(
  335. [&](mojom::ConnectToServiceResultPtr connect_to_service_result) {
  336. EXPECT_FALSE(connect_to_service_result);
  337. run_loop.Quit();
  338. }));
  339. run_loop.Run();
  340. }
  341. #else
  342. TEST_F(AdapterTest, TestConnectToServiceInsecurely_UnknownDevice) {
  343. adapter_->AllowConnectionsForUuid(device::BluetoothUUID(kServiceId));
  344. base::RunLoop run_loop;
  345. adapter_->ConnectToServiceInsecurely(
  346. kUnknownDeviceAddress, device::BluetoothUUID(kServiceId),
  347. base::BindLambdaForTesting(
  348. [&](mojom::ConnectToServiceResultPtr connect_to_service_result) {
  349. EXPECT_FALSE(connect_to_service_result);
  350. run_loop.Quit();
  351. }));
  352. run_loop.Run();
  353. }
  354. #endif
  355. TEST_F(AdapterTest, TestCreateRfcommServiceInsecurely_DisallowedUuid) {
  356. // Do not call Adapter::AllowConnectionsForUuid();
  357. base::RunLoop run_loop;
  358. adapter_->CreateRfcommServiceInsecurely(
  359. kServiceName, device::BluetoothUUID(kServiceId),
  360. base::BindLambdaForTesting(
  361. [&](mojo::PendingRemote<mojom::ServerSocket> pending_server_socket) {
  362. EXPECT_FALSE(pending_server_socket);
  363. run_loop.Quit();
  364. }));
  365. run_loop.Run();
  366. }
  367. TEST_F(AdapterTest, TestCreateRfcommServiceInsecurely_Error) {
  368. EXPECT_CALL(*mock_bluetooth_adapter_,
  369. CreateRfcommService(device::BluetoothUUID(kServiceId), _, _, _))
  370. .WillOnce(RunOnceCallback<3>("Error"));
  371. adapter_->AllowConnectionsForUuid(device::BluetoothUUID(kServiceId));
  372. base::RunLoop run_loop;
  373. adapter_->CreateRfcommServiceInsecurely(
  374. kServiceName, device::BluetoothUUID(kServiceId),
  375. base::BindLambdaForTesting(
  376. [&](mojo::PendingRemote<mojom::ServerSocket> pending_server_socket) {
  377. EXPECT_FALSE(pending_server_socket);
  378. run_loop.Quit();
  379. }));
  380. run_loop.Run();
  381. }
  382. TEST_F(AdapterTest, TestCreateRfcommServiceInsecurely_Success) {
  383. EXPECT_CALL(*mock_bluetooth_adapter_,
  384. CreateRfcommService(device::BluetoothUUID(kServiceId), _, _, _))
  385. .WillOnce(RunOnceCallback<2>(mock_bluetooth_socket_));
  386. adapter_->AllowConnectionsForUuid(device::BluetoothUUID(kServiceId));
  387. base::RunLoop run_loop;
  388. adapter_->CreateRfcommServiceInsecurely(
  389. kServiceName, device::BluetoothUUID(kServiceId),
  390. base::BindLambdaForTesting(
  391. [&](mojo::PendingRemote<mojom::ServerSocket> pending_server_socket) {
  392. EXPECT_TRUE(pending_server_socket);
  393. run_loop.Quit();
  394. }));
  395. run_loop.Run();
  396. }
  397. #if BUILDFLAG(IS_CHROMEOS)
  398. TEST_F(AdapterTest, TestMetricsOnShutdown_NoPendingConnects) {
  399. base::HistogramTester histogram_tester;
  400. adapter_.reset();
  401. EXPECT_EQ(0u,
  402. histogram_tester
  403. .GetAllSamples(
  404. "Bluetooth.Mojo.PendingConnectAtShutdown.DurationWaiting")
  405. .size());
  406. histogram_tester.ExpectUniqueSample(
  407. "Bluetooth.Mojo.PendingConnectAtShutdown."
  408. "NumberOfServiceDiscoveriesInProgress",
  409. /*sample=*/0, /*expected_bucket_count=*/1);
  410. }
  411. TEST_F(AdapterTest, TestMetricsOnShutdown_PendingConnects) {
  412. base::HistogramTester histogram_tester;
  413. EXPECT_CALL(*mock_bluetooth_adapter_,
  414. ConnectDevice(kUnknownDeviceAddress, _, _, _))
  415. .WillOnce(RunOnceCallback<2>(mock_unknown_bluetooth_device_.get()));
  416. EXPECT_CALL(*mock_unknown_bluetooth_device_,
  417. IsGattServicesDiscoveryComplete())
  418. .WillRepeatedly(Return(false));
  419. adapter_->AllowConnectionsForUuid(device::BluetoothUUID(kServiceId));
  420. adapter_->ConnectToServiceInsecurely(kUnknownDeviceAddress,
  421. device::BluetoothUUID(kServiceId),
  422. base::DoNothing());
  423. base::RunLoop().RunUntilIdle();
  424. adapter_.reset();
  425. histogram_tester.ExpectUniqueSample(
  426. "Bluetooth.Mojo.PendingConnectAtShutdown."
  427. "NumberOfServiceDiscoveriesInProgress",
  428. /*sample=*/1, /*expected_bucket_count=*/1);
  429. EXPECT_EQ(1u, histogram_tester
  430. .GetAllSamples("Bluetooth.Mojo.PendingConnectAtShutdown."
  431. "NumberOfServiceDiscoveriesInProgress")
  432. .size());
  433. }
  434. #endif
  435. } // namespace bluetooth