chromium/services/device/generic_sensor/platform_sensor_provider_chromeos_unittest.cc

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

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/351564777): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif

#include "services/device/generic_sensor/platform_sensor_provider_chromeos.h"

#include <memory>
#include <optional>
#include <utility>

#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/test/bind.h"
#include "base/test/task_environment.h"
#include "build/chromeos_buildflags.h"
#include "chromeos/components/sensors/fake_sensor_device.h"
#include "chromeos/components/sensors/fake_sensor_hal_server.h"
#include "services/device/generic_sensor/sensor_impl.h"
#include "services/device/public/cpp/generic_sensor/sensor_traits.h"
#include "testing/gtest/include/gtest/gtest.h"

#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "chromeos/components/sensors/ash/sensor_hal_dispatcher.h"
#endif  // BUILDFLAG(IS_CHROMEOS_ASH)

namespace device {

namespace {

constexpr int kFakeDeviceId = 1;

constexpr double kScaleValue = 10.0;
constexpr char kWrongScale[] = "10..0";

constexpr char kWrongLocation[] = "basee";

constexpr int64_t kFakeSampleData = 1;
constexpr int64_t kFakeTimestampData = 163176689212344ll;

// The number of axes for which there are accelerometer and gyroscope readings.
constexpr uint32_t kNumberOfAxes = 3u;

constexpr char kAccelerometerChannels[][10] = {"accel_x", "accel_y", "accel_z"};
constexpr char kGyroscopeChannels[][10] = {"anglvel_x", "anglvel_y",
                                           "anglvel_z"};

class FakeClient : public PlatformSensor::Client {
 public:
  explicit FakeClient(PlatformSensor* platform_sensor)
      : platform_sensor_(platform_sensor) {}
  ~FakeClient() override {}

  void OnSensorReadingChanged(mojom::SensorType type) override {}
  void OnSensorError() override { platform_sensor_->RemoveClient(this); }
  bool IsSuspended() override { return false; }

 private:
  raw_ptr<PlatformSensor> platform_sensor_;
};

}  // namespace

class PlatformSensorProviderChromeOSTest : public ::testing::Test {
 protected:
  void SetUp() override {
#if BUILDFLAG(IS_CHROMEOS_ASH)
    chromeos::sensors::SensorHalDispatcher::Initialize();
#endif  // BUILDFLAG(IS_CHROMEOS_ASH)

    sensor_hal_server_ =
        std::make_unique<chromeos::sensors::FakeSensorHalServer>();
    provider_ = std::make_unique<PlatformSensorProviderChromeOS>();
  }

  void TearDown() override {
#if BUILDFLAG(IS_CHROMEOS_ASH)
    chromeos::sensors::SensorHalDispatcher::Shutdown();
#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
  }

  void AddDevice(int32_t iio_device_id,
                 chromeos::sensors::mojom::DeviceType type,
                 const std::optional<std::string>& scale,
                 const std::optional<std::string>& location,
                 std::vector<chromeos::sensors::FakeSensorDevice::ChannelData>
                     channels_data = {}) {
    AddDevice(iio_device_id,
              std::set<chromeos::sensors::mojom::DeviceType>{type},
              std::move(scale), std::move(location), std::move(channels_data));
  }

  void AddDevice(int32_t iio_device_id,
                 std::set<chromeos::sensors::mojom::DeviceType> types,
                 const std::optional<std::string>& scale,
                 const std::optional<std::string>& location,
                 std::vector<chromeos::sensors::FakeSensorDevice::ChannelData>
                     channels_data = {}) {
    auto sensor_device = std::make_unique<chromeos::sensors::FakeSensorDevice>(
        std::move(channels_data));

    if (scale.has_value()) {
      sensor_device->SetAttribute(chromeos::sensors::mojom::kScale,
                                  scale.value());
    }
    if (location.has_value()) {
      sensor_device->SetAttribute(chromeos::sensors::mojom::kLocation,
                                  location.value());
    }

    sensor_devices_.push_back(sensor_device.get());

    sensor_hal_server_->GetSensorService()->SetDevice(
        iio_device_id, std::move(types), std::move(sensor_device));
  }

