chromium/device/bluetooth/bluetooth_remote_gatt_service_unittest.cc

// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/logging.h"
#include "base/run_loop.h"
#include "build/build_config.h"
#include "device/bluetooth/bluetooth_gatt_service.h"
#include "device/bluetooth/bluetooth_remote_gatt_characteristic.h"
#include "device/bluetooth/test/test_bluetooth_adapter_observer.h"
#include "testing/gtest/include/gtest/gtest.h"

#if BUILDFLAG(IS_ANDROID)
#include "device/bluetooth/test/bluetooth_test_android.h"
#elif BUILDFLAG(IS_APPLE)
#include "device/bluetooth/test/bluetooth_test_mac.h"
#elif BUILDFLAG(IS_WIN)
#include "device/bluetooth/test/bluetooth_test_win.h"
#elif defined(USE_CAST_BLUETOOTH_ADAPTER)
#include "device/bluetooth/test/bluetooth_test_cast.h"
#elif BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX)
#include "device/bluetooth/test/bluetooth_test_bluez.h"
#elif BUILDFLAG(IS_FUCHSIA)
#include "device/bluetooth/test/bluetooth_test_fuchsia.h"
#endif

namespace device {

class BluetoothRemoteGattServiceTest :
#if BUILDFLAG(IS_WIN)
    public BluetoothTestWinrt {};

#if BUILDFLAG(IS_WIN)
using BluetoothRemoteGattServiceTestWinrt = BluetoothRemoteGattServiceTest;
#endif

// Android is excluded because it fires a single discovery event per device.
#if BUILDFLAG(IS_APPLE)
#define MAYBE_IsDiscoveryComplete
#else
#define MAYBE_IsDiscoveryComplete
#endif
#if BUILDFLAG(IS_WIN)
TEST_P(BluetoothRemoteGattServiceTestWinrt, IsDiscoveryComplete) {
#else
TEST_F(BluetoothRemoteGattServiceTest, MAYBE_IsDiscoveryComplete) {}

#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_APPLE)
#define MAYBE_GetIdentifier
#else
#define MAYBE_GetIdentifier
#endif
#if BUILDFLAG(IS_WIN)
TEST_P(BluetoothRemoteGattServiceTestWinrt, GetIdentifier) {
#else
TEST_F(BluetoothRemoteGattServiceTest, MAYBE_GetIdentifier) {}

#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_APPLE)
#define MAYBE_GetUUID
#else
#define MAYBE_GetUUID
#endif
#if BUILDFLAG(IS_WIN)
TEST_P(BluetoothRemoteGattServiceTestWinrt, GetUUID) {
#else
TEST_F(BluetoothRemoteGattServiceTest, MAYBE_GetUUID) {}

#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_APPLE)
#define MAYBE_GetCharacteristics_FindNone
#else
#define MAYBE_GetCharacteristics_FindNone
#endif
#if BUILDFLAG(IS_WIN)
TEST_P(BluetoothRemoteGattServiceTestWinrt, GetCharacteristics_FindNone) {
#else
TEST_F(BluetoothRemoteGattServiceTest, MAYBE_GetCharacteristics_FindNone) {}

#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_APPLE)
#define MAYBE_GetCharacteristics_and_GetCharacteristic
#else
#define MAYBE_GetCharacteristics_and_GetCharacteristic
#endif
#if BUILDFLAG(IS_WIN)
TEST_P(BluetoothRemoteGattServiceTestWinrt,
       GetCharacteristics_and_GetCharacteristic) {
#else
TEST_F(BluetoothRemoteGattServiceTest,
       MAYBE_GetCharacteristics_and_GetCharacteristic) {}

#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_APPLE)
#define MAYBE_GetCharacteristicsByUUID
#else
#define MAYBE_GetCharacteristicsByUUID
#endif
#if BUILDFLAG(IS_WIN)
TEST_P(BluetoothRemoteGattServiceTestWinrt, GetCharacteristicsByUUID) {
#else
TEST_F(BluetoothRemoteGattServiceTest, MAYBE_GetCharacteristicsByUUID) {}

#if BUILDFLAG(IS_APPLE)
#define MAYBE_GattCharacteristics_ObserversCalls
#else
#define MAYBE_GattCharacteristics_ObserversCalls
#endif
// The GattServicesRemoved event is not implemented for WinRT.
TEST_F(BluetoothRemoteGattServiceTest,
       MAYBE_GattCharacteristics_ObserversCalls) {}

#if BUILDFLAG(IS_APPLE)
#define MAYBE_SimulateGattServiceRemove
#else
#define MAYBE_SimulateGattServiceRemove
#endif
#if BUILDFLAG(IS_WIN)
TEST_P(BluetoothRemoteGattServiceTestWinrt, SimulateGattServiceRemove) {
#else
TEST_F(BluetoothRemoteGattServiceTest, MAYBE_SimulateGattServiceRemove) {}

#if BUILDFLAG(IS_APPLE)
// Tests to receive a services changed notification from macOS, while
// discovering characteristics. The gatt device should scan again for services
// and characteristics, before scanning for descriptors. Only after the gatt
// service changed notification should be sent.
// Android: This test doesn't apply to Android because there is no services
// changed event that could arrive during a discovery procedure.
TEST_F(BluetoothRemoteGattServiceTest,
       SimulateDeviceModificationWhileDiscoveringCharacteristics) {
  InitWithFakeAdapter();
  StartLowEnergyDiscoverySession();
  BluetoothDevice* device = SimulateLowEnergyDevice(3);
  device->CreateGattConnection(
      GetGattConnectionCallback(Call::EXPECTED, Result::SUCCESS));

  TestBluetoothAdapterObserver observer(adapter_);

  // Starts first discovery process.
  SimulateGattConnection(device);
  AddServicesToDeviceMac(device, {kTestUUIDHeartRate});
  SimulateDidDiscoverServicesMac(device);
  EXPECT_EQ(1u, device->GetGattServices().size());
  BluetoothRemoteGattService* service = device->GetGattServices()[0];
  std::string characteristic_uuid1 = "11111111-0000-1000-8000-00805f9b34fb";
  AddCharacteristicToServiceMac(service, characteristic_uuid1,
                                /* properties */ 0);
  // Now waiting for characteristic discovery.

  // Starts second discovery process.
  SimulateGattServicesChanged(device);
  SimulateDidDiscoverServicesMac(device);
  // Now waiting for the second characteristic discovery.

  // First system call to -[id<CBPeripheralDelegate>
  // peripheral:didDiscoverCharacteristicsForService:error:]
  SimulateDidDiscoverCharacteristicsMac(service);
  EXPECT_EQ(0, observer.gatt_service_changed_count());
  EXPECT_EQ(1u, service->GetCharacteristics().size());

  // Finish discovery process.
  std::string characteristic_uuid2 = "22222222-0000-1000-8000-00805f9b34fb";
  AddCharacteristicToServiceMac(service, characteristic_uuid2,
                                /* properties */ 0);
  // Second system call to -[id<CBPeripheralDelegate>
  // peripheral:didDiscoverCharacteristicsForService:error:]
  SimulateDidDiscoverCharacteristicsMac(service);
  EXPECT_EQ(2u, service->GetCharacteristics().size());
  EXPECT_EQ(0, observer.gatt_service_changed_count());
  BluetoothRemoteGattCharacteristic* characteristic1 =
      service->GetCharacteristics()[0];
  BluetoothRemoteGattCharacteristic* characteristic2 =
      service->GetCharacteristics()[1];
  if (characteristic1->GetUUID().canonical_value() == characteristic_uuid2) {
    BluetoothRemoteGattCharacteristic* tmp = characteristic1;
    characteristic1 = characteristic2;
    characteristic2 = tmp;
  }
  EXPECT_EQ(characteristic_uuid1, characteristic1->GetUUID().canonical_value());
  EXPECT_EQ(characteristic_uuid2, characteristic2->GetUUID().canonical_value());
  SimulateDidDiscoverDescriptorsMac(characteristic1);
  SimulateDidDiscoverDescriptorsMac(characteristic2);
  EXPECT_EQ(1, observer.gatt_service_changed_count());
}
#endif  // BUILDFLAG(IS_APPLE)

#if BUILDFLAG(IS_APPLE)
// Simulates to receive an extra discovery characteristic notifications from
// macOS. Those notifications should be ignored.
// Android: This test doesn't apply to Android because there is no services
// changed event that could arrive during a discovery procedure.
TEST_F(BluetoothRemoteGattServiceTest, ExtraDidDiscoverServicesCall) {
  InitWithFakeAdapter();
  StartLowEnergyDiscoverySession();
  BluetoothDevice* device = SimulateLowEnergyDevice(3);
  device->CreateGattConnection(
      GetGattConnectionCallback(Call::EXPECTED, Result::SUCCESS));

  TestBluetoothAdapterObserver observer(adapter_);

  // Starts first discovery process.
  SimulateGattConnection(device);
  AddServicesToDeviceMac(device, {kTestUUIDHeartRate});
  SimulateDidDiscoverServicesMac(device);
  EXPECT_EQ(1u, device->GetGattServices().size());
  BluetoothRemoteGattService* service = device->GetGattServices()[0];
  std::string characteristic_uuid = "11111111-0000-1000-8000-00805f9b34fb";
  AddCharacteristicToServiceMac(service, characteristic_uuid,
                                /* properties */ 0);
  SimulateDidDiscoverCharacteristicsMac(service);
  EXPECT_EQ(1u, service->GetCharacteristics().size());
  BluetoothRemoteGattCharacteristic* characteristic =
      service->GetCharacteristics()[0];
  std::string descriptor_uuid = "22222222-0000-1000-8000-00805f9b34fb";
  AddDescriptorToCharacteristicMac(characteristic, descriptor_uuid);
  SimulateDidDiscoverDescriptorsMac(characteristic);
  EXPECT_EQ(1, observer.gatt_service_changed_count());
  EXPECT_EQ(1u, service->GetCharacteristics().size());
  EXPECT_EQ(1u, characteristic->GetDescriptors().size());

  observer.Reset();
  SimulateDidDiscoverServicesMac(device);  // Extra system call.
  SimulateGattServicesChanged(device);
  SimulateDidDiscoverServicesMac(device);
  SimulateDidDiscoverServicesMac(device);  // Extra system call.
  SimulateDidDiscoverCharacteristicsMac(service);
  SimulateDidDiscoverServicesMac(device);  // Extra system call.
  SimulateDidDiscoverDescriptorsMac(characteristic);
  SimulateDidDiscoverServicesMac(device);  // Extra system call.
  EXPECT_EQ(2, observer.device_changed_count());
}
#endif  // BUILDFLAG(IS_APPLE)

#if BUILDFLAG(IS_APPLE)
// Simulates to receive an extra discovery characteristic notifications from
// macOS. Those notifications should be ignored.
// Android: This test doesn't apply to Android because there is no services
// changed event that could arrive during a discovery procedure.
TEST_F(BluetoothRemoteGattServiceTest, ExtraDidDiscoverCharacteristicsCall) {
  InitWithFakeAdapter();
  StartLowEnergyDiscoverySession();
  BluetoothDevice* device = SimulateLowEnergyDevice(3);
  device->CreateGattConnection(
      GetGattConnectionCallback(Call::EXPECTED, Result::SUCCESS));

  TestBluetoothAdapterObserver observer(adapter_);

  // Starts first discovery process.
  SimulateGattConnection(device);
  AddServicesToDeviceMac(device, {kTestUUIDHeartRate});
  SimulateDidDiscoverServicesMac(device);
  EXPECT_EQ(1u, device->GetGattServices().size());
  BluetoothRemoteGattService* service = device->GetGattServices()[0];
  std::string characteristic_uuid = "11111111-0000-1000-8000-00805f9b34fb";
  AddCharacteristicToServiceMac(service, characteristic_uuid,
                                /* properties */ 0);
  SimulateDidDiscoverCharacteristicsMac(service);
  EXPECT_EQ(1u, service->GetCharacteristics().size());
  BluetoothRemoteGattCharacteristic* characteristic =
      service->GetCharacteristics()[0];
  std::string descriptor_uuid = "22222222-0000-1000-8000-00805f9b34fb";
  AddDescriptorToCharacteristicMac(characteristic, descriptor_uuid);
  SimulateDidDiscoverDescriptorsMac(characteristic);
  EXPECT_EQ(1, observer.gatt_service_changed_count());
  EXPECT_EQ(1u, service->GetCharacteristics().size());
  EXPECT_EQ(1u, characteristic->GetDescriptors().size());

  observer.Reset();
  SimulateDidDiscoverCharacteristicsMac(service);  // Extra system call.
  SimulateGattServicesChanged(device);
  SimulateDidDiscoverCharacteristicsMac(service);  // Extra system call.
  SimulateDidDiscoverServicesMac(device);
  SimulateDidDiscoverCharacteristicsMac(service);
  SimulateDidDiscoverCharacteristicsMac(service);  // Extra system call.
  SimulateDidDiscoverDescriptorsMac(characteristic);
  SimulateDidDiscoverCharacteristicsMac(service);  // Extra system call.
  EXPECT_EQ(2, observer.device_changed_count());
}
#endif  // BUILDFLAG(IS_APPLE)

#if BUILDFLAG(IS_WIN)
INSTANTIATE_TEST_SUITE_P(All,
                         BluetoothRemoteGattServiceTestWinrt,
                         ::testing::ValuesIn(kBluetoothTestWinrtParam));
#endif  // BUILDFLAG(IS_WIN)

}  // namespace device