| Index: device/bluetooth/device_unittest.cc | 
| diff --git a/device/bluetooth/device_unittest.cc b/device/bluetooth/device_unittest.cc | 
| index b1e9ae1a36b847c9e60d1f3fb12cbbceeacebf1d..6493e3d86d31b2456f7a0ce3d49e71cd5b40cdbd 100644 | 
| --- a/device/bluetooth/device_unittest.cc | 
| +++ b/device/bluetooth/device_unittest.cc | 
| @@ -14,7 +14,9 @@ | 
| #include "base/run_loop.h" | 
| #include "device/bluetooth/test/mock_bluetooth_adapter.h" | 
| #include "device/bluetooth/test/mock_bluetooth_device.h" | 
| +#include "device/bluetooth/test/mock_bluetooth_gatt_characteristic.h" | 
| #include "device/bluetooth/test/mock_bluetooth_gatt_connection.h" | 
| +#include "device/bluetooth/test/mock_bluetooth_gatt_service.h" | 
| #include "testing/gtest/include/gtest/gtest.h" | 
|  | 
| using ::testing::Return; | 
| @@ -26,9 +28,14 @@ using NiceMockBluetoothAdapter = | 
| using NiceMockBluetoothDevice = testing::NiceMock<device::MockBluetoothDevice>; | 
| using NiceMockBluetoothGattService = | 
| testing::NiceMock<device::MockBluetoothGattService>; | 
| +using NiceMockBluetoothGattCharacteristic = | 
| +    testing::NiceMock<device::MockBluetoothGattCharacteristic>; | 
| using NiceMockBluetoothGattConnection = | 
| testing::NiceMock<device::MockBluetoothGattConnection>; | 
|  | 
| +using Properties = device::BluetoothGattCharacteristic::Properties; | 
| +using Property = device::BluetoothGattCharacteristic::Property; | 
| + | 
| namespace { | 
| const char kTestLeDeviceAddress0[] = "11:22:33:44:55:66"; | 
| const char kTestLeDeviceName0[] = "Test LE Device 0"; | 
| @@ -39,6 +46,19 @@ const char kTestServiceUuid0[] = "1234"; | 
| const char kTestServiceId1[] = "service_id1"; | 
| const char kTestServiceUuid1[] = "5678"; | 
|  | 
| +const char kTestCharacteristicId0[] = "characteristic_id0"; | 
| +const char kTestCharacteristicUuid0[] = "1234"; | 
| + | 
| +const char kTestCharacteristicId1[] = "characteristic_id1"; | 
| +const char kTestCharacteristicUuid1[] = "5678"; | 
| + | 
| +const char kTestCharacteristicId2[] = "characteristic_id2"; | 
| +const char kTestCharacteristicUuid2[] = "9012"; | 
| + | 
| +const Properties kReadWriteProperties = | 
| +    Property::PROPERTY_READ | Property::PROPERTY_WRITE; | 
| +const Properties kAllProperties = Property::NUM_PROPERTY - 1; | 
| + | 
| class BluetoothInterfaceDeviceTest : public testing::Test { | 
| public: | 
| enum class Call { EXPECTED, NOT_EXPECTED }; | 
| @@ -58,10 +78,44 @@ class BluetoothInterfaceDeviceTest : public testing::Test { | 
| auto service1 = base::MakeUnique<NiceMockBluetoothGattService>( | 
| &device_, kTestServiceId0, device::BluetoothUUID(kTestServiceUuid0), | 
| true /* is_primary */, false /* is_local */); | 
| + | 
| +    auto characteristic1 = | 
| +        base::MakeUnique<NiceMockBluetoothGattCharacteristic>( | 
| +            service1.get(), kTestCharacteristicId0, | 
| +            device::BluetoothUUID(kTestCharacteristicUuid0), | 
| +            false /* is_local */, kReadWriteProperties, 0 /* permissions */); | 
| + | 
| +    auto characteristic2 = | 
| +        base::MakeUnique<NiceMockBluetoothGattCharacteristic>( | 
| +            service1.get(), kTestCharacteristicId1, | 
| +            device::BluetoothUUID(kTestCharacteristicUuid1), | 
| +            false /* is_local */, kReadWriteProperties, 0 /* permissions */); | 
| + | 
| +    service1->AddMockCharacteristic(std::move(characteristic1)); | 
| +    service1->AddMockCharacteristic(std::move(characteristic2)); | 
| + | 
| auto service2 = base::MakeUnique<NiceMockBluetoothGattService>( | 
| &device_, kTestServiceId1, device::BluetoothUUID(kTestServiceUuid1), | 
| true /* is_primary */, false /* is_local */); | 
|  | 
| +    auto characteristic3 = | 
| +        base::MakeUnique<NiceMockBluetoothGattCharacteristic>( | 
| +            service2.get(), kTestCharacteristicId2, | 
| +            device::BluetoothUUID(kTestCharacteristicUuid2), | 
| +            false /* is_local */, kAllProperties, 0 /* permissions */); | 
| + | 
| +    service2->AddMockCharacteristic(std::move(characteristic3)); | 
| + | 
| +    EXPECT_CALL(*service1, GetCharacteristics()) | 
| +        .WillRepeatedly( | 
| +            Invoke(service1.get(), | 
| +                   &device::MockBluetoothGattService::GetMockCharacteristics)); | 
| + | 
| +    EXPECT_CALL(*service2, GetCharacteristics()) | 
| +        .WillRepeatedly( | 
| +            Invoke(service2.get(), | 
| +                   &device::MockBluetoothGattService::GetMockCharacteristics)); | 
| + | 
| device_.AddMockService(std::move(service1)); | 
| device_.AddMockService(std::move(service2)); | 
|  | 
| @@ -69,6 +123,10 @@ class BluetoothInterfaceDeviceTest : public testing::Test { | 
| .WillRepeatedly( | 
| Invoke(&device_, &device::MockBluetoothDevice::GetMockServices)); | 
|  | 
| +    EXPECT_CALL(device_, GetGattService(testing::_)) | 
| +        .WillRepeatedly( | 
| +            Invoke(&device_, &device::MockBluetoothDevice::GetMockService)); | 
| + | 
| auto connection = base::MakeUnique<NiceMockBluetoothGattConnection>( | 
| adapter_, device_.GetAddress()); | 
|  | 
| @@ -98,30 +156,53 @@ class BluetoothInterfaceDeviceTest : public testing::Test { | 
| observer.DeviceChanged(adapter_.get(), &device_); | 
| } | 
|  | 
| -  void GetServicesCheckForPrecedingCalls( | 
| +  void CheckGetServicesCountImpl(Call expected, | 
| +                                 size_t expected_service_count, | 
| +                                 int num_of_preceding_calls, | 
| +                                 std::vector<mojom::ServiceInfoPtr> services) { | 
| +    EXPECT_EQ(num_of_preceding_calls, actual_callback_count_); | 
| +    ++actual_callback_count_; | 
| + | 
| +    if (expected == Call::EXPECTED) | 
| +      ++actual_success_callback_calls_; | 
| + | 
| +    EXPECT_EQ(expected_service_count, services.size()); | 
| +  } | 
| + | 
| +  Device::GetServicesCallback CheckGetServicesCount(Call expected) { | 
| +    if (expected == Call::EXPECTED) | 
| +      ++expected_success_callback_calls_; | 
| + | 
| +    return base::Bind(&BluetoothInterfaceDeviceTest::CheckGetServicesCountImpl, | 
| +                      weak_factory_.GetWeakPtr(), expected, | 
| +                      2 /* expected_service_count */, | 
| +                      expected_callback_count_++); | 
| +  } | 
| + | 
| +  void CheckGetCharacteristicsCountImpl( | 
| Call expected, | 
| -      size_t expected_service_count, | 
| +      size_t expected_count, | 
| int num_of_preceding_calls, | 
| -      std::vector<mojom::ServiceInfoPtr> services) { | 
| -    EXPECT_EQ(num_of_preceding_calls, callback_count_); | 
| -    ++callback_count_; | 
| +      std::vector<mojom::CharacteristicInfoPtr> characteristics) { | 
| +    EXPECT_EQ(num_of_preceding_calls, actual_callback_count_); | 
| +    ++actual_callback_count_; | 
|  | 
| if (expected == Call::EXPECTED) | 
| ++actual_success_callback_calls_; | 
|  | 
| -    EXPECT_EQ(expected_service_count, services.size()); | 
| +    EXPECT_EQ(expected_count, characteristics.size()); | 
| } | 
|  | 
| -  Device::GetServicesCallback GetGetServicesCheckForPrecedingCalls( | 
| +  Device::GetCharacteristicsCallback CheckGetCharacteristicsCount( | 
| Call expected, | 
| -      int num_of_preceding_calls) { | 
| +      int expected_count) { | 
| if (expected == Call::EXPECTED) | 
| ++expected_success_callback_calls_; | 
|  | 
| return base::Bind( | 
| -        &BluetoothInterfaceDeviceTest::GetServicesCheckForPrecedingCalls, | 
| -        weak_factory_.GetWeakPtr(), expected, 2 /* expected_service_count */, | 
| -        num_of_preceding_calls); | 
| +        &BluetoothInterfaceDeviceTest::CheckGetCharacteristicsCountImpl, | 
| +        weak_factory_.GetWeakPtr(), expected, expected_count, | 
| +        expected_callback_count_++); | 
| } | 
|  | 
| scoped_refptr<NiceMockBluetoothAdapter> adapter_; | 
| @@ -134,7 +215,8 @@ class BluetoothInterfaceDeviceTest : public testing::Test { | 
| bool expect_device_service_deleted_ = false; | 
| int expected_success_callback_calls_ = 0; | 
| int actual_success_callback_calls_ = 0; | 
| -  int callback_count_ = 0; | 
| +  int actual_callback_count_ = 0; | 
| +  int expected_callback_count_ = 0; | 
|  | 
| base::WeakPtrFactory<BluetoothInterfaceDeviceTest> weak_factory_; | 
| }; | 
| @@ -144,8 +226,7 @@ TEST_F(BluetoothInterfaceDeviceTest, GetServices) { | 
| EXPECT_CALL(device_, IsGattServicesDiscoveryComplete()) | 
| .WillRepeatedly(Return(true)); | 
|  | 
| -  proxy_->GetServices(GetGetServicesCheckForPrecedingCalls( | 
| -      Call::EXPECTED, 0 /* num_of_preceding_calls */)); | 
| +  proxy_->GetServices(CheckGetServicesCount(Call::EXPECTED)); | 
|  | 
| base::RunLoop().RunUntilIdle(); | 
| } | 
| @@ -157,10 +238,8 @@ TEST_F(BluetoothInterfaceDeviceTest, GetServicesNotDiscovered) { | 
| .WillRepeatedly(Return(true)); | 
|  | 
| // Client: Sends multiple requests for services. | 
| -  proxy_->GetServices(GetGetServicesCheckForPrecedingCalls( | 
| -      Call::EXPECTED, 0 /* num_of_preceding_calls */)); | 
| -  proxy_->GetServices(GetGetServicesCheckForPrecedingCalls( | 
| -      Call::EXPECTED, 1 /* num_of_preceding_calls */)); | 
| +  proxy_->GetServices(CheckGetServicesCount(Call::EXPECTED)); | 
| +  proxy_->GetServices(CheckGetServicesCount(Call::EXPECTED)); | 
|  | 
| base::RunLoop().RunUntilIdle(); | 
|  | 
| @@ -172,10 +251,8 @@ TEST_F(BluetoothInterfaceDeviceTest, GetServicesNotDiscovered) { | 
| base::RunLoop().RunUntilIdle(); | 
|  | 
| // Client: Sends more requests which run immediately. | 
| -  proxy_->GetServices(GetGetServicesCheckForPrecedingCalls( | 
| -      Call::EXPECTED, 2 /* num_of_preceding_calls */)); | 
| -  proxy_->GetServices(GetGetServicesCheckForPrecedingCalls( | 
| -      Call::EXPECTED, 3 /* num_of_preceding_calls */)); | 
| +  proxy_->GetServices(CheckGetServicesCount(Call::EXPECTED)); | 
| +  proxy_->GetServices(CheckGetServicesCount(Call::EXPECTED)); | 
|  | 
| base::RunLoop().RunUntilIdle(); | 
|  | 
| @@ -191,11 +268,9 @@ TEST_F(BluetoothInterfaceDeviceTest, | 
| EXPECT_CALL(device_, IsGattServicesDiscoveryComplete()) | 
| .WillRepeatedly(Return(false)); | 
| // Client: Sends multiple requests for services. | 
| -  proxy_->GetServices(GetGetServicesCheckForPrecedingCalls( | 
| -      Call::NOT_EXPECTED, 0 /* num_of_preceding_calls */)); | 
| -  proxy_->GetServices(GetGetServicesCheckForPrecedingCalls( | 
| -      Call::NOT_EXPECTED, 1 /* num_of_preceding_calls */)); | 
| -  EXPECT_EQ(0, callback_count_); | 
| +  proxy_->GetServices(CheckGetServicesCount(Call::NOT_EXPECTED)); | 
| +  proxy_->GetServices(CheckGetServicesCount(Call::NOT_EXPECTED)); | 
| +  EXPECT_EQ(0, actual_callback_count_); | 
|  | 
| // Simulate connection loss. | 
| device_.SetConnected(false); | 
| @@ -212,11 +287,95 @@ TEST_F(BluetoothInterfaceDeviceTest, | 
| .WillRepeatedly(Return(false)); | 
|  | 
| // Client: Sends multiple requests for services. | 
| -  proxy_->GetServices(GetGetServicesCheckForPrecedingCalls( | 
| -      Call::NOT_EXPECTED, 0 /* num_of_preceding_calls */)); | 
| -  proxy_->GetServices(GetGetServicesCheckForPrecedingCalls( | 
| -      Call::NOT_EXPECTED, 1 /* num_of_preceding_calls */)); | 
| -  EXPECT_EQ(0, callback_count_); | 
| +  proxy_->GetServices(CheckGetServicesCount(Call::NOT_EXPECTED)); | 
| +  proxy_->GetServices(CheckGetServicesCount(Call::NOT_EXPECTED)); | 
| +  EXPECT_EQ(0, actual_callback_count_); | 
| + | 
| +  // Simulate connection loss. | 
| +  proxy_->Disconnect(); | 
| +  expect_device_service_deleted_ = true; | 
| + | 
| +  // Wait for message pipe to process error. | 
| +  base::RunLoop().RunUntilIdle(); | 
| +} | 
| + | 
| +TEST_F(BluetoothInterfaceDeviceTest, GetCharacteristics) { | 
| +  EXPECT_CALL(device_, IsGattServicesDiscoveryComplete()) | 
| +      .WillRepeatedly(Return(true)); | 
| + | 
| +  proxy_->GetCharacteristics(kTestServiceId0, | 
| +                             CheckGetCharacteristicsCount(Call::EXPECTED, 2)); | 
| + | 
| +  base::RunLoop().RunUntilIdle(); | 
| +} | 
| + | 
| +TEST_F(BluetoothInterfaceDeviceTest, GetCharacteristicsNotDiscovered) { | 
| +  EXPECT_CALL(device_, IsGattServicesDiscoveryComplete()) | 
| +      .WillOnce(Return(false)) | 
| +      .WillOnce(Return(false)) | 
| +      .WillRepeatedly(Return(true)); | 
| + | 
| +  // Client: Sends multiple requests for characteristics. | 
| +  proxy_->GetCharacteristics(kTestServiceId0, | 
| +                             CheckGetCharacteristicsCount(Call::EXPECTED, 2)); | 
| +  proxy_->GetCharacteristics(kTestServiceId1, | 
| +                             CheckGetCharacteristicsCount(Call::EXPECTED, 1)); | 
| + | 
| +  base::RunLoop().RunUntilIdle(); | 
| + | 
| +  SimulateGattServicesDiscovered(); | 
| + | 
| +  // No more GetCharacteristics calls will complete. | 
| +  SimulateGattServicesDiscovered(); | 
| + | 
| +  base::RunLoop().RunUntilIdle(); | 
| + | 
| +  // Client: Sends more requests which run immediately. | 
| +  proxy_->GetCharacteristics(kTestServiceId0, | 
| +                             CheckGetCharacteristicsCount(Call::EXPECTED, 2)); | 
| +  proxy_->GetCharacteristics(kTestServiceId1, | 
| +                             CheckGetCharacteristicsCount(Call::EXPECTED, 1)); | 
| + | 
| +  base::RunLoop().RunUntilIdle(); | 
| + | 
| +  // No more GetCharacteristics calls will complete. | 
| +  SimulateGattServicesDiscovered(); | 
| + | 
| +  // Wait for message pipe to process error. | 
| +  base::RunLoop().RunUntilIdle(); | 
| +} | 
| + | 
| +TEST_F(BluetoothInterfaceDeviceTest, | 
| +       GetCharacteristicsLostConnectionWithPendingRequests) { | 
| +  EXPECT_CALL(device_, IsGattServicesDiscoveryComplete()) | 
| +      .WillRepeatedly(Return(false)); | 
| +  // Client: Sends multiple requests for characteristics. | 
| +  proxy_->GetCharacteristics( | 
| +      kTestServiceId0, CheckGetCharacteristicsCount(Call::NOT_EXPECTED, 2)); | 
| +  proxy_->GetCharacteristics( | 
| +      kTestServiceId1, CheckGetCharacteristicsCount(Call::NOT_EXPECTED, 1)); | 
| +  EXPECT_EQ(0, actual_callback_count_); | 
| + | 
| +  // Simulate connection loss. | 
| +  device_.SetConnected(false); | 
| +  SimulateDeviceChanged(); | 
| +  expect_device_service_deleted_ = true; | 
| + | 
| +  // Wait for message pipe to process error. | 
| +  base::RunLoop().RunUntilIdle(); | 
| +} | 
| + | 
| +TEST_F(BluetoothInterfaceDeviceTest, | 
| +       GetCharacteristicsForcedDisconnectionWithPendingRequests) { | 
| +  EXPECT_CALL(device_, IsGattServicesDiscoveryComplete()) | 
| +      .WillRepeatedly(Return(false)); | 
| + | 
| +  // Client: Sends multiple requests for characteristics. | 
| +  proxy_->GetCharacteristics( | 
| +      kTestServiceId0, CheckGetCharacteristicsCount(Call::NOT_EXPECTED, 2)); | 
| +  proxy_->GetCharacteristics( | 
| +      kTestServiceId1, CheckGetCharacteristicsCount(Call::NOT_EXPECTED, 1)); | 
| +  EXPECT_EQ(0, actual_callback_count_); | 
|  | 
| // Simulate connection loss. | 
| proxy_->Disconnect(); | 
|  |