  std::vector<chromeos::sensors::FakeSensorDevice::ChannelData>
  GetChannelsWithAxes(const char channels[][10]) {
    std::vector<chromeos::sensors::FakeSensorDevice::ChannelData> channels_data(
        kNumberOfAxes + 1);
    for (uint32_t i = 0; i < kNumberOfAxes; ++i) {
      channels_data[i].id = channels[i];
      channels_data[i].sample_data = kFakeSampleData;
    }
    channels_data.back().id = chromeos::sensors::mojom::kTimestampChannel;
    channels_data.back().sample_data = kFakeTimestampData;

    return channels_data;
  }

  // Sensor creation is asynchronous, therefore inner loop is used to wait for
  // PlatformSensorProvider::CreateSensorCallback completion.
  scoped_refptr<PlatformSensor> CreateSensor(mojom::SensorType type) {
    scoped_refptr<PlatformSensor> sensor;
    base::RunLoop run_loop;
    provider_->CreateSensor(type,
                            base::BindLambdaForTesting(
                                [&](scoped_refptr<PlatformSensor> new_sensor) {
                                  if (new_sensor)
                                    EXPECT_EQ(type, new_sensor->GetType());

                                  sensor = std::move(new_sensor);
                                  run_loop.Quit();
                                }));
    run_loop.Run();
    return sensor;
  }

  void RegisterSensorHalServer() {
#if BUILDFLAG(IS_CHROMEOS_ASH)
    // MojoConnectionServiceProvider::BootstrapMojoConnectionForIioService is
    // responsible for calling this outside unit tests.
    // This will eventually call PlatformSensorProviderChromeOS::SetUpChannel().
    chromeos::sensors::SensorHalDispatcher::GetInstance()->RegisterServer(
        sensor_hal_server_->PassRemote());
#else
    // As SensorHalDispatcher is only defined in ash, manually setting up Mojo
    // connection between |fake_sensor_hal_server_| and |provider_|.
    // This code is duplicating what SensorHalDispatcher::EstablishMojoChannel()
    // does.
    mojo::PendingRemote<chromeos::sensors::mojom::SensorService> pending_remote;
    sensor_hal_server_->CreateChannel(
        pending_remote.InitWithNewPipeAndPassReceiver());
    provider_->SetUpChannel(std::move(pending_remote));
#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
  }

  std::unique_ptr<chromeos::sensors::FakeSensorHalServer> sensor_hal_server_;
  std::vector<raw_ptr<chromeos::sensors::FakeSensorDevice, VectorExperimental>>
      sensor_devices_;

  std::unique_ptr<PlatformSensorProviderChromeOS> provider_;

  base::test::SingleThreadTaskEnvironment task_environment_;
};

TEST_F(PlatformSensorProviderChromeOSTest, CheckUnsupportedTypes) {
  int fake_id = 1;
  // Containing at least one supported device type.
  AddDevice(fake_id++, chromeos::sensors::mojom::DeviceType::ACCEL,
            base::NumberToString(kScaleValue),
            chromeos::sensors::mojom::kLocationBase);
  AddDevice(fake_id++,
            std::set<chromeos::sensors::mojom::DeviceType>{
                chromeos::sensors::mojom::DeviceType::ANGLVEL,
                chromeos::sensors::mojom::DeviceType::COUNT},
            base::NumberToString(kScaleValue),
            chromeos::sensors::mojom::kLocationBase);

  // All device types are unsupported.
  AddDevice(fake_id++, chromeos::sensors::mojom::DeviceType::COUNT,
            base::NumberToString(kScaleValue),
            chromeos::sensors::mojom::kLocationBase);
  AddDevice(fake_id++, chromeos::sensors::mojom::DeviceType::ANGL,
            base::NumberToString(kScaleValue),
            chromeos::sensors::mojom::kLocationBase);
  AddDevice(fake_id++,
            std::set<chromeos::sensors::mojom::DeviceType>{
                chromeos::sensors::mojom::DeviceType::ANGL,
                chromeos::sensors::mojom::DeviceType::BARO},
            base::NumberToString(kScaleValue),
            chromeos::sensors::mojom::kLocationBase);

  RegisterSensorHalServer();

  EXPECT_TRUE(CreateSensor(mojom::SensorType::ACCELEROMETER));

  EXPECT_FALSE(provider_->sensors_[1].ignored);
  EXPECT_FALSE(provider_->sensors_[2].ignored);
  EXPECT_TRUE(provider_->sensors_[3].ignored);
  EXPECT_TRUE(provider_->sensors_[4].ignored);
  EXPECT_TRUE(provider_->sensors_[5].ignored);
}

TEST_F(PlatformSensorProviderChromeOSTest, MissingScale) {
  AddDevice(kFakeDeviceId, chromeos::sensors::mojom::DeviceType::ACCEL,
            /*scale=*/std::nullopt, chromeos::sensors::mojom::kLocationBase);

  RegisterSensorHalServer();

  EXPECT_FALSE(CreateSensor(mojom::SensorType::ACCELEROMETER));
}

TEST_F(PlatformSensorProviderChromeOSTest, MissingLocation) {
  AddDevice(kFakeDeviceId, chromeos::sensors::mojom::DeviceType::ACCEL,
            base::NumberToString(kScaleValue),
            /*location=*/std::nullopt);

  RegisterSensorHalServer();

  EXPECT_FALSE(CreateSensor(mojom::SensorType::ACCELEROMETER));
}

TEST_F(PlatformSensorProviderChromeOSTest, WrongScale) {
  AddDevice(kFakeDeviceId, chromeos::sensors::mojom::DeviceType::ACCEL,
            kWrongScale, chromeos::sensors::mojom::kLocationBase);

  RegisterSensorHalServer();

  EXPECT_FALSE(CreateSensor(mojom::SensorType::ACCELEROMETER));
}

TEST_F(PlatformSensorProviderChromeOSTest, WrongLocation) {
  AddDevice(kFakeDeviceId, chromeos::sensors::mojom::DeviceType::ACCEL,
            base::NumberToString(kScaleValue), kWrongLocation);

  RegisterSensorHalServer();

  EXPECT_FALSE(CreateSensor(mojom::SensorType::ACCELEROMETER));
}

TEST_F(PlatformSensorProviderChromeOSTest, CheckMainLocationBase) {
  int fake_id = 1;
  AddDevice(fake_id++, chromeos::sensors::mojom::DeviceType::ACCEL,
            base::NumberToString(kScaleValue),
            chromeos::sensors::mojom::kLocationBase);

  // Will not be used.
  AddDevice(fake_id++, chromeos::sensors::mojom::DeviceType::ACCEL,
            base::NumberToString(kScaleValue),
            chromeos::sensors::mojom::kLocationLid);

  AddDevice(fake_id++, chromeos::sensors::mojom::DeviceType::ANGLVEL,
            base::NumberToString(kScaleValue),
            chromeos::sensors::mojom::kLocationBase);

  AddDevice(fake_id++, chromeos::sensors::mojom::DeviceType::MAGN,
            base::NumberToString(kScaleValue),
            chromeos::sensors::mojom::kLocationBase);

  RegisterSensorHalServer();

  EXPECT_TRUE(CreateSensor(mojom::SensorType::GYROSCOPE));

  // Wait until the disconnect of the gyroscope arrives at FakeSensorDevice.
  base::RunLoop().RunUntilIdle();

  // Remote stored in |provider_|.
  EXPECT_TRUE(sensor_devices_[0]->HasReceivers());
  EXPECT_TRUE(sensor_devices_[3]->HasReceivers());
  // Removed in |provider_| as it'll never be used.
  EXPECT_FALSE(sensor_devices_[1]->HasReceivers());
}

TEST_F(PlatformSensorProviderChromeOSTest, CheckMainLocationLid) {
  int fake_id = 1;
  AddDevice(fake_id++, chromeos::sensors::mojom::DeviceType::ACCEL,
            base::NumberToString(kScaleValue),
            chromeos::sensors::mojom::kLocationBase);
  AddDevice(fake_id++, chromeos::sensors::mojom::DeviceType::ACCEL,
            base::NumberToString(kScaleValue),
            chromeos::sensors::mojom::kLocationLid);

  AddDevice(fake_id++, chromeos::sensors::mojom::DeviceType::ANGLVEL,
            base::NumberToString(kScaleValue),
            chromeos::sensors::mojom::kLocationBase);
  AddDevice(fake_id++, chromeos::sensors::mojom::DeviceType::ANGLVEL,
            base::NumberToString(kScaleValue),
            chromeos::sensors::mojom::kLocationLid);

  AddDevice(fake_id++, chromeos::sensors::mojom::DeviceType::MAGN,
            base::NumberToString(kScaleValue),
            chromeos::sensors::mojom::kLocationCamera);

  RegisterSensorHalServer();

  // Wait until the disconnect of the first gyroscope arrives at
  // FakeSensorDevice.
  base::RunLoop().RunUntilIdle();

  // Removed in |provider_| as they'll never be used.
  EXPECT_FALSE(sensor_devices_[0]->HasReceivers());
  EXPECT_FALSE(sensor_devices_[2]->HasReceivers());
  EXPECT_FALSE(sensor_devices_[4]->HasReceivers());
  // Remote stored in |provider_|.
  EXPECT_TRUE(sensor_devices_[1]->HasReceivers());
  EXPECT_TRUE(sensor_devices_[3]->HasReceivers());
}

TEST_F(PlatformSensorProviderChromeOSTest,
       CheckMainLocationLidWithMultitypeSensor) {
  int fake_id = 1;
  AddDevice(fake_id++,
            std::set<chromeos::sensors::mojom::DeviceType>{
                chromeos::sensors::mojom::DeviceType::ACCEL,
                chromeos::sensors::mojom::DeviceType::ANGLVEL},
            base::NumberToString(kScaleValue),
            chromeos::sensors::mojom::kLocationLid);

  AddDevice(fake_id++, chromeos::sensors::mojom::DeviceType::ANGLVEL,
            base::NumberToString(kScaleValue),
            chromeos::sensors::mojom::kLocationBase);

  RegisterSensorHalServer();

  // Wait until the disconnect of the gyroscope arrives at FakeSensorDevice.
  base::RunLoop().RunUntilIdle();

  // Remote stored in |provider_|.
  EXPECT_TRUE(sensor_devices_[0]->HasReceivers());
  // Removed in |provider_| as it'll never be used.
  EXPECT_FALSE(sensor_devices_[1]->HasReceivers());

  EXPECT_TRUE(CreateSensor(mojom::SensorType::ACCELEROMETER));
  EXPECT_TRUE(CreateSensor(mojom::SensorType::GYROSCOPE));
}

TEST_F(PlatformSensorProviderChromeOSTest, CheckAmbientLightSensorLocationLid) {
  int fake_id = 1;
  AddDevice(fake_id++, chromeos::sensors::mojom::DeviceType::LIGHT,
            base::NumberToString(kScaleValue),
            chromeos::sensors::mojom::kLocationBase);
  AddDevice(fake_id++, chromeos::sensors::mojom::DeviceType::LIGHT,
            base::NumberToString(kScaleValue),
            chromeos::sensors::mojom::kLocationLid);

  RegisterSensorHalServer();

  // Wait until the disconnect of the first ambient light sensor arrives at
  // FakeSensorDevice.
  base::RunLoop().RunUntilIdle();

  // Removed in |provider_| as it'll never be used.
  EXPECT_FALSE(sensor_devices_[0]->HasReceivers());
  // Remote stored in |provider_|.
  EXPECT_TRUE(sensor_devices_[1]->HasReceivers());
}

TEST_F(PlatformSensorProviderChromeOSTest,
       CheckAmbientLightSensorLocationBase) {
  AddDevice(kFakeDeviceId, chromeos::sensors::mojom::DeviceType::LIGHT,
            base::NumberToString(kScaleValue),
            chromeos::sensors::mojom::kLocationBase);

  RegisterSensorHalServer();

  EXPECT_TRUE(CreateSensor(mojom::SensorType::AMBIENT_LIGHT));
}

TEST_F(PlatformSensorProviderChromeOSTest, SensorDeviceDisconnect) {
  int fake_id = 1;
  AddDevice(fake_id++, chromeos::sensors::mojom::DeviceType::ACCEL,
            base::NumberToString(kScaleValue),
            chromeos::sensors::mojom::kLocationLid,
            GetChannelsWithAxes(kAccelerometerChannels));
  AddDevice(fake_id++, chromeos::sensors::mojom::DeviceType::ANGLVEL,
            base::NumberToString(kScaleValue),
            chromeos::sensors::mojom::kLocationLid);

  RegisterSensorHalServer();

  auto accel_lid = CreateSensor(mojom::SensorType::ACCELEROMETER);
  EXPECT_TRUE(accel_lid);

  // Wait until all tasks are done and no failures occur in |provider_| or
  // |accel_lid|.
  base::RunLoop().RunUntilIdle();

  EXPECT_TRUE(sensor_devices_.front()->HasReceivers());

  // Simulate a disconnection of an existing SensorDevice in |provider_|, which
  // triggers PlatformSensorProviderChromeOS::OnSensorDeviceDisconnect(). The
  // default reason is IIOSERVICE_CRASHED.
  sensor_devices_.back()->ClearReceivers();

  // Wait until the disconnection is done.
  base::RunLoop().RunUntilIdle();
  // PlatformSensorProviderChromeOS::OnSensorDeviceDisconnect() resets the
  // SensorService Mojo channel.
  EXPECT_FALSE(sensor_hal_server_->GetSensorService()->HasReceivers());

  // The existing PlatformSensors will also be reset.
  EXPECT_FALSE(sensor_devices_.front()->HasReceivers());
}

TEST_F(PlatformSensorProviderChromeOSTest, SensorDeviceDisconnectWithReason) {
  int fake_id = 1;
  AddDevice(fake_id++, chromeos::sensors::mojom::DeviceType::ACCEL,
            base::NumberToString(kScaleValue),
            chromeos::sensors::mojom::kLocationLid,
            GetChannelsWithAxes(kAccelerometerChannels));
  AddDevice(fake_id++, chromeos::sensors::mojom::DeviceType::ANGLVEL,
            base::NumberToString(kScaleValue),
            chromeos::sensors::mojom::kLocationLid,
            GetChannelsWithAxes(kGyroscopeChannels));
  AddDevice(fake_id++, chromeos::sensors::mojom::DeviceType::ACCEL,
            base::NumberToString(kScaleValue),
            chromeos::sensors::mojom::kLocationBase,
            GetChannelsWithAxes(kAccelerometerChannels));
  AddDevice(fake_id++, chromeos::sensors::mojom::DeviceType::ANGLVEL,
            base::NumberToString(kScaleValue),
            chromeos::sensors::mojom::kLocationBase);

  RegisterSensorHalServer();

  auto accel_lid = CreateSensor(mojom::SensorType::ACCELEROMETER);
  EXPECT_TRUE(accel_lid);
  base::RunLoop().RunUntilIdle();

  // The accelerometer created is on the lid.
  EXPECT_TRUE(sensor_devices_[0]->HasReceivers());
  EXPECT_FALSE(sensor_devices_[2]->HasReceivers());

  auto gyro_lid = CreateSensor(mojom::SensorType::GYROSCOPE);
  EXPECT_TRUE(gyro_lid);
  FakeClient client(gyro_lid.get());
  gyro_lid->AddClient(&client);
  PlatformSensorConfiguration config;
  config.set_frequency(100);
  gyro_lid->StartListening(&client, config);

  // Wait until all tasks are done and |gyro_lid| is reading samples.
  base::RunLoop().RunUntilIdle();

  // Simulate a disconnection of the gyro_lid in |provider_|, which triggers
  // PlatformSensorProviderChromeOS::OnSensorDeviceDisconnect(). As the mojo
  // pipe is reset with reason: DEVICE_REMOVED, |provider_| will only remove the
  // SensorDevice instead of the entire SensorService and the corresponding mojo
  // pipes.
  EXPECT_TRUE(sensor_devices_[1]->HasReceivers());
  EXPECT_FALSE(sensor_devices_[3]->HasReceivers());
  sensor_devices_[1]->ClearReceiversWithReason(
      chromeos::sensors::mojom::SensorDeviceDisconnectReason::DEVICE_REMOVED,
      "Device was removed");

  // Wait until the disconnection is done.
  base::RunLoop().RunUntilIdle();

  // PlatformSensorProviderChromeOS::OnSensorDeviceDisconnect() doesn't reset
  // the SensorService Mojo channel with the reason: DEVICE_REMOVED.
  EXPECT_TRUE(sensor_hal_server_->GetSensorService()->HasReceivers());

  auto accel_base = CreateSensor(mojom::SensorType::ACCELEROMETER);
  base::RunLoop().RunUntilIdle();

  // The new accelerometer created is on the base, as there are more motion
  // sensors on the base now.
  EXPECT_FALSE(sensor_devices_[0]->HasReceivers());
  EXPECT_TRUE(sensor_devices_[2]->HasReceivers());

  EXPECT_FALSE(base::Contains(provider_->sensors_, 2 /* gyro_lid's id */));
  EXPECT_EQ(provider_->sensor_id_by_type_[mojom::SensorType::ACCELEROMETER],
            3 /* accel_base's id */);
  EXPECT_EQ(provider_->sensor_id_by_type_[mojom::SensorType::GYROSCOPE],
            4 /* accel_base's id */);
}

TEST_F(PlatformSensorProviderChromeOSTest, ReconnectClient) {
  AddDevice(kFakeDeviceId, chromeos::sensors::mojom::DeviceType::ACCEL,
            base::NumberToString(kScaleValue),
            chromeos::sensors::mojom::kLocationLid);

  RegisterSensorHalServer();

  EXPECT_TRUE(CreateSensor(mojom::SensorType::ACCELEROMETER));

  // Simulate a disconnection between |provider_| and SensorHalDispatcher.
  provider_->OnSensorHalClientFailure(base::TimeDelta());
#if BUILDFLAG(IS_CHROMEOS_LACROS)
  // Need to manually re-connect the Mojo as SensorHalDispatcher doesn't exist
  // in Lacros-Chrome.
  RegisterSensorHalServer();
#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)

  EXPECT_TRUE(CreateSensor(mojom::SensorType::ACCELEROMETER));
}

TEST_F(PlatformSensorProviderChromeOSTest, ReconnectServer) {
  AddDevice(kFakeDeviceId, chromeos::sensors::mojom::DeviceType::ACCEL,
            base::NumberToString(kScaleValue),
            chromeos::sensors::mojom::kLocationLid);

  RegisterSensorHalServer();

  EXPECT_TRUE(CreateSensor(mojom::SensorType::ACCELEROMETER));

  sensor_hal_server_->OnServerDisconnect();
  sensor_hal_server_->GetSensorService()->ClearReceivers();

  base::RunLoop().RunUntilIdle();
  // Finished simulating a disconnection with IIO Service.
  EXPECT_FALSE(provider_->GetSensor(mojom::SensorType::ACCELEROMETER));

  RegisterSensorHalServer();

  EXPECT_TRUE(CreateSensor(mojom::SensorType::ACCELEROMETER));
}

TEST_F(PlatformSensorProviderChromeOSTest,
       CheckLinearAccelerationSensorNotCreatedIfNoAccelerometer) {
  RegisterSensorHalServer();

  EXPECT_FALSE(CreateSensor(mojom::SensorType::LINEAR_ACCELERATION));
}

TEST_F(PlatformSensorProviderChromeOSTest, CheckLinearAcceleration) {
  AddDevice(kFakeDeviceId, chromeos::sensors::mojom::DeviceType::ACCEL,
            base::NumberToString(kScaleValue),
            chromeos::sensors::mojom::kLocationBase);

  RegisterSensorHalServer();

  EXPECT_TRUE(CreateSensor(mojom::SensorType::LINEAR_ACCELERATION));
}

TEST_F(
    PlatformSensorProviderChromeOSTest,
    CheckAbsoluteOrientationSensorNotCreatedIfNoAccelerometerAndNoMagnetometer) {
  RegisterSensorHalServer();

  EXPECT_FALSE(
      CreateSensor(mojom::SensorType::ABSOLUTE_ORIENTATION_EULER_ANGLES));
  EXPECT_FALSE(
      CreateSensor(mojom::SensorType::ABSOLUTE_ORIENTATION_QUATERNION));
}

TEST_F(PlatformSensorProviderChromeOSTest,
       CheckAbsoluteOrientationSensorNotCreatedIfNoAccelerometer) {
  AddDevice(kFakeDeviceId, chromeos::sensors::mojom::DeviceType::MAGN,
            base::NumberToString(kScaleValue),
            chromeos::sensors::mojom::kLocationBase);

  RegisterSensorHalServer();

  EXPECT_FALSE(
      CreateSensor(mojom::SensorType::ABSOLUTE_ORIENTATION_EULER_ANGLES));
  EXPECT_FALSE(
      CreateSensor(mojom::SensorType::ABSOLUTE_ORIENTATION_QUATERNION));
}

TEST_F(PlatformSensorProviderChromeOSTest,
       CheckAbsoluteOrientationSensorNotCreatedIfNoMagnetometer) {
  AddDevice(kFakeDeviceId, chromeos::sensors::mojom::DeviceType::ACCEL,
            base::NumberToString(kScaleValue),
            chromeos::sensors::mojom::kLocationBase);

  RegisterSensorHalServer();

  EXPECT_FALSE(
      CreateSensor(mojom::SensorType::ABSOLUTE_ORIENTATION_EULER_ANGLES));
  EXPECT_FALSE(
      CreateSensor(mojom::SensorType::ABSOLUTE_ORIENTATION_QUATERNION));
}

TEST_F(PlatformSensorProviderChromeOSTest, CheckAbsoluteOrientationSensors) {
  int fake_id = 1;

  AddDevice(fake_id++, chromeos::sensors::mojom::DeviceType::ACCEL,
            base::NumberToString(kScaleValue),
            chromeos::sensors::mojom::kLocationBase);
  AddDevice(fake_id++, chromeos::sensors::mojom::DeviceType::MAGN,
            base::NumberToString(kScaleValue),
            chromeos::sensors::mojom::kLocationBase);

  RegisterSensorHalServer();

  EXPECT_TRUE(
      CreateSensor(mojom::SensorType::ABSOLUTE_ORIENTATION_EULER_ANGLES));
  EXPECT_TRUE(CreateSensor(mojom::SensorType::ABSOLUTE_ORIENTATION_QUATERNION));
}

TEST_F(
    PlatformSensorProviderChromeOSTest,
    CheckRelativeOrientationSensorNotCreatedIfNoAccelerometerAndNoGyroscope) {
  RegisterSensorHalServer();

  EXPECT_FALSE(
      CreateSensor(mojom::SensorType::RELATIVE_ORIENTATION_EULER_ANGLES));
  EXPECT_FALSE(
      CreateSensor(mojom::SensorType::RELATIVE_ORIENTATION_QUATERNION));
}

TEST_F(PlatformSensorProviderChromeOSTest,
       CheckRelativeOrientationSensorNotCreatedIfNoAccelerometer) {
  AddDevice(kFakeDeviceId, chromeos::sensors::mojom::DeviceType::ANGLVEL,
            base::NumberToString(kScaleValue),
            chromeos::sensors::mojom::kLocationBase);

  RegisterSensorHalServer();

  EXPECT_FALSE(
      CreateSensor(mojom::SensorType::RELATIVE_ORIENTATION_EULER_ANGLES));
  EXPECT_FALSE(
      CreateSensor(mojom::SensorType::RELATIVE_ORIENTATION_QUATERNION));
}

TEST_F(PlatformSensorProviderChromeOSTest,
       CheckRelativeOrientationSensorUsingAccelerometer) {
  AddDevice(kFakeDeviceId, chromeos::sensors::mojom::DeviceType::ACCEL,
            base::NumberToString(kScaleValue),
            chromeos::sensors::mojom::kLocationBase);

  RegisterSensorHalServer();

  EXPECT_TRUE(
      CreateSensor(mojom::SensorType::RELATIVE_ORIENTATION_EULER_ANGLES));
  EXPECT_TRUE(CreateSensor(mojom::SensorType::RELATIVE_ORIENTATION_QUATERNION));
}

TEST_F(PlatformSensorProviderChromeOSTest,
       CheckRelativeOrientationSensorUsingAccelerometerAndGyroscope) {
  int fake_id = 1;
  AddDevice(fake_id++, chromeos::sensors::mojom::DeviceType::ANGLVEL,
            base::NumberToString(kScaleValue),
            chromeos::sensors::mojom::kLocationBase);
  AddDevice(fake_id++, chromeos::sensors::mojom::DeviceType::ACCEL,
            base::NumberToString(kScaleValue),
            chromeos::sensors::mojom::kLocationBase);

  RegisterSensorHalServer();

  EXPECT_TRUE(
      CreateSensor(mojom::SensorType::RELATIVE_ORIENTATION_EULER_ANGLES));
  EXPECT_TRUE(CreateSensor(mojom::SensorType::RELATIVE_ORIENTATION_QUATERNION));
}

TEST_F(PlatformSensorProviderChromeOSTest, LatePresentMotionSensors) {
  int fake_id = 1;
  AddDevice(fake_id++, chromeos::sensors::mojom::DeviceType::ACCEL,
            base::NumberToString(kScaleValue),
            chromeos::sensors::mojom::kLocationBase,
            GetChannelsWithAxes(kAccelerometerChannels));
  AddDevice(fake_id++, chromeos::sensors::mojom::DeviceType::ACCEL,
            base::NumberToString(kScaleValue),
            chromeos::sensors::mojom::kLocationLid,
            GetChannelsWithAxes(kAccelerometerChannels));

  RegisterSensorHalServer();

  // Wait until the disconnect of the accelerometer_base arrives at
  // FakeSensorDevice.
  base::RunLoop().RunUntilIdle();

  // Removed in |provider_| as it'll never be used.
  EXPECT_FALSE(sensor_devices_[0]->HasReceivers());
  // Remote stored in |provider_|.
  EXPECT_TRUE(sensor_devices_[1]->HasReceivers());

  AddDevice(fake_id++, chromeos::sensors::mojom::DeviceType::ANGLVEL,
            base::NumberToString(kScaleValue),
            chromeos::sensors::mojom::kLocationBase,
            GetChannelsWithAxes(kGyroscopeChannels));

  // Wait until |provider_| is notifies that the device is present.
  base::RunLoop().RunUntilIdle();

  auto accel_base = CreateSensor(mojom::SensorType::ACCELEROMETER);
  EXPECT_TRUE(accel_base);

  // Wait until all tasks are done and no failures occur in |provider_| or
  // |accel_base|.
  base::RunLoop().RunUntilIdle();

  // Motion sensors on base are used. Accelerometer on lid is reset.
  EXPECT_TRUE(sensor_devices_[0]->HasReceivers());
  EXPECT_FALSE(sensor_devices_[1]->HasReceivers());
  EXPECT_TRUE(sensor_devices_[2]->HasReceivers());

  AddDevice(fake_id++, chromeos::sensors::mojom::DeviceType::ANGLVEL,
            base::NumberToString(kScaleValue),
            chromeos::sensors::mojom::kLocationLid,
            GetChannelsWithAxes(kGyroscopeChannels));

  // Wait until |provider_| is notifies that the device is present.
  base::RunLoop().RunUntilIdle();

  auto accel_lid = CreateSensor(mojom::SensorType::ACCELEROMETER);
  EXPECT_TRUE(accel_lid);

  // Wait until all tasks are done and no failures occur in |provider_| or
  // |accel_lid|.
  base::RunLoop().RunUntilIdle();

  // Motion sensors on lid are used. Gyroscope on base is reset.
  EXPECT_FALSE(sensor_devices_[0]->HasReceivers());
  EXPECT_TRUE(sensor_devices_[1]->HasReceivers());
  EXPECT_FALSE(sensor_devices_[2]->HasReceivers());
  EXPECT_TRUE(sensor_devices_[3]->HasReceivers());

  accel_base.reset();

  // Wait until all tasks are done and no failures occur.
  base::RunLoop().RunUntilIdle();
}

TEST_F(PlatformSensorProviderChromeOSTest, LatePresentLightSensors) {
  int fake_id = 1;
  std::vector<chromeos::sensors::FakeSensorDevice::ChannelData> channels_data(
      2);
  channels_data.front().id = chromeos::sensors::mojom::kLightChannel;
  channels_data.front().sample_data = kFakeSampleData;
  channels_data.back().id = chromeos::sensors::mojom::kTimestampChannel;
  channels_data.back().sample_data = kFakeTimestampData;

  AddDevice(fake_id++, chromeos::sensors::mojom::DeviceType::LIGHT,
            base::NumberToString(kScaleValue),
            chromeos::sensors::mojom::kLocationBase, channels_data);

  RegisterSensorHalServer();

  auto light_base = CreateSensor(mojom::SensorType::AMBIENT_LIGHT);
  EXPECT_TRUE(light_base);
  EXPECT_TRUE(sensor_devices_[0]->HasReceivers());

  AddDevice(fake_id++, chromeos::sensors::mojom::DeviceType::LIGHT,
            base::NumberToString(kScaleValue),
            chromeos::sensors::mojom::kLocationLid, channels_data);

  // Wait until |provider_| finishes processing the new device.
  base::RunLoop().RunUntilIdle();

  // Test PlatformSensorProvider::NotifySensorCreated on different sensors
  // of the same type.
  auto light_lid = CreateSensor(mojom::SensorType::AMBIENT_LIGHT);
  EXPECT_TRUE(light_lid);

  // Wait until all tasks are done and no failures occur in |provider_| or
  // |light_lid|.
  base::RunLoop().RunUntilIdle();

  // The light sensor on lid is used in |light_lid|.
  EXPECT_TRUE(sensor_devices_[1]->HasReceivers());

  // The light sensor on base is not used after being overridden in
  // |light_base|.
  EXPECT_FALSE(sensor_devices_[0]->HasReceivers());

  // Test the usage of |light_base->reading_buffer_|.
  SensorReading result;
  EXPECT_FALSE(light_base->GetLatestReading(&result));

  // Test PlatformSensorProvider::RemoveSensor on different sensors of the
  // same type.
  light_base.reset();

  // Wait until all tasks are done and no failures occur.
  base::RunLoop().RunUntilIdle();
}

}  // namespace